diff options
author | Guillaume Maudoux <guillaume.maudoux@tweag.io> | 2022-03-18 01:25:55 +0100 |
---|---|---|
committer | Guillaume Maudoux <guillaume.maudoux@tweag.io> | 2022-03-18 01:25:55 +0100 |
commit | ca5c3e86abf4ba7ff8e680a0a89c895d452931b9 (patch) | |
tree | 1a5dc481a375e6ab060221118f0d61959a06ecf6 /src/libexpr/eval.cc | |
parent | 1942fed6d9cee95775046c5ad3d253ab2e8ab210 (diff) | |
parent | 6afc3617982e872fac2142c3aeccd1e8482e7e52 (diff) |
Merge remote-tracking branch 'origin/master' into coerce-string
Diffstat (limited to 'src/libexpr/eval.cc')
-rw-r--r-- | src/libexpr/eval.cc | 111 |
1 files changed, 69 insertions, 42 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index e3dfe6718..3ad4df77e 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -63,9 +63,15 @@ static char * dupString(const char * s) } -static char * dupStringWithLen(const char * s, size_t size) +// When there's no need to write to the string, we can optimize away empty +// string allocations. +// This function handles makeImmutableStringWithLen(null, 0) by returning the +// empty string. +static const char * makeImmutableStringWithLen(const char * s, size_t size) { char * t; + if (size == 0) + return ""; #if HAVE_BOEHMGC t = GC_STRNDUP(s, size); #else @@ -75,6 +81,10 @@ static char * dupStringWithLen(const char * s, size_t size) return t; } +static inline const char * makeImmutableString(std::string_view s) { + return makeImmutableStringWithLen(s.data(), s.size()); +} + RootValue allocRootValue(Value * v) { @@ -439,8 +449,10 @@ EvalState::EvalState( , regexCache(makeRegexCache()) #if HAVE_BOEHMGC , valueAllocCache(std::allocate_shared<void *>(traceable_allocator<void *>(), nullptr)) + , env1AllocCache(std::allocate_shared<void *>(traceable_allocator<void *>(), nullptr)) #else , valueAllocCache(std::make_shared<void *>(nullptr)) + , env1AllocCache(std::make_shared<void *>(nullptr)) #endif , baseEnv(allocEnv(128)) , staticBaseEnv(false, 0) @@ -702,9 +714,18 @@ LocalNoInlineNoReturn(void throwTypeErrorWithTrace(const Pos & pos, const char * }).addTrace(p2, s3); } +LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const Suggestions & suggestions, const char * s, const std::string & s2)) +{ + throw EvalError(ErrorInfo { + .msg = hintfmt(s, s2), + .errPos = pos, + .suggestions = suggestions, + }); +} + LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const std::string & s2)) { - throw EvalError({ + throw EvalError(ErrorInfo { .msg = hintfmt(s, s2), .errPos = pos }); @@ -727,6 +748,37 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & p1, const char * s, const }); } +LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s)) +{ + throw TypeError({ + .msg = hintfmt(s), + .errPos = pos + }); +} + +LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const ExprLambda & fun, const Symbol & s2)) +{ + throw TypeError({ + .msg = hintfmt(s, fun.showNamePos(), s2), + .errPos = pos + }); +} + +LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const Suggestions & suggestions, const char * s, const ExprLambda & fun, const Symbol & s2)) +{ + throw TypeError(ErrorInfo { + .msg = hintfmt(s, fun.showNamePos(), s2), + .errPos = pos, + .suggestions = suggestions, + }); +} + + +LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v)) +{ + throw TypeError(s, showType(v)); +} + LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s, const std::string & s1)) { throw AssertionError({ @@ -764,7 +816,7 @@ LocalNoInline(void addErrorTrace(Error & e, const Pos & pos, const char * s, con void Value::mkString(std::string_view s) { - mkString(dupStringWithLen(s.data(), s.size())); + mkString(makeImmutableString(s)); } @@ -795,7 +847,7 @@ void Value::mkStringMove(const char * s, const PathSet & context) void Value::mkPath(std::string_view s) { - mkPath(dupStringWithLen(s.data(), s.size())); + mkPath(makeImmutableString(s)); } @@ -825,42 +877,6 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval) } -Value * EvalState::allocValue() -{ - /* We use the boehm batch allocator to speed up allocations of Values (of which there are many). - GC_malloc_many returns a linked list of objects of the given size, where the first word - of each object is also the pointer to the next object in the list. This also means that we - have to explicitly clear the first word of every object we take. */ - if (!*valueAllocCache) { - *valueAllocCache = GC_malloc_many(sizeof(Value)); - if (!*valueAllocCache) throw std::bad_alloc(); - } - - /* GC_NEXT is a convenience macro for accessing the first word of an object. - Take the first list item, advance the list to the next item, and clear the next pointer. */ - void * p = *valueAllocCache; - GC_PTR_STORE_AND_DIRTY(&*valueAllocCache, GC_NEXT(p)); - GC_NEXT(p) = nullptr; - - nrValues++; - auto v = (Value *) p; - return v; -} - - -Env & EvalState::allocEnv(size_t size) -{ - nrEnvs++; - nrValuesInEnvs += size; - Env * env = (Env *) allocBytes(sizeof(Env) + size * sizeof(Value *)); - env->type = Env::Plain; - - /* We assume that env->values has been cleared by the allocator; maybeThunk() and lookupVar fromWith expect this. */ - - return *env; -} - - void EvalState::mkList(Value & v, size_t size) { v.mkList(size); @@ -1230,8 +1246,15 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v) } } else { state.forceAttrs(*vAttrs, pos, "While selecting an attribute"); - if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) - throwEvalError(pos, "attribute '%1%' missing", name); + if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) { + std::set<std::string> allAttrNames; + for (auto & attr : *vAttrs->attrs) + allAttrNames.insert(attr.name); + throwEvalError( + pos, + Suggestions::bestMatches(allAttrNames, name), + "attribute '%1%' missing", name); + } } vAttrs = j->value; pos2 = j->pos; @@ -1360,7 +1383,11 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & user. */ for (auto & i : *args[0]->attrs) if (!lambda.formals->has(i.name)) { + std::set<std::string> formalNames; + for (auto & formal : lambda.formals->formals) + formalNames.insert(formal.name); throwTypeErrorWithTrace(lambda.pos, + Suggestions::bestMatches(formalNames, i.name), "Function '%1%' called with unexpected argument '%2%'", prettyLambdaName(lambda), i.name, pos, "from call site"); } |