aboutsummaryrefslogtreecommitdiff
path: root/src/libexpr/eval.hh
diff options
context:
space:
mode:
Diffstat (limited to 'src/libexpr/eval.hh')
-rw-r--r--src/libexpr/eval.hh262
1 files changed, 212 insertions, 50 deletions
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 1f0e97b2e..f07f15d43 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -13,7 +13,6 @@
#include <unordered_map>
#include <mutex>
-
namespace nix {
@@ -23,18 +22,22 @@ class StorePath;
enum RepairFlag : bool;
-typedef void (* PrimOpFun) (EvalState & state, const Pos & pos, Value * * args, Value & v);
-
+typedef void (* PrimOpFun) (EvalState & state, const PosIdx pos, Value * * args, Value & v);
struct PrimOp
{
PrimOpFun fun;
size_t arity;
- Symbol name;
+ std::string name;
std::vector<std::string> args;
const char * doc = nullptr;
};
+#if HAVE_BOEHMGC
+ typedef std::map<std::string, Value *, std::less<std::string>, traceable_allocator<std::pair<const std::string, Value *> > > ValMap;
+#else
+ typedef std::map<std::string, Value *> ValMap;
+#endif
struct Env
{
@@ -44,6 +47,10 @@ struct Env
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, PathSet & context);
@@ -53,7 +60,9 @@ void copyContext(const Value & v, PathSet & context);
typedef std::map<Path, StorePath> SrcToStore;
-std::ostream & operator << (std::ostream & str, const Value & v);
+std::ostream & printValue(const EvalState & state, std::ostream & str, const Value & v);
+std::string printValue(const EvalState & state, const Value & v);
+std::ostream & operator << (std::ostream & os, const ValueType t);
typedef std::pair<std::string, std::string> SearchPathElem;
@@ -68,21 +77,34 @@ struct RegexCache;
std::shared_ptr<RegexCache> makeRegexCache();
+struct DebugTrace {
+ std::optional<ErrPos> pos;
+ const Expr & expr;
+ const Env & env;
+ hintformat hint;
+ bool isError;
+};
+
+void debugError(Error * e, Env & env, Expr & expr);
-class EvalState
+class EvalState : public std::enable_shared_from_this<EvalState>
{
public:
SymbolTable symbols;
+ PosTable positions;
+
+ static inline std::string derivationNixPath = "//builtin/derivation.nix";
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue,
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls,
sFile, sLine, sColumn, sFunctor, sToString,
sRight, sWrong, sStructuredAttrs, sBuilder, sArgs,
- sContentAddressed,
+ sContentAddressed, sImpure,
sOutputHash, sOutputHashAlgo, sOutputHashMode,
sRecurseForDerivations,
sDescription, sSelf, sEpsilon, sStartSet, sOperator, sKey, sPath,
- sPrefix;
+ sPrefix,
+ sOutputSpecified;
Symbol sDerivationNix;
/* If set, force copying files to the Nix store even if they
@@ -104,12 +126,56 @@ public:
RootValue vCallFlake = nullptr;
RootValue vImportedDrvToDerivation = nullptr;
+ /* Debugger */
+ void (* debugRepl)(ref<EvalState> es, const ValMap & extraEnv);
+ bool debugStop;
+ bool debugQuit;
+ int trylevel;
+ std::list<DebugTrace> debugTraces;
+ std::map<const Expr*, const std::shared_ptr<const StaticEnv>> exprEnvs;
+ const std::shared_ptr<const StaticEnv> getStaticEnv(const Expr & expr) const
+ {
+ auto i = exprEnvs.find(&expr);
+ if (i != exprEnvs.end())
+ return i->second;
+ else
+ return std::shared_ptr<const StaticEnv>();;
+ }
+
+ void runDebugRepl(const Error * error, const Env & env, const Expr & expr);
+
+ template<class E>
+ [[gnu::noinline, gnu::noreturn]]
+ void debugThrow(E && error, const Env & env, const Expr & expr)
+ {
+ if (debugRepl)
+ runDebugRepl(&error, env, expr);
+
+ throw std::move(error);
+ }
+
+ template<class E>
+ [[gnu::noinline, gnu::noreturn]]
+ void debugThrowLastTrace(E && e)
+ {
+ // Call this in the situation where Expr and Env are inaccessible.
+ // The debugger will start in the last context that's in the
+ // DebugTrace stack.
+ if (debugRepl && !debugTraces.empty()) {
+ const DebugTrace & last = debugTraces.front();
+ runDebugRepl(&e, last.env, last.expr);
+ }
+
+ throw std::move(e);
+ }
+
+
private:
SrcToStore srcToStore;
/* A cache from path names to parse trees. */
#if HAVE_BOEHMGC
- typedef std::map<Path, Expr *, std::less<Path>, traceable_allocator<std::pair<const Path, Expr *> > > FileParseCache;
+ typedef std::map<Path, Expr *, std::less<Path>, traceable_allocator<std::pair<const Path, Expr *>>> FileParseCache;
#else
typedef std::map<Path, Expr *> FileParseCache;
#endif
@@ -117,7 +183,7 @@ private:
/* A cache from path names to values. */
#if HAVE_BOEHMGC
- typedef std::map<Path, Value, std::less<Path>, traceable_allocator<std::pair<const Path, Value> > > FileEvalCache;
+ typedef std::map<Path, Value, std::less<Path>, traceable_allocator<std::pair<const Path, Value>>> FileEvalCache;
#else
typedef std::map<Path, Value> FileEvalCache;
#endif
@@ -133,9 +199,14 @@ private:
/* Cache used by prim_match(). */
std::shared_ptr<RegexCache> regexCache;
+#if HAVE_BOEHMGC
/* Allocation cache for GC'd Value objects. */
std::shared_ptr<void *> valueAllocCache;
+ /* Allocation cache for size-1 Env objects. */
+ std::shared_ptr<void *> env1AllocCache;
+#endif
+
public:
EvalState(
@@ -144,12 +215,6 @@ public:
std::shared_ptr<Store> buildStore = nullptr);
~EvalState();
- void requireExperimentalFeatureOnEvaluation(
- const ExperimentalFeature &,
- const std::string_view fName,
- const Pos & pos
- );
-
void addToSearchPath(const std::string & s);
SearchPath getSearchPath() { return searchPath; }
@@ -161,6 +226,9 @@ public:
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. */
+ void allowAndSetStorePathString(const StorePath & storePath, Value & v);
+
/* Check whether access to a path is allowed and throw an error if
not. Otherwise return the canonicalised path. */
Path checkSourcePath(const Path & path);
@@ -178,10 +246,10 @@ public:
/* Parse a Nix expression from the specified file. */
Expr * parseExprFromFile(const Path & path);
- Expr * parseExprFromFile(const Path & path, StaticEnv & staticEnv);
+ Expr * parseExprFromFile(const Path & path, std::shared_ptr<StaticEnv> & staticEnv);
/* Parse a Nix expression from the specified string. */
- Expr * parseExprFromString(std::string s, const Path & basePath, StaticEnv & staticEnv);
+ Expr * parseExprFromString(std::string s, const Path & basePath, std::shared_ptr<StaticEnv> & staticEnv);
Expr * parseExprFromString(std::string s, const Path & basePath);
Expr * parseStdin();
@@ -191,7 +259,7 @@ public:
trivial (i.e. doesn't require arbitrary computation). */
void evalFile(const Path & path, Value & v, bool mustBeTrivial = false);
- /* Like `cacheFile`, but with an already parsed expression. */
+ /* Like `evalFile`, but with an already parsed expression. */
void cacheFile(
const Path & path,
const Path & resolvedPath,
@@ -203,7 +271,7 @@ public:
/* Look up a file in the search path. */
Path findFile(const std::string_view path);
- Path findFile(SearchPath & searchPath, const std::string_view path, const Pos & pos = noPos);
+ Path findFile(SearchPath & searchPath, const std::string_view path, const PosIdx pos = noPos);
/* If the specified search path element is a URI, download it. */
std::pair<bool, std::string> resolveSearchPathElem(const SearchPathElem & elem);
@@ -215,14 +283,14 @@ public:
/* Evaluation the expression, then verify that it has the expected
type. */
inline bool evalBool(Env & env, Expr * e);
- inline bool evalBool(Env & env, Expr * e, const Pos & pos);
+ inline bool evalBool(Env & env, Expr * e, const PosIdx pos);
inline void evalAttrs(Env & env, Expr * e, Value & v);
/* 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 Pos & pos);
+ inline void forceValue(Value & v, const PosIdx pos);
template <typename Callable>
inline void forceValue(Value & v, Callable getPos);
@@ -232,33 +300,103 @@ public:
void forceValueDeep(Value & v);
/* Force `v', and then verify that it has the expected type. */
- NixInt forceInt(Value & v, const Pos & pos);
- NixFloat forceFloat(Value & v, const Pos & pos);
- bool forceBool(Value & v, const Pos & pos);
+ NixInt forceInt(Value & v, const PosIdx pos);
+ NixFloat forceFloat(Value & v, const PosIdx pos);
+ bool forceBool(Value & v, const PosIdx pos);
- void forceAttrs(Value & v, const Pos & pos);
+ void forceAttrs(Value & v, const PosIdx pos);
template <typename Callable>
inline void forceAttrs(Value & v, Callable getPos);
- inline void forceList(Value & v, const Pos & pos);
- void forceFunction(Value & v, const Pos & pos); // either lambda or primop
- std::string_view forceString(Value & v, const Pos & pos = noPos);
- std::string_view forceString(Value & v, PathSet & context, const Pos & pos = noPos);
- std::string_view forceStringNoCtx(Value & v, const Pos & pos = noPos);
+ inline void forceList(Value & v, const PosIdx pos);
+ void forceFunction(Value & v, const PosIdx pos); // either lambda or primop
+ std::string_view forceString(Value & v, const PosIdx pos = noPos);
+ std::string_view forceString(Value & v, PathSet & context, const PosIdx pos = noPos);
+ std::string_view forceStringNoCtx(Value & v, const PosIdx pos = noPos);
+
+ [[gnu::noinline, gnu::noreturn]]
+ void throwEvalError(const PosIdx pos, const char * s);
+ [[gnu::noinline, gnu::noreturn]]
+ void throwEvalError(const PosIdx pos, const char * s,
+ Env & env, Expr & expr);
+ [[gnu::noinline, gnu::noreturn]]
+ void throwEvalError(const char * s, const std::string & s2);
+ [[gnu::noinline, gnu::noreturn]]
+ void throwEvalError(const PosIdx pos, const char * s, const std::string & s2);
+ [[gnu::noinline, gnu::noreturn]]
+ void throwEvalError(const char * s, const std::string & s2,
+ Env & env, Expr & expr);
+ [[gnu::noinline, gnu::noreturn]]
+ void throwEvalError(const PosIdx pos, const char * s, const std::string & s2,
+ Env & env, Expr & expr);
+ [[gnu::noinline, gnu::noreturn]]
+ void throwEvalError(const char * s, const std::string & s2, const std::string & s3,
+ Env & env, Expr & expr);
+ [[gnu::noinline, gnu::noreturn]]
+ void throwEvalError(const PosIdx pos, const char * s, const std::string & s2, const std::string & s3,
+ Env & env, Expr & expr);
+ [[gnu::noinline, gnu::noreturn]]
+ void throwEvalError(const PosIdx pos, const char * s, const std::string & s2, const std::string & s3);
+ [[gnu::noinline, gnu::noreturn]]
+ void throwEvalError(const char * s, const std::string & s2, const std::string & s3);
+ [[gnu::noinline, gnu::noreturn]]
+ void throwEvalError(const PosIdx pos, const Suggestions & suggestions, const char * s, const std::string & s2,
+ Env & env, Expr & expr);
+ [[gnu::noinline, gnu::noreturn]]
+ void throwEvalError(const PosIdx p1, const char * s, const Symbol sym, const PosIdx p2,
+ Env & env, Expr & expr);
+
+ [[gnu::noinline, gnu::noreturn]]
+ void throwTypeError(const PosIdx pos, const char * s, const Value & v);
+ [[gnu::noinline, gnu::noreturn]]
+ void throwTypeError(const PosIdx pos, const char * s, const Value & v,
+ Env & env, Expr & expr);
+ [[gnu::noinline, gnu::noreturn]]
+ void throwTypeError(const PosIdx pos, const char * s);
+ [[gnu::noinline, gnu::noreturn]]
+ void throwTypeError(const PosIdx pos, const char * s,
+ Env & env, Expr & expr);
+ [[gnu::noinline, gnu::noreturn]]
+ void throwTypeError(const PosIdx pos, const char * s, const ExprLambda & fun, const Symbol s2,
+ Env & env, Expr & expr);
+ [[gnu::noinline, gnu::noreturn]]
+ void throwTypeError(const PosIdx pos, const Suggestions & suggestions, const char * s, const ExprLambda & fun, const Symbol s2,
+ Env & env, Expr & expr);
+ [[gnu::noinline, gnu::noreturn]]
+ void throwTypeError(const char * s, const Value & v,
+ Env & env, Expr & expr);
+
+ [[gnu::noinline, gnu::noreturn]]
+ void throwAssertionError(const PosIdx pos, const char * s, const std::string & s1,
+ Env & env, Expr & expr);
+
+ [[gnu::noinline, gnu::noreturn]]
+ void throwUndefinedVarError(const PosIdx pos, const char * s, const std::string & s1,
+ Env & env, Expr & expr);
+
+ [[gnu::noinline, gnu::noreturn]]
+ void throwMissingArgumentError(const PosIdx pos, const char * s, const std::string & s1,
+ Env & env, Expr & expr);
+
+ [[gnu::noinline]]
+ void addErrorTrace(Error & e, const char * s, const std::string & s2) const;
+ [[gnu::noinline]]
+ void addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2) const;
+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 Pos & pos, Value & v,
+ std::optional<std::string> tryAttrsToString(const PosIdx pos, Value & v,
PathSet & context, bool coerceMore = false, bool copyToStore = true);
/* String coercion. Converts strings, paths and derivations to a
string. If `coerceMore' is set, also converts nulls, integers,
booleans and lists to a string. If `copyToStore' is set,
referenced paths are copied to the Nix store as a side effect. */
- BackedStringView coerceToString(const Pos & pos, Value & v, PathSet & context,
+ BackedStringView coerceToString(const PosIdx pos, Value & v, PathSet & context,
bool coerceMore = false, bool copyToStore = true,
bool canonicalizePath = true);
@@ -267,7 +405,10 @@ public:
/* 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. */
- Path coerceToPath(const Pos & pos, Value & v, PathSet & context);
+ Path coerceToPath(const PosIdx pos, Value & v, PathSet & context);
+
+ /* Like coerceToPath, but the result must be a store path. */
+ StorePath coerceToStorePath(const PosIdx pos, Value & v, PathSet & context);
public:
@@ -276,7 +417,7 @@ public:
Env & baseEnv;
/* The same, but used during parsing to resolve variables. */
- StaticEnv staticBaseEnv; // !!! should be private
+ std::shared_ptr<StaticEnv> staticBaseEnv; // !!! should be private
private:
@@ -300,7 +441,7 @@ public:
struct Doc
{
Pos pos;
- std::optional<Symbol> name;
+ std::optional<std::string> name;
size_t arity;
std::vector<std::string> args;
const char * doc;
@@ -317,7 +458,7 @@ private:
friend struct ExprLet;
Expr * parse(char * text, size_t length, FileOrigin origin, const PathView path,
- const PathView basePath, StaticEnv & staticEnv);
+ const PathView basePath, std::shared_ptr<StaticEnv> & staticEnv);
public:
@@ -328,9 +469,9 @@ public:
bool isFunctor(Value & fun);
// FIXME: use std::span
- void callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const Pos & pos);
+ void callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const PosIdx pos);
- void callFunction(Value & fun, Value & arg, Value & vRes, const Pos & pos)
+ void callFunction(Value & fun, Value & arg, Value & vRes, const PosIdx pos)
{
Value * args[] = {&arg};
callFunction(fun, 1, args, vRes, pos);
@@ -341,10 +482,10 @@ public:
void autoCallFunction(Bindings & args, Value & fun, Value & res);
/* Allocation primitives. */
- Value * allocValue();
- Env & allocEnv(size_t size);
+ inline Value * allocValue();
+ inline Env & allocEnv(size_t size);
- Value * allocAttr(Value & vAttrs, const Symbol & name);
+ Value * allocAttr(Value & vAttrs, Symbol name);
Value * allocAttr(Value & vAttrs, std::string_view name);
Bindings * allocBindings(size_t capacity);
@@ -356,9 +497,9 @@ public:
void mkList(Value & v, size_t length);
void mkThunk_(Value & v, Expr * expr);
- void mkPos(Value & v, ptr<Pos> pos);
+ void mkPos(Value & v, PosIdx pos);
- void concatLists(Value & v, size_t nrLists, Value * * lists, const Pos & pos);
+ void concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos);
/* Print statistics. */
void printStats();
@@ -386,7 +527,7 @@ private:
bool countCalls;
- typedef std::map<Symbol, size_t> PrimOpCalls;
+ typedef std::map<std::string, size_t> PrimOpCalls;
PrimOpCalls primOpCalls;
typedef std::map<ExprLambda *, size_t> FunctionCalls;
@@ -394,7 +535,7 @@ private:
void incrFunctionCall(ExprLambda * fun);
- typedef std::map<Pos, size_t> AttrSelects;
+ typedef std::map<PosIdx, size_t> AttrSelects;
AttrSelects attrSelects;
friend struct ExprOpUpdate;
@@ -405,13 +546,23 @@ private:
friend struct ExprFloat;
friend struct ExprPath;
friend struct ExprSelect;
- friend void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v);
- friend void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v);
- friend void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v);
+ friend void prim_getAttr(EvalState & state, const PosIdx pos, Value * * args, Value & v);
+ friend void prim_match(EvalState & state, const PosIdx pos, Value * * args, Value & v);
+ friend void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v);
friend struct Value;
};
+struct DebugTraceStacker {
+ DebugTraceStacker(EvalState & evalState, DebugTrace t);
+ ~DebugTraceStacker()
+ {
+ // assert(evalState.debugTraces.front() == trace);
+ evalState.debugTraces.pop_front();
+ }
+ EvalState & evalState;
+ DebugTrace trace;
+};
/* Return a string representing the type of the value `v'. */
std::string_view showType(ValueType type);
@@ -419,7 +570,7 @@ std::string showType(const Value & v);
/* Decode a context string ‘!<name>!<path>’ into a pair <path,
name>. */
-std::pair<std::string, std::string> decodeContext(std::string_view s);
+NixStringContextElem decodeContext(const Store & store, std::string_view s);
/* If `path' refers to a directory, then append "/default.nix". */
Path resolveExprPath(Path path);
@@ -496,6 +647,15 @@ struct EvalSettings : Config
Setting<bool> useEvalCache{this, true, "eval-cache",
"Whether to use the flake evaluation cache."};
+
+ Setting<bool> ignoreExceptionsDuringTry{this, false, "ignore-try",
+ R"(
+ If set to true, ignore exceptions inside 'tryEval' calls when evaluating nix expressions in
+ debug mode (using the --debugger flag). By default the debugger will pause on all exceptions.
+ )"};
+
+ Setting<bool> traceVerbose{this, false, "trace-verbose",
+ "Whether `builtins.traceVerbose` should trace its first argument when evaluated."};
};
extern EvalSettings evalSettings;
@@ -503,3 +663,5 @@ extern EvalSettings evalSettings;
static const std::string corepkgsPrefix{"/__corepkgs__/"};
}
+
+#include "eval-inline.hh"