aboutsummaryrefslogtreecommitdiff
path: root/src/libexpr/eval.cc
diff options
context:
space:
mode:
authorGuillaume Maudoux <guillaume.maudoux@tweag.io>2022-03-18 01:25:55 +0100
committerGuillaume Maudoux <guillaume.maudoux@tweag.io>2022-03-18 01:25:55 +0100
commitca5c3e86abf4ba7ff8e680a0a89c895d452931b9 (patch)
tree1a5dc481a375e6ab060221118f0d61959a06ecf6 /src/libexpr/eval.cc
parent1942fed6d9cee95775046c5ad3d253ab2e8ab210 (diff)
parent6afc3617982e872fac2142c3aeccd1e8482e7e52 (diff)
Merge remote-tracking branch 'origin/master' into coerce-string
Diffstat (limited to 'src/libexpr/eval.cc')
-rw-r--r--src/libexpr/eval.cc111
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");
}