1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
|
#pragma once
///@file
#include "lix/libexpr/attr-set.hh"
#include "lix/libexpr/eval-error.hh"
#include "lix/libexpr/gc-alloc.hh"
#include "lix/libutil/box_ptr.hh"
#include "lix/libutil/generator.hh"
#include "lix/libutil/async.hh"
#include "lix/libutil/source-path.hh"
#include "lix/libutil/types.hh"
#include "lix/libexpr/value.hh"
#include "lix/libexpr/nixexpr.hh"
#include "lix/libexpr/symbol-table.hh"
#include "lix/libutil/config.hh"
#include "lix/libutil/experimental-features.hh"
#include "lix/libexpr/search-path.hh"
#include "lix/libexpr/repl-exit-status.hh"
#include "lix/libutil/backed-string-view.hh"
#include <concepts>
#include <map>
#include <optional>
#include <unordered_map>
#include <functional>
namespace nix {
class Store;
class EvalState;
class StorePath;
struct SingleDerivedPath;
enum RepairFlag : bool;
struct MemoryInputAccessor;
namespace eval_cache {
class EvalCache;
}
std::ostream & operator<<(std::ostream & output, const PrimOp & primOp);
/**
* Info about a constant
*/
struct Constant
{
/**
* Optional type of the constant (known since it is a fixed value).
*
* @todo we should use an enum for this.
*/
ValueType type = nThunk;
/**
* Optional free-form documentation about the constant.
*/
const char * doc = nullptr;
/**
* Whether the constant is impure, and not available in pure mode.
*/
bool impureOnly = false;
};
using ValMap = GcMap<std::string, Value>;
struct alignas(Value::Acb::TAG_ALIGN) Env
{
Env * up;
Value values[0];
};
void printEnvBindings(const EvalState &es, const Expr & expr, const Env & env);
void printEnvBindings(const SymbolTable & st, const StaticEnv & se, const Env & env, int lvl = 0);
std::unique_ptr<ValMap> mapStaticEnvBindings(const SymbolTable & st, const StaticEnv & se, const Env & env);
void copyContext(const Value & v, NixStringContext & context);
std::string printValue(EvalState & state, Value & v);
std::ostream & operator << (std::ostream & os, const ValueType t);
Symbol getName(const AttrName & name, EvalState & state, Env & env);
std::string showAttrPath(EvalState & state, Env & env, const AttrPath & attrPath);
/**
* Initialise the evaluator (including Boehm GC, if applicable).
*/
void initLibExpr();
struct RegexCache;
struct DebugTrace {
std::shared_ptr<Pos> pos;
const Expr & expr;
const Env & env;
HintFmt hint;
bool isError;
std::shared_ptr<const DebugTrace> parent;
};
struct DebugState
{
private:
std::weak_ptr<const DebugTrace> latestTrace;
const PosTable & positions;
const SymbolTable & symbols;
public:
std::function<ReplExitStatus(ValMap const & extraEnv, NeverAsync)> errorCallback;
bool stop = false;
bool inDebugger = false;
std::map<const Expr *, const std::shared_ptr<const StaticEnv>> exprEnvs;
int trylevel = 0;
explicit DebugState(
const PosTable & positions,
const SymbolTable & symbols,
std::function<ReplExitStatus(ValMap const & extraEnv, NeverAsync)> errorCallback
)
: positions(positions)
, symbols(symbols)
, errorCallback(errorCallback)
{
assert(errorCallback);
}
void onEvalError(const EvalError * error, const Env & env, const Expr & expr, NeverAsync = {});
const std::shared_ptr<const StaticEnv> staticEnvFor(const Expr & expr) const
{
if (auto i = exprEnvs.find(&expr); i != exprEnvs.end()) {
return i->second;
}
return nullptr;
}
class TraceFrame
{
friend struct DebugState;
template<std::derived_from<EvalError> T>
friend class EvalErrorBuilder;
// holds both the data for this frame *and* a deleter that pulls this frame
// off the trace stack. EvalErrorBuilder uses this for withFrame fake trace
// frames, and to avoid needing to see this class definition in its header.
const std::shared_ptr<const DebugTrace> entry = nullptr;
explicit TraceFrame(std::shared_ptr<const DebugTrace> entry): entry(std::move(entry)) {}
public:
TraceFrame(std::nullptr_t) {}
};
TraceFrame addTrace(DebugTrace t);
/// Enumerates the debug frame stack, from the current frame to the root frame.
/// All values are guaranteed to not be null, but must be pointers because C++.
Generator<const DebugTrace *> traces()
{
for (auto current = latestTrace.lock(); current; current = current->parent) {
co_yield current.get();
}
}
};
class EvalMemory
{
static constexpr size_t CACHES = 8;
static constexpr size_t CACHE_INCREMENT = sizeof(void *);
/**
* Allocation caches for small values.
*/
void * gcCache[CACHES] = {};
public:
struct Statistics
{
unsigned long nrEnvs = 0;
unsigned long nrValuesInEnvs = 0;
unsigned long nrAttrsets = 0;
unsigned long nrAttrsInAttrsets = 0;
unsigned long nrListElems = 0;
};
EvalMemory();
~EvalMemory();
EvalMemory(const EvalMemory &) = delete;
EvalMemory(EvalMemory &&) = delete;
EvalMemory & operator=(const EvalMemory &) = delete;
EvalMemory & operator=(EvalMemory &&) = delete;
inline void * allocBytes(size_t size);
template<typename T>
inline T * allocType(size_t n = 1);
inline Env & allocEnv(size_t size);
Bindings * allocBindings(size_t capacity);
Value::List * newList(size_t length);
BindingsBuilder buildBindings(SymbolTable & symbols, size_t capacity)
{
return BindingsBuilder(symbols, allocBindings(capacity), capacity);
}
const Statistics getStats() const { return stats; }
private:
Statistics stats;
};
class EvalBuiltins
{
EvalMemory & mem;
SymbolTable & symbols;
public:
explicit EvalBuiltins(
EvalMemory & mem,
SymbolTable & symbols,
const SearchPath & searchPath,
const Path & storeDir,
size_t size = 128
);
/**
* The base environment, containing the builtin functions and
* values.
*/
Env & env;
/**
* The same, but used during parsing to resolve variables.
*/
std::shared_ptr<StaticEnv> staticEnv; // !!! should be private
/**
* Name and documentation about every constant.
*
* Constants from primops are hard to crawl, and their docs will go
* here too.
*/
std::vector<std::pair<std::string, Constant>> constantInfos;
private:
unsigned int baseEnvDispl = 0;
void createBaseEnv(const SearchPath & searchPath, const Path & storeDir);
void addConstant(const std::string & name, const Value & v, Constant info);
void addPrimOp(PrimOpDetails primOp);
Value prepareNixPath(const SearchPath & searchPath);
public:
Value & get(const std::string & name);
struct Doc
{
Pos pos;
std::optional<std::string> name;
size_t arity;
std::vector<std::string> args;
/**
* Unlike the other `doc` fields in this file, this one should never be
* `null`.
*/
const char * doc;
};
std::optional<Doc> getDoc(Value & v);
};
struct CachedEvalFile;
struct EvalRuntimeCaches
{
RootValue vCallFlake;
RootValue vImportedDrvToDerivation;
/**
* Cache used by prim_match() and other regex functions.
*/
std::shared_ptr<RegexCache> regexes;
/**
* A cache from path names to values for evalFile().
*/
std::map<SourcePath, std::shared_ptr<CachedEvalFile>> fileEval;
};
struct EvalErrorContext
{
const PosTable & positions;
DebugState * debug;
template<std::derived_from<EvalError> T, typename... Args>
[[gnu::noinline]]
EvalErrorBuilder<T> make(const Args & ... args) {
return EvalErrorBuilder<T>(positions, debug, args...);
}
};
class EvalPaths
{
ref<Store> store;
SearchPath searchPath_;
EvalErrorContext & errors;
public:
EvalPaths(
AsyncIoRoot & aio,
const ref<Store> & store,
SearchPath searchPath,
EvalErrorContext & errors
);
const SearchPath & searchPath() const { return searchPath_; }
private:
struct AllowedPath
{
struct ComponentLess : std::less<>
{
// we'll only use this for string-likes, it's fine. trust me sis.
using is_transparent = void;
};
std::map<std::string, AllowedPath, ComponentLess> children;
bool allowAllChildren = false;
};
/**
* The allowed filesystem paths in restricted or pure evaluation
* mode.
*/
std::optional<AllowedPath> allowedPaths;
/* Cache for calls to addToStore(); maps source paths to the store
paths. */
std::map<SourcePath, StorePath> srcToStore;
std::map<std::string, std::optional<std::string>> searchPathResolved;
/**
* Cache used by checkSourcePath().
*/
std::unordered_map<Path, CheckedSourcePath> resolvedPaths;
public:
/**
* Allow access to a path.
*/
void allowPath(const Path & path);
/**
* Allow access to a store path. Note that this gets remapped to
* the real store path if `store` is a chroot store.
*/
void allowPath(const StorePath & storePath);
/**
* Allow access to a store path and return it as a string.
*/
Value allowAndSetStorePathString(const StorePath & storePath);
/**
* Check whether access to a path is allowed and throw an error if
* not. Otherwise return the canonicalised path.
*/
CheckedSourcePath checkSourcePath(const SourcePath & path);
/**
* If `path` refers to a directory, then append "/default.nix".
*/
CheckedSourcePath resolveExprPath(SourcePath path);
void checkURI(const std::string & uri);
/**
* When using a diverted store and 'path' is in the Nix store, map
* 'path' to the diverted location (e.g. /nix/store/foo is mapped
* to /home/alice/my-nix/nix/store/foo). However, this is only
* done if the context is not empty, since otherwise we're
* probably trying to read from the actual /nix/store. This is
* intended to distinguish between import-from-derivation and
* sources stored in the actual /nix/store.
*/
Path toRealPath(const Path & path, const NixStringContext & context);
/**
* findFile wants to throw a debuggable error when the requested file
* is not found, but it can't invoke the debugger itself because it's
* async code. This wraps the result-or-error to allow it regardless.
* This happens for copyPathToStore as well, with another error type.
*/
template<typename T, typename E>
struct PathResult : private std::variant<T, EvalErrorBuilder<E>>
{
PathResult(T p) : std::variant<T, EvalErrorBuilder<E>>(std::move(p)) {}
PathResult(EvalErrorBuilder<E> e) : std::variant<T, EvalErrorBuilder<E>>(std::move(e)) {}
T unwrap(NeverAsync = {}) &&
{
return std::visit(
overloaded{
[](T & p) -> T { return std::move(p); },
[](EvalErrorBuilder<E> & e) -> T {
std::move(e).debugThrow(always_progresses);
}
},
static_cast<std::variant<T, EvalErrorBuilder<E>> &>(*this)
);
}
};
/**
* Look up a file in the search path.
*/
kj::Promise<Result<PathResult<SourcePath, ThrownError>>> findFile(const std::string_view path);
kj::Promise<Result<PathResult<SourcePath, ThrownError>>>
findFile(const SearchPath & searchPath, const std::string_view path, const PosIdx pos = noPos);
/**
* Try to resolve a search path value (not the optinal key part)
*
* If the specified search path element is a URI, download it.
*
* If it is not found, return `std::nullopt`
*/
kj::Promise<Result<std::optional<std::string>>>
resolveSearchPathPath(const SearchPath::Path & path);
kj::Promise<Result<PathResult<StorePath, EvalError>>> copyPathToStore(
NixStringContext & context, const SourcePath & path, RepairFlag repair = NoRepair
);
/**
* Create a string representing a store path.
*
* The string is the printed store path with a context containing a
* single `NixStringContextElem::Opaque` element of that store path.
*/
Value mkStorePathString(const StorePath & storePath);
};
struct EvalStatistics
{
unsigned long nrLookups = 0;
unsigned long nrAvoided = 0;
unsigned long nrOpUpdates = 0;
unsigned long nrOpUpdateValuesCopied = 0;
unsigned long nrListConcats = 0;
unsigned long nrPrimOpCalls = 0;
unsigned long nrFunctionCalls = 0;
unsigned long nrThunks = 0;
bool countCalls = false;
std::map<std::string, size_t> primOpCalls;
std::map<ExprLambda *, size_t> functionCalls;
std::map<PosIdx, size_t> attrSelects;
void addCall(ExprLambda & fun);
};
class Evaluator
{
friend class EvalBuiltins;
friend class EvalState;
EvalState * activeEval = nullptr;
public:
NixSymbolTable symbols;
PosTable positions;
EvalMemory mem;
EvalRuntimeCaches caches;
EvalPaths paths;
EvalBuiltins builtins;
EvalStatistics stats;
/**
* If set, force copying files to the Nix store even if they
* already exist there.
*/
RepairFlag repair;
/**
* Store used to materialise .drv files.
*/
const ref<Store> store;
/**
* Store used to build stuff.
*/
ref<Store> buildStore;
std::unique_ptr<DebugState> debug;
EvalErrorContext errors;
Evaluator(
AsyncIoRoot & aio,
const SearchPath & _searchPath,
ref<Store> store,
std::shared_ptr<Store> buildStore = nullptr,
std::function<ReplExitStatus(EvalState & es, ValMap const & extraEnv)> debugRepl = nullptr
);
Evaluator(const Evaluator &) = delete;
Evaluator(Evaluator &&) = delete;
Evaluator & operator=(const Evaluator &) = delete;
Evaluator & operator=(Evaluator &&) = delete;
/**
* Parse a Nix expression from the specified file.
*/
Expr & parseExprFromFile(const CheckedSourcePath & path);
Expr & parseExprFromFile(const CheckedSourcePath & path, std::shared_ptr<StaticEnv> & staticEnv);
/**
* Parse a Nix expression from the specified string.
*/
Expr & parseExprFromString(
std::string s,
const SourcePath & basePath,
std::shared_ptr<StaticEnv> & staticEnv,
const FeatureSettings & xpSettings = featureSettings
);
Expr & parseExprFromString(
std::string s,
const SourcePath & basePath,
const FeatureSettings & xpSettings = featureSettings
);
std::variant<std::unique_ptr<Expr>, ExprReplBindings>
parseReplInput(
std::string s,
const SourcePath & basePath,
std::shared_ptr<StaticEnv> & staticEnv,
const FeatureSettings & xpSettings = featureSettings
);
Expr & parseStdin();
/**
* Creates a thunk that will evaluate the given expression when forced.
*/
Value evalLazily(Expr & e);
/** If debugging is enabled, returns the next trace. Otherwise, std::nullopt. */
std::optional<DebugTrace const *> nextDebugTrace() const;
private:
Expr * parse(
char * text,
size_t length,
Pos::Origin origin,
const SourcePath & basePath,
std::shared_ptr<StaticEnv> & staticEnv,
const FeatureSettings & xpSettings = featureSettings);
std::variant<std::unique_ptr<Expr>, ExprReplBindings>
parse_repl(
char * text,
size_t length,
Pos::Origin origin,
const SourcePath & basePath,
std::shared_ptr<StaticEnv> & staticEnv,
const FeatureSettings & xpSettings = featureSettings
);
public:
BindingsBuilder buildBindings(size_t capacity)
{
return mem.buildBindings(symbols, capacity);
}
/**
* Print statistics, if enabled.
*
* Performs a full memory GC before printing the statistics, so that the
* GC statistics are more accurate.
*/
void maybePrintStats();
/**
* Print statistics, unconditionally, cheaply, without performing a GC first.
*/
void printStatistics();
/**
* Perform a full memory garbage collection - not incremental.
*
* @return true if Nix was built with GC and a GC was performed, false if not.
* The return value is currently not thread safe - just the return value.
*/
bool fullGC();
/**
* Create an `EvalState` in prepation to evaluate some amount of Nix code.
*
* While preparation of evaluation can be done with Evaluator itself only,
* actually evaluating things requires an EvalState. This function creates
* an EvalState and returns it. At most one EvalState per Evaluator may be
* live at any given point, and references to this EvalState must not live
* anywhere except in the returned box, local variables, or arguments. Any
* reference held in an object type is illegal, be it in a lambda capture,
* a pointer member of an object, a hidden member such as arguments passed
* to functions by `std::thread` or `std::async`—all references held where
* they could be copied are moved from are disallowed. EvalState is thus a
* witness type that a given thread may evaluate nix code and must *never*
* be run inside `kj::Promise` context. This is due to a kj limitation, in
* which it is not possible to block on a promise while already running in
* a promise without doing this blocking on a different event loop/thread.
*/
box_ptr<EvalState> begin(AsyncIoRoot & aio);
};
class EvalState
{
friend class Evaluator;
explicit EvalState(AsyncIoRoot & aio, Evaluator & ctx);
public:
Evaluator & ctx;
AsyncIoRoot & aio;
EvalState(const EvalState &) = delete;
EvalState(EvalState &&) = delete;
EvalState & operator=(const EvalState &) = delete;
EvalState & operator=(EvalState &&) = delete;
~EvalState();
/**
* Evaluate an expression read from the given file to normal form.
*/
Value evalFile(const SourcePath & path);
void resetFileCache();
/**
* Evaluate an expression to normal form
*/
Value eval(Expr & e);
/**
* If `v` is a thunk, enter it and overwrite `v` with the result
* of the evaluation of the thunk. If `v` is a delayed function
* application, call the function and overwrite `v` with the
* result. Otherwise, this is a no-op.
*/
inline void forceValue(Value & v, const PosIdx pos);
void tryFixupBlackHolePos(Value & v, PosIdx pos);
/**
* Force a value, then recursively force list elements and
* attributes.
*/
void forceValueDeep(Value & v);
/**
* Force `v`, and then verify that it has the expected type.
*/
NixInt forceInt(Value & v, const PosIdx pos, std::string_view errorCtx);
NixFloat forceFloat(Value & v, const PosIdx pos, std::string_view errorCtx);
bool forceBool(Value & v, const PosIdx pos, std::string_view errorCtx);
inline Bindings * forceAttrs(Value & v, const PosIdx pos, std::string_view errorCtx);
inline void forceList(Value & v, const PosIdx pos, std::string_view errorCtx);
/**
* @param v either lambda or primop
*/
void forceFunction(Value & v, const PosIdx pos, std::string_view errorCtx);
std::string_view forceString(Value & v, const PosIdx pos, std::string_view errorCtx);
std::string_view forceString(Value & v, NixStringContext & context, const PosIdx pos, std::string_view errorCtx);
std::string_view forceStringNoCtx(Value & v, const PosIdx pos, std::string_view errorCtx);
inline void checkType(Value & v, ValueType vType, Env & env, Expr & e);
inline void checkType(Value & v, ValueType vType);
template<typename... Args>
bool checkBool(Value & v, Args &&... errorArgs);
template<typename... Args>
NixInt checkInt(Value & v, Args &&... errorArgs);
template<typename... Args>
NixFloat checkFloat(Value & v, Args &&... errorArgs);
template<typename... Args>
void checkList(Value & v, Args &&... errorArgs);
template<typename... Args>
Bindings * checkAttrs(Value & v, Args &&... errorArgs);
/**
* Realise the given context, and return a mapping from the placeholders
* used to construct the associated value to their final store path
*/
[[nodiscard]] StringMap realiseContext(const NixStringContext & context);
public:
/**
* @return true iff the value `v` denotes a derivation (i.e. a
* set with attribute `type = "derivation"`).
*/
bool isDerivation(Value & v);
std::optional<std::string> tryAttrsToString(const PosIdx pos, Value & v,
NixStringContext & context, StringCoercionMode mode = StringCoercionMode::Strict, bool copyToStore = true);
/**
* String coercion.
*
* Converts strings, paths and derivations to a
* string. If `copyToStore` is set,
* referenced paths are copied to the Nix store as a side effect.
*/
BackedStringView coerceToString(const PosIdx pos, Value & v, NixStringContext & context,
std::string_view errorCtx,
StringCoercionMode mode = StringCoercionMode::Strict, bool copyToStore = true,
bool canonicalizePath = true);
/**
* Path coercion.
*
* Converts strings, paths and derivations to a
* path. The result is guaranteed to be a canonicalised, absolute
* path. Nothing is copied to the store.
*/
SourcePath coerceToPath(const PosIdx pos, Value & v, NixStringContext & context, std::string_view errorCtx);
/**
* Like coerceToPath, but the result must be a store path.
*/
StorePath coerceToStorePath(const PosIdx pos, Value & v, NixStringContext & context, std::string_view errorCtx);
/**
* Part of `coerceToSingleDerivedPath()` without any store IO which is exposed for unit testing only.
*/
std::pair<SingleDerivedPath, std::string_view> coerceToSingleDerivedPathUnchecked(const PosIdx pos, Value & v, std::string_view errorCtx);
/**
* Coerce to `SingleDerivedPath`.
*
* Must be a string which is either a literal store path.
*
* Even more importantly, the string context must be exactly one
* element, which is either a `NixStringContextElem::Opaque` or
* `NixStringContextElem::Built`. (`NixStringContextEleme::DrvDeep`
* is not permitted).
*
* The string is parsed based on the context --- the context is the
* source of truth, and ultimately tells us what we want, and then
* we ensure the string corresponds to it.
*/
SingleDerivedPath coerceToSingleDerivedPath(const PosIdx pos, Value & v, std::string_view errorCtx);
private:
inline Value * lookupVar(Env * env, const ExprVar & var, bool noEval);
friend struct ExprVar;
friend struct ExprSet;
friend struct ExprLet;
/**
* Current Nix call stack depth, used with `max-call-depth` setting to throw stack overflow hopefully before we run out of system stack.
*/
size_t callDepth = 0;
public:
/**
* Do a deep equality test between two values. That is, list
* elements and attributes are compared recursively.
*/
bool eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_view errorCtx);
bool isFunctor(Value & fun);
Value callFunction(Value & fun, std::span<Value> args, const PosIdx pos);
Value callFunction(Value & fun, Value & arg, const PosIdx pos)
{
return callFunction(fun, {&arg, 1}, pos);
}
/**
* Automatically call a function for which each argument has a
* default value or has a binding in the `args` map.
*/
Value autoCallFunction(Bindings & args, Value & fun, PosIdx pos);
Value mkPos(PosIdx pos);
/**
* Create a string representing a `SingleDerivedPath::Built`.
*
* The string is the printed store path with a context containing a
* single `NixStringContextElem::Built` element of the drv path and
* output name.
*
* @param b the drv whose output we are making a string for, and the
* output
*
* @param staticOutputPath Output path for that string.
* Will be printed to form string.
*/
Value mkOutputString(const SingleDerivedPath::Built & b, const StorePath & staticOutputPath);
Value updateAttrs(const Value & v1, const Value & v2);
Value concatLists(std::span<Value> lists, const PosIdx pos, std::string_view errorCtx);
private:
/**
* Like `mkOutputString` but just creates a raw string, not an
* string Value, which would also have a string context.
*/
std::string mkOutputStringRaw(
const StorePath & staticOutputPath);
/**
* Like `mkSingleDerivedPathStringRaw` but just creates a raw string
* Value, which would also have a string context.
*/
std::string mkSingleDerivedPathStringRaw(
const SingleDerivedPath & p);
};
/**
* @return A string representing the type of the value `v`.
*
* @param withArticle Whether to begin with an english article, e.g. "an
* integer" vs "integer".
*/
std::string_view showType(ValueType type, bool withArticle = true);
std::string showType(const Value & v);
static constexpr std::string_view corepkgsPrefix{"/__corepkgs__/"};
// In C++, template functions need to be defined in the header :/
template<typename... Args>
DebugState::TraceFrame makeDebugTraceStacker(
EvalState & state, Expr & expr, Env & env, std::shared_ptr<Pos> && pos, const Args &... formatArgs
)
{
auto trace = state.ctx.debug->addTrace(
DebugTrace{
.pos = std::move(pos),
.expr = expr,
.env = env,
.hint = HintFmt(formatArgs...),
.isError = false,
}
);
if (state.ctx.debug->stop && state.ctx.debug->errorCallback) {
state.ctx.debug->onEvalError(nullptr, env, expr);
}
return trace;
}
}
#include "lix/libexpr/eval-inline.hh" // IWYU pragma: keep
|