mirror of
https://github.com/NixOS/nix
synced 2025-06-26 11:41:15 +02:00
Revert "Revert "Merge pull request #6204 from layus/coerce-string""
This reverts commit 9b33ef3879
.
This commit is contained in:
parent
38b90c618f
commit
e4726a0c79
31 changed files with 986 additions and 863 deletions
|
@ -318,7 +318,7 @@ static Symbol getName(const AttrName & name, EvalState & state, Env & env)
|
|||
} else {
|
||||
Value nameValue;
|
||||
name.expr->eval(state, env, nameValue);
|
||||
state.forceStringNoCtx(nameValue);
|
||||
state.forceStringNoCtx(nameValue, noPos, "while evaluating an attribute name");
|
||||
return state.symbols.create(nameValue.string.s);
|
||||
}
|
||||
}
|
||||
|
@ -414,6 +414,44 @@ static Strings parseNixPath(const std::string & s)
|
|||
return res;
|
||||
}
|
||||
|
||||
ErrorBuilder & ErrorBuilder::atPos(PosIdx pos)
|
||||
{
|
||||
info.errPos = state.positions[pos];
|
||||
return *this;
|
||||
}
|
||||
|
||||
ErrorBuilder & ErrorBuilder::withTrace(PosIdx pos, const std::string_view text)
|
||||
{
|
||||
info.traces.push_front(Trace{ .pos = state.positions[pos], .hint = hintformat(std::string(text)), .frame = false });
|
||||
return *this;
|
||||
}
|
||||
|
||||
ErrorBuilder & ErrorBuilder::withFrameTrace(PosIdx pos, const std::string_view text)
|
||||
{
|
||||
info.traces.push_front(Trace{ .pos = state.positions[pos], .hint = hintformat(std::string(text)), .frame = true });
|
||||
return *this;
|
||||
}
|
||||
|
||||
ErrorBuilder & ErrorBuilder::withSuggestions(Suggestions & s)
|
||||
{
|
||||
info.suggestions = s;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ErrorBuilder & ErrorBuilder::withFrame(const Env & env, const Expr & expr)
|
||||
{
|
||||
// NOTE: This is abusing side-effects.
|
||||
// TODO: check compatibility with nested debugger calls.
|
||||
state.debugTraces.push_front(DebugTrace {
|
||||
.pos = nullptr,
|
||||
.expr = expr,
|
||||
.env = env,
|
||||
.hint = hintformat("Fake frame for debugging purposes"),
|
||||
.isError = true
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
EvalState::EvalState(
|
||||
const Strings & _searchPath,
|
||||
|
@ -646,25 +684,7 @@ void EvalState::addConstant(const std::string & name, Value * v)
|
|||
Value * EvalState::addPrimOp(const std::string & name,
|
||||
size_t arity, PrimOpFun primOp)
|
||||
{
|
||||
auto name2 = name.substr(0, 2) == "__" ? name.substr(2) : name;
|
||||
auto sym = symbols.create(name2);
|
||||
|
||||
/* Hack to make constants lazy: turn them into a application of
|
||||
the primop to a dummy value. */
|
||||
if (arity == 0) {
|
||||
auto vPrimOp = allocValue();
|
||||
vPrimOp->mkPrimOp(new PrimOp { .fun = primOp, .arity = 1, .name = name2 });
|
||||
Value v;
|
||||
v.mkApp(vPrimOp, vPrimOp);
|
||||
return addConstant(name, v);
|
||||
}
|
||||
|
||||
Value * v = allocValue();
|
||||
v->mkPrimOp(new PrimOp { .fun = primOp, .arity = arity, .name = name2 });
|
||||
staticBaseEnv->vars.emplace_back(symbols.create(name), baseEnvDispl);
|
||||
baseEnv.values[baseEnvDispl++] = v;
|
||||
baseEnv.values[0]->attrs->push_back(Attr(sym, v));
|
||||
return v;
|
||||
return addPrimOp(PrimOp { .fun = primOp, .arity = arity, .name = name });
|
||||
}
|
||||
|
||||
|
||||
|
@ -842,176 +862,14 @@ void EvalState::runDebugRepl(const Error * error, const Env & env, const Expr &
|
|||
}
|
||||
}
|
||||
|
||||
/* Every "format" object (even temporary) takes up a few hundred bytes
|
||||
of stack space, which is a real killer in the recursive
|
||||
evaluator. So here are some helper functions for throwing
|
||||
exceptions. */
|
||||
void EvalState::throwEvalError(const PosIdx pos, const char * s, Env & env, Expr & expr)
|
||||
{
|
||||
debugThrow(EvalError({
|
||||
.msg = hintfmt(s),
|
||||
.errPos = positions[pos]
|
||||
}), env, expr);
|
||||
}
|
||||
|
||||
void EvalState::throwEvalError(const PosIdx pos, const char * s)
|
||||
{
|
||||
debugThrowLastTrace(EvalError({
|
||||
.msg = hintfmt(s),
|
||||
.errPos = positions[pos]
|
||||
}));
|
||||
}
|
||||
|
||||
void EvalState::throwEvalError(const char * s, const std::string & s2)
|
||||
{
|
||||
debugThrowLastTrace(EvalError(s, s2));
|
||||
}
|
||||
|
||||
void EvalState::throwEvalError(const PosIdx pos, const Suggestions & suggestions, const char * s,
|
||||
const std::string & s2, Env & env, Expr & expr)
|
||||
{
|
||||
debugThrow(EvalError(ErrorInfo{
|
||||
.msg = hintfmt(s, s2),
|
||||
.errPos = positions[pos],
|
||||
.suggestions = suggestions,
|
||||
}), env, expr);
|
||||
}
|
||||
|
||||
void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2)
|
||||
{
|
||||
debugThrowLastTrace(EvalError({
|
||||
.msg = hintfmt(s, s2),
|
||||
.errPos = positions[pos]
|
||||
}));
|
||||
}
|
||||
|
||||
void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2, Env & env, Expr & expr)
|
||||
{
|
||||
debugThrow(EvalError({
|
||||
.msg = hintfmt(s, s2),
|
||||
.errPos = positions[pos]
|
||||
}), env, expr);
|
||||
}
|
||||
|
||||
void EvalState::throwEvalError(const char * s, const std::string & s2,
|
||||
const std::string & s3)
|
||||
{
|
||||
debugThrowLastTrace(EvalError({
|
||||
.msg = hintfmt(s, s2, s3),
|
||||
.errPos = positions[noPos]
|
||||
}));
|
||||
}
|
||||
|
||||
void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2,
|
||||
const std::string & s3)
|
||||
{
|
||||
debugThrowLastTrace(EvalError({
|
||||
.msg = hintfmt(s, s2, s3),
|
||||
.errPos = positions[pos]
|
||||
}));
|
||||
}
|
||||
|
||||
void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2,
|
||||
const std::string & s3, Env & env, Expr & expr)
|
||||
{
|
||||
debugThrow(EvalError({
|
||||
.msg = hintfmt(s, s2, s3),
|
||||
.errPos = positions[pos]
|
||||
}), env, expr);
|
||||
}
|
||||
|
||||
void EvalState::throwEvalError(const PosIdx p1, const char * s, const Symbol sym, const PosIdx p2, Env & env, Expr & expr)
|
||||
{
|
||||
// p1 is where the error occurred; p2 is a position mentioned in the message.
|
||||
debugThrow(EvalError({
|
||||
.msg = hintfmt(s, symbols[sym], positions[p2]),
|
||||
.errPos = positions[p1]
|
||||
}), env, expr);
|
||||
}
|
||||
|
||||
void EvalState::throwTypeError(const PosIdx pos, const char * s, const Value & v)
|
||||
{
|
||||
debugThrowLastTrace(TypeError({
|
||||
.msg = hintfmt(s, showType(v)),
|
||||
.errPos = positions[pos]
|
||||
}));
|
||||
}
|
||||
|
||||
void EvalState::throwTypeError(const PosIdx pos, const char * s, const Value & v, Env & env, Expr & expr)
|
||||
{
|
||||
debugThrow(TypeError({
|
||||
.msg = hintfmt(s, showType(v)),
|
||||
.errPos = positions[pos]
|
||||
}), env, expr);
|
||||
}
|
||||
|
||||
void EvalState::throwTypeError(const PosIdx pos, const char * s)
|
||||
{
|
||||
debugThrowLastTrace(TypeError({
|
||||
.msg = hintfmt(s),
|
||||
.errPos = positions[pos]
|
||||
}));
|
||||
}
|
||||
|
||||
void EvalState::throwTypeError(const PosIdx pos, const char * s, const ExprLambda & fun,
|
||||
const Symbol s2, Env & env, Expr &expr)
|
||||
{
|
||||
debugThrow(TypeError({
|
||||
.msg = hintfmt(s, fun.showNamePos(*this), symbols[s2]),
|
||||
.errPos = positions[pos]
|
||||
}), env, expr);
|
||||
}
|
||||
|
||||
void EvalState::throwTypeError(const PosIdx pos, const Suggestions & suggestions, const char * s,
|
||||
const ExprLambda & fun, const Symbol s2, Env & env, Expr &expr)
|
||||
{
|
||||
debugThrow(TypeError(ErrorInfo {
|
||||
.msg = hintfmt(s, fun.showNamePos(*this), symbols[s2]),
|
||||
.errPos = positions[pos],
|
||||
.suggestions = suggestions,
|
||||
}), env, expr);
|
||||
}
|
||||
|
||||
void EvalState::throwTypeError(const char * s, const Value & v, Env & env, Expr &expr)
|
||||
{
|
||||
debugThrow(TypeError({
|
||||
.msg = hintfmt(s, showType(v)),
|
||||
.errPos = positions[expr.getPos()],
|
||||
}), env, expr);
|
||||
}
|
||||
|
||||
void EvalState::throwAssertionError(const PosIdx pos, const char * s, const std::string & s1, Env & env, Expr &expr)
|
||||
{
|
||||
debugThrow(AssertionError({
|
||||
.msg = hintfmt(s, s1),
|
||||
.errPos = positions[pos]
|
||||
}), env, expr);
|
||||
}
|
||||
|
||||
void EvalState::throwUndefinedVarError(const PosIdx pos, const char * s, const std::string & s1, Env & env, Expr &expr)
|
||||
{
|
||||
debugThrow(UndefinedVarError({
|
||||
.msg = hintfmt(s, s1),
|
||||
.errPos = positions[pos]
|
||||
}), env, expr);
|
||||
}
|
||||
|
||||
void EvalState::throwMissingArgumentError(const PosIdx pos, const char * s, const std::string & s1, Env & env, Expr &expr)
|
||||
{
|
||||
debugThrow(MissingArgumentError({
|
||||
.msg = hintfmt(s, s1),
|
||||
.errPos = positions[pos]
|
||||
}), env, expr);
|
||||
}
|
||||
|
||||
void EvalState::addErrorTrace(Error & e, const char * s, const std::string & s2) const
|
||||
{
|
||||
e.addTrace(nullptr, s, s2);
|
||||
}
|
||||
|
||||
void EvalState::addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2) const
|
||||
void EvalState::addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2, bool frame) const
|
||||
{
|
||||
e.addTrace(positions[pos], s, s2);
|
||||
e.addTrace(positions[pos], hintfmt(s, s2), frame);
|
||||
}
|
||||
|
||||
static std::unique_ptr<DebugTraceStacker> makeDebugTraceStacker(
|
||||
|
@ -1088,7 +946,7 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
|
|||
if (env->type == Env::HasWithExpr) {
|
||||
if (noEval) return 0;
|
||||
Value * v = allocValue();
|
||||
evalAttrs(*env->up, (Expr *) env->values[0], *v);
|
||||
evalAttrs(*env->up, (Expr *) env->values[0], *v, noPos, "<borked>");
|
||||
env->values[0] = v;
|
||||
env->type = Env::HasWithAttrs;
|
||||
}
|
||||
|
@ -1098,7 +956,7 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
|
|||
return j->value;
|
||||
}
|
||||
if (!env->prevWith)
|
||||
throwUndefinedVarError(var.pos, "undefined variable '%1%'", symbols[var.name], *env, const_cast<ExprVar&>(var));
|
||||
error("undefined variable '%1%'", symbols[var.name]).atPos(var.pos).withFrame(*env, var).debugThrow<UndefinedVarError>();
|
||||
for (size_t l = env->prevWith; l; --l, env = env->up) ;
|
||||
}
|
||||
}
|
||||
|
@ -1248,7 +1106,7 @@ void EvalState::cacheFile(
|
|||
// computation.
|
||||
if (mustBeTrivial &&
|
||||
!(dynamic_cast<ExprAttrs *>(e)))
|
||||
throw EvalError("file '%s' must be an attribute set", path);
|
||||
error("file '%s' must be an attribute set", path).debugThrow<EvalError>();
|
||||
eval(e, v);
|
||||
} catch (Error & e) {
|
||||
addErrorTrace(e, "while evaluating the file '%1%':", resolvedPath);
|
||||
|
@ -1266,31 +1124,31 @@ void EvalState::eval(Expr * e, Value & v)
|
|||
}
|
||||
|
||||
|
||||
inline bool EvalState::evalBool(Env & env, Expr * e)
|
||||
inline bool EvalState::evalBool(Env & env, Expr * e, const PosIdx pos, std::string_view errorCtx)
|
||||
{
|
||||
Value v;
|
||||
e->eval(*this, env, v);
|
||||
if (v.type() != nBool)
|
||||
throwTypeError(noPos, "value is %1% while a Boolean was expected", v, env, *e);
|
||||
return v.boolean;
|
||||
try {
|
||||
Value v;
|
||||
e->eval(*this, env, v);
|
||||
if (v.type() != nBool)
|
||||
error("value is %1% while a Boolean was expected", showType(v)).withFrame(env, *e).debugThrow<TypeError>();
|
||||
return v.boolean;
|
||||
} catch (Error & e) {
|
||||
e.addTrace(positions[pos], errorCtx);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline bool EvalState::evalBool(Env & env, Expr * e, const PosIdx pos)
|
||||
inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const PosIdx pos, std::string_view errorCtx)
|
||||
{
|
||||
Value v;
|
||||
e->eval(*this, env, v);
|
||||
if (v.type() != nBool)
|
||||
throwTypeError(pos, "value is %1% while a Boolean was expected", v, env, *e);
|
||||
return v.boolean;
|
||||
}
|
||||
|
||||
|
||||
inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v)
|
||||
{
|
||||
e->eval(*this, env, v);
|
||||
if (v.type() != nAttrs)
|
||||
throwTypeError(noPos, "value is %1% while a set was expected", v, env, *e);
|
||||
try {
|
||||
e->eval(*this, env, v);
|
||||
if (v.type() != nAttrs)
|
||||
error("value is %1% while a set was expected", showType(v)).withFrame(env, *e).debugThrow<TypeError>();
|
||||
} catch (Error & e) {
|
||||
e.addTrace(positions[pos], errorCtx);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1363,7 +1221,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
|||
Hence we need __overrides.) */
|
||||
if (hasOverrides) {
|
||||
Value * vOverrides = (*v.attrs)[overrides->second.displ].value;
|
||||
state.forceAttrs(*vOverrides, [&]() { return vOverrides->determinePos(noPos); });
|
||||
state.forceAttrs(*vOverrides, [&]() { return vOverrides->determinePos(noPos); }, "while evaluating the `__overrides` attribute");
|
||||
Bindings * newBnds = state.allocBindings(v.attrs->capacity() + vOverrides->attrs->size());
|
||||
for (auto & i : *v.attrs)
|
||||
newBnds->push_back(i);
|
||||
|
@ -1391,11 +1249,11 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
|||
state.forceValue(nameVal, i.pos);
|
||||
if (nameVal.type() == nNull)
|
||||
continue;
|
||||
state.forceStringNoCtx(nameVal);
|
||||
state.forceStringNoCtx(nameVal, i.pos, "while evaluating the name of a dynamic attribute");
|
||||
auto nameSym = state.symbols.create(nameVal.string.s);
|
||||
Bindings::iterator j = v.attrs->find(nameSym);
|
||||
if (j != v.attrs->end())
|
||||
state.throwEvalError(i.pos, "dynamic attribute '%1%' already defined at %2%", nameSym, j->pos, env, *this);
|
||||
state.error("dynamic attribute '%1%' already defined at %2%", state.symbols[nameSym], state.positions[j->pos]).atPos(i.pos).withFrame(env, *this).debugThrow<EvalError>();
|
||||
|
||||
i.valueExpr->setName(nameSym);
|
||||
/* Keep sorted order so find can catch duplicates */
|
||||
|
@ -1492,15 +1350,14 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
|||
return;
|
||||
}
|
||||
} else {
|
||||
state.forceAttrs(*vAttrs, pos);
|
||||
state.forceAttrs(*vAttrs, pos, "while selecting an attribute");
|
||||
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) {
|
||||
std::set<std::string> allAttrNames;
|
||||
for (auto & attr : *vAttrs->attrs)
|
||||
allAttrNames.insert(state.symbols[attr.name]);
|
||||
state.throwEvalError(
|
||||
pos,
|
||||
Suggestions::bestMatches(allAttrNames, state.symbols[name]),
|
||||
"attribute '%1%' missing", state.symbols[name], env, *this);
|
||||
auto suggestions = Suggestions::bestMatches(allAttrNames, state.symbols[name]);
|
||||
state.error("attribute '%1%' missing", state.symbols[name])
|
||||
.atPos(pos).withSuggestions(suggestions).withFrame(env, *this).debugThrow<EvalError>();
|
||||
}
|
||||
}
|
||||
vAttrs = j->value;
|
||||
|
@ -1595,7 +1452,12 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
|||
if (!lambda.hasFormals())
|
||||
env2.values[displ++] = args[0];
|
||||
else {
|
||||
forceAttrs(*args[0], pos);
|
||||
try {
|
||||
forceAttrs(*args[0], lambda.pos, "while evaluating the value passed for the lambda argument");
|
||||
} catch (Error & e) {
|
||||
if (pos) e.addTrace(positions[pos], "from call site");
|
||||
throw;
|
||||
}
|
||||
|
||||
if (lambda.arg)
|
||||
env2.values[displ++] = args[0];
|
||||
|
@ -1607,8 +1469,15 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
|||
for (auto & i : lambda.formals->formals) {
|
||||
auto j = args[0]->attrs->get(i.name);
|
||||
if (!j) {
|
||||
if (!i.def) throwTypeError(pos, "%1% called without required argument '%2%'",
|
||||
lambda, i.name, *fun.lambda.env, lambda);
|
||||
if (!i.def) {
|
||||
error("function '%1%' called without required argument '%2%'",
|
||||
(lambda.name ? std::string(symbols[lambda.name]) : "anonymous lambda"),
|
||||
symbols[i.name])
|
||||
.atPos(lambda.pos)
|
||||
.withTrace(pos, "from call site")
|
||||
.withFrame(*fun.lambda.env, lambda)
|
||||
.debugThrow<TypeError>();
|
||||
}
|
||||
env2.values[displ++] = i.def->maybeThunk(*this, env2);
|
||||
} else {
|
||||
attrsUsed++;
|
||||
|
@ -1626,11 +1495,15 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
|||
std::set<std::string> formalNames;
|
||||
for (auto & formal : lambda.formals->formals)
|
||||
formalNames.insert(symbols[formal.name]);
|
||||
throwTypeError(
|
||||
pos,
|
||||
Suggestions::bestMatches(formalNames, symbols[i.name]),
|
||||
"%1% called with unexpected argument '%2%'",
|
||||
lambda, i.name, *fun.lambda.env, lambda);
|
||||
auto suggestions = Suggestions::bestMatches(formalNames, symbols[i.name]);
|
||||
error("function '%1%' called with unexpected argument '%2%'",
|
||||
(lambda.name ? std::string(symbols[lambda.name]) : "anonymous lambda"),
|
||||
symbols[i.name])
|
||||
.atPos(lambda.pos)
|
||||
.withTrace(pos, "from call site")
|
||||
.withSuggestions(suggestions)
|
||||
.withFrame(*fun.lambda.env, lambda)
|
||||
.debugThrow<TypeError>();
|
||||
}
|
||||
abort(); // can't happen
|
||||
}
|
||||
|
@ -1653,11 +1526,15 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
|||
lambda.body->eval(*this, env2, vCur);
|
||||
} catch (Error & e) {
|
||||
if (loggerSettings.showTrace.get()) {
|
||||
addErrorTrace(e, lambda.pos, "while calling %s",
|
||||
(lambda.name
|
||||
? concatStrings("'", symbols[lambda.name], "'")
|
||||
: "anonymous lambda"));
|
||||
addErrorTrace(e, pos, "while evaluating call site%s", "");
|
||||
addErrorTrace(
|
||||
e,
|
||||
lambda.pos,
|
||||
"while calling %s",
|
||||
lambda.name
|
||||
? concatStrings("'", symbols[lambda.name], "'")
|
||||
: "anonymous lambda",
|
||||
true);
|
||||
if (pos) addErrorTrace(e, pos, "from call site%s", "", true);
|
||||
}
|
||||
throw;
|
||||
}
|
||||
|
@ -1676,9 +1553,17 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
|||
return;
|
||||
} else {
|
||||
/* We have all the arguments, so call the primop. */
|
||||
auto name = vCur.primOp->name;
|
||||
|
||||
nrPrimOpCalls++;
|
||||
if (countCalls) primOpCalls[vCur.primOp->name]++;
|
||||
vCur.primOp->fun(*this, pos, args, vCur);
|
||||
if (countCalls) primOpCalls[name]++;
|
||||
|
||||
try {
|
||||
vCur.primOp->fun(*this, noPos, args, vCur);
|
||||
} catch (Error & e) {
|
||||
addErrorTrace(e, pos, "while calling the '%1%' builtin", name);
|
||||
throw;
|
||||
}
|
||||
|
||||
nrArgs -= argsLeft;
|
||||
args += argsLeft;
|
||||
|
@ -1713,9 +1598,20 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
|||
for (size_t i = 0; i < argsLeft; ++i)
|
||||
vArgs[argsDone + i] = args[i];
|
||||
|
||||
auto name = primOp->primOp->name;
|
||||
nrPrimOpCalls++;
|
||||
if (countCalls) primOpCalls[primOp->primOp->name]++;
|
||||
primOp->primOp->fun(*this, pos, vArgs, vCur);
|
||||
if (countCalls) primOpCalls[name]++;
|
||||
|
||||
try {
|
||||
// TODO:
|
||||
// 1. Unify this and above code. Heavily redundant.
|
||||
// 2. Create a fake env (arg1, arg2, etc.) and a fake expr (arg1: arg2: etc: builtins.name arg1 arg2 etc)
|
||||
// so the debugger allows to inspect the wrong parameters passed to the builtin.
|
||||
primOp->primOp->fun(*this, noPos, vArgs, vCur);
|
||||
} catch (Error & e) {
|
||||
addErrorTrace(e, pos, "while calling the '%1%' builtin", name);
|
||||
throw;
|
||||
}
|
||||
|
||||
nrArgs -= argsLeft;
|
||||
args += argsLeft;
|
||||
|
@ -1728,14 +1624,18 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
|||
heap-allocate a copy and use that instead. */
|
||||
Value * args2[] = {allocValue(), args[0]};
|
||||
*args2[0] = vCur;
|
||||
/* !!! Should we use the attr pos here? */
|
||||
callFunction(*functor->value, 2, args2, vCur, pos);
|
||||
try {
|
||||
callFunction(*functor->value, 2, args2, vCur, functor->pos);
|
||||
} catch (Error & e) {
|
||||
e.addTrace(positions[pos], "while calling a functor (an attribute set with a '__functor' attribute)");
|
||||
throw;
|
||||
}
|
||||
nrArgs--;
|
||||
args++;
|
||||
}
|
||||
|
||||
else
|
||||
throwTypeError(pos, "attempt to call something which is not a function but %1%", vCur);
|
||||
error("attempt to call something which is not a function but %1%", showType(vCur)).atPos(pos).debugThrow<TypeError>();
|
||||
}
|
||||
|
||||
vRes = vCur;
|
||||
|
@ -1799,13 +1699,12 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
|
|||
if (j != args.end()) {
|
||||
attrs.insert(*j);
|
||||
} else if (!i.def) {
|
||||
throwMissingArgumentError(i.pos, R"(cannot evaluate a function that has an argument without a value ('%1%')
|
||||
|
||||
error(R"(cannot evaluate a function that has an argument without a value ('%1%')
|
||||
Nix attempted to evaluate a function as a top level expression; in
|
||||
this case it must have its arguments supplied either by default
|
||||
values, or passed explicitly with '--arg' or '--argstr'. See
|
||||
https://nixos.org/manual/nix/stable/language/constructs.html#functions.)", symbols[i.name],
|
||||
*fun.lambda.env, *fun.lambda.fun);
|
||||
https://nixos.org/manual/nix/stable/language/constructs.html#functions.)", symbols[i.name])
|
||||
.atPos(i.pos).withFrame(*fun.lambda.env, *fun.lambda.fun).debugThrow<MissingArgumentError>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1828,16 +1727,17 @@ void ExprWith::eval(EvalState & state, Env & env, Value & v)
|
|||
|
||||
void ExprIf::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
(state.evalBool(env, cond, pos) ? then : else_)->eval(state, env, v);
|
||||
// We cheat in the parser, and pass the position of the condition as the position of the if itself.
|
||||
(state.evalBool(env, cond, pos, "while evaluating a branch condition") ? then : else_)->eval(state, env, v);
|
||||
}
|
||||
|
||||
|
||||
void ExprAssert::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
if (!state.evalBool(env, cond, pos)) {
|
||||
if (!state.evalBool(env, cond, pos, "in the condition of the assert statement")) {
|
||||
std::ostringstream out;
|
||||
cond->show(state.symbols, out);
|
||||
state.throwAssertionError(pos, "assertion '%1%' failed", out.str(), env, *this);
|
||||
state.error("assertion '%1%' failed", out.str()).atPos(pos).withFrame(env, *this).debugThrow<AssertionError>();
|
||||
}
|
||||
body->eval(state, env, v);
|
||||
}
|
||||
|
@ -1845,7 +1745,7 @@ void ExprAssert::eval(EvalState & state, Env & env, Value & v)
|
|||
|
||||
void ExprOpNot::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
v.mkBool(!state.evalBool(env, e));
|
||||
v.mkBool(!state.evalBool(env, e, noPos, "in the argument of the not operator")); // XXX: FIXME: !
|
||||
}
|
||||
|
||||
|
||||
|
@ -1853,7 +1753,7 @@ void ExprOpEq::eval(EvalState & state, Env & env, Value & v)
|
|||
{
|
||||
Value v1; e1->eval(state, env, v1);
|
||||
Value v2; e2->eval(state, env, v2);
|
||||
v.mkBool(state.eqValues(v1, v2));
|
||||
v.mkBool(state.eqValues(v1, v2, pos, "while testing two values for equality"));
|
||||
}
|
||||
|
||||
|
||||
|
@ -1861,33 +1761,33 @@ void ExprOpNEq::eval(EvalState & state, Env & env, Value & v)
|
|||
{
|
||||
Value v1; e1->eval(state, env, v1);
|
||||
Value v2; e2->eval(state, env, v2);
|
||||
v.mkBool(!state.eqValues(v1, v2));
|
||||
v.mkBool(!state.eqValues(v1, v2, pos, "while testing two values for inequality"));
|
||||
}
|
||||
|
||||
|
||||
void ExprOpAnd::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
v.mkBool(state.evalBool(env, e1, pos) && state.evalBool(env, e2, pos));
|
||||
v.mkBool(state.evalBool(env, e1, pos, "in the left operand of the AND (&&) operator") && state.evalBool(env, e2, pos, "in the right operand of the AND (&&) operator"));
|
||||
}
|
||||
|
||||
|
||||
void ExprOpOr::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
v.mkBool(state.evalBool(env, e1, pos) || state.evalBool(env, e2, pos));
|
||||
v.mkBool(state.evalBool(env, e1, pos, "in the left operand of the OR (||) operator") || state.evalBool(env, e2, pos, "in the right operand of the OR (||) operator"));
|
||||
}
|
||||
|
||||
|
||||
void ExprOpImpl::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
v.mkBool(!state.evalBool(env, e1, pos) || state.evalBool(env, e2, pos));
|
||||
v.mkBool(!state.evalBool(env, e1, pos, "in the left operand of the IMPL (->) operator") || state.evalBool(env, e2, pos, "in the right operand of the IMPL (->) operator"));
|
||||
}
|
||||
|
||||
|
||||
void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
Value v1, v2;
|
||||
state.evalAttrs(env, e1, v1);
|
||||
state.evalAttrs(env, e2, v2);
|
||||
state.evalAttrs(env, e1, v1, pos, "in the left operand of the update (//) operator");
|
||||
state.evalAttrs(env, e2, v2, pos, "in the right operand of the update (//) operator");
|
||||
|
||||
state.nrOpUpdates++;
|
||||
|
||||
|
@ -1926,18 +1826,18 @@ void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v)
|
|||
Value v1; e1->eval(state, env, v1);
|
||||
Value v2; e2->eval(state, env, v2);
|
||||
Value * lists[2] = { &v1, &v2 };
|
||||
state.concatLists(v, 2, lists, pos);
|
||||
state.concatLists(v, 2, lists, pos, "while evaluating one of the elements to concatenate");
|
||||
}
|
||||
|
||||
|
||||
void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos)
|
||||
void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos, std::string_view errorCtx)
|
||||
{
|
||||
nrListConcats++;
|
||||
|
||||
Value * nonEmpty = 0;
|
||||
size_t len = 0;
|
||||
for (size_t n = 0; n < nrLists; ++n) {
|
||||
forceList(*lists[n], pos);
|
||||
forceList(*lists[n], pos, errorCtx);
|
||||
auto l = lists[n]->listSize();
|
||||
len += l;
|
||||
if (l) nonEmpty = lists[n];
|
||||
|
@ -2014,20 +1914,20 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
|||
nf = n;
|
||||
nf += vTmp.fpoint;
|
||||
} else
|
||||
state.throwEvalError(i_pos, "cannot add %1% to an integer", showType(vTmp), env, *this);
|
||||
state.error("cannot add %1% to an integer", showType(vTmp)).atPos(i_pos).withFrame(env, *this).debugThrow<EvalError>();
|
||||
} else if (firstType == nFloat) {
|
||||
if (vTmp.type() == nInt) {
|
||||
nf += vTmp.integer;
|
||||
} else if (vTmp.type() == nFloat) {
|
||||
nf += vTmp.fpoint;
|
||||
} else
|
||||
state.throwEvalError(i_pos, "cannot add %1% to a float", showType(vTmp), env, *this);
|
||||
state.error("cannot add %1% to a float", showType(vTmp)).atPos(i_pos).withFrame(env, *this).debugThrow<EvalError>();
|
||||
} else {
|
||||
if (s.empty()) s.reserve(es->size());
|
||||
/* skip canonization of first path, which would only be not
|
||||
canonized in the first place if it's coming from a ./${foo} type
|
||||
path */
|
||||
auto part = state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first);
|
||||
auto part = state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first, "while evaluating a path segment");
|
||||
sSize += part->size();
|
||||
s.emplace_back(std::move(part));
|
||||
}
|
||||
|
@ -2041,7 +1941,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
|||
v.mkFloat(nf);
|
||||
else if (firstType == nPath) {
|
||||
if (!context.empty())
|
||||
state.throwEvalError(pos, "a string that refers to a store path cannot be appended to a path", env, *this);
|
||||
state.error("a string that refers to a store path cannot be appended to a path").atPos(pos).withFrame(env, *this).debugThrow<EvalError>();
|
||||
v.mkPath(canonPath(str()));
|
||||
} else
|
||||
v.mkStringMove(c_str(), context);
|
||||
|
@ -2091,33 +1991,47 @@ void EvalState::forceValueDeep(Value & v)
|
|||
}
|
||||
|
||||
|
||||
NixInt EvalState::forceInt(Value & v, const PosIdx pos)
|
||||
NixInt EvalState::forceInt(Value & v, const PosIdx pos, std::string_view errorCtx)
|
||||
{
|
||||
forceValue(v, pos);
|
||||
if (v.type() != nInt)
|
||||
throwTypeError(pos, "value is %1% while an integer was expected", v);
|
||||
|
||||
return v.integer;
|
||||
}
|
||||
|
||||
|
||||
NixFloat EvalState::forceFloat(Value & v, const PosIdx pos)
|
||||
{
|
||||
forceValue(v, pos);
|
||||
if (v.type() == nInt)
|
||||
try {
|
||||
forceValue(v, pos);
|
||||
if (v.type() != nInt)
|
||||
error("value is %1% while an integer was expected", showType(v)).debugThrow<TypeError>();
|
||||
return v.integer;
|
||||
else if (v.type() != nFloat)
|
||||
throwTypeError(pos, "value is %1% while a float was expected", v);
|
||||
return v.fpoint;
|
||||
} catch (Error & e) {
|
||||
e.addTrace(positions[pos], errorCtx);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool EvalState::forceBool(Value & v, const PosIdx pos)
|
||||
NixFloat EvalState::forceFloat(Value & v, const PosIdx pos, std::string_view errorCtx)
|
||||
{
|
||||
forceValue(v, pos);
|
||||
if (v.type() != nBool)
|
||||
throwTypeError(pos, "value is %1% while a Boolean was expected", v);
|
||||
return v.boolean;
|
||||
try {
|
||||
forceValue(v, pos);
|
||||
if (v.type() == nInt)
|
||||
return v.integer;
|
||||
else if (v.type() != nFloat)
|
||||
error("value is %1% while a float was expected", showType(v)).debugThrow<TypeError>();
|
||||
return v.fpoint;
|
||||
} catch (Error & e) {
|
||||
e.addTrace(positions[pos], errorCtx);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool EvalState::forceBool(Value & v, const PosIdx pos, std::string_view errorCtx)
|
||||
{
|
||||
try {
|
||||
forceValue(v, pos);
|
||||
if (v.type() != nBool)
|
||||
error("value is %1% while a Boolean was expected", showType(v)).debugThrow<TypeError>();
|
||||
return v.boolean;
|
||||
} catch (Error & e) {
|
||||
e.addTrace(positions[pos], errorCtx);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -2127,21 +2041,30 @@ bool EvalState::isFunctor(Value & fun)
|
|||
}
|
||||
|
||||
|
||||
void EvalState::forceFunction(Value & v, const PosIdx pos)
|
||||
void EvalState::forceFunction(Value & v, const PosIdx pos, std::string_view errorCtx)
|
||||
{
|
||||
forceValue(v, pos);
|
||||
if (v.type() != nFunction && !isFunctor(v))
|
||||
throwTypeError(pos, "value is %1% while a function was expected", v);
|
||||
try {
|
||||
forceValue(v, pos);
|
||||
if (v.type() != nFunction && !isFunctor(v))
|
||||
error("value is %1% while a function was expected", showType(v)).debugThrow<TypeError>();
|
||||
} catch (Error & e) {
|
||||
e.addTrace(positions[pos], errorCtx);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::string_view EvalState::forceString(Value & v, const PosIdx pos)
|
||||
std::string_view EvalState::forceString(Value & v, const PosIdx pos, std::string_view errorCtx)
|
||||
{
|
||||
forceValue(v, pos);
|
||||
if (v.type() != nString) {
|
||||
throwTypeError(pos, "value is %1% while a string was expected", v);
|
||||
try {
|
||||
forceValue(v, pos);
|
||||
if (v.type() != nString)
|
||||
error("value is %1% while a string was expected", showType(v)).debugThrow<TypeError>();
|
||||
return v.string.s;
|
||||
} catch (Error & e) {
|
||||
e.addTrace(positions[pos], errorCtx);
|
||||
throw;
|
||||
}
|
||||
return v.string.s;
|
||||
}
|
||||
|
||||
|
||||
|
@ -2164,24 +2087,19 @@ NixStringContext Value::getContext(const Store & store)
|
|||
}
|
||||
|
||||
|
||||
std::string_view EvalState::forceString(Value & v, PathSet & context, const PosIdx pos)
|
||||
std::string_view EvalState::forceString(Value & v, PathSet & context, const PosIdx pos, std::string_view errorCtx)
|
||||
{
|
||||
auto s = forceString(v, pos);
|
||||
auto s = forceString(v, pos, errorCtx);
|
||||
copyContext(v, context);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos)
|
||||
std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos, std::string_view errorCtx)
|
||||
{
|
||||
auto s = forceString(v, pos);
|
||||
auto s = forceString(v, pos, errorCtx);
|
||||
if (v.string.context) {
|
||||
if (pos)
|
||||
throwEvalError(pos, "the string '%1%' is not allowed to refer to a store path (such as '%2%')",
|
||||
v.string.s, v.string.context[0]);
|
||||
else
|
||||
throwEvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%')",
|
||||
v.string.s, v.string.context[0]);
|
||||
error("the string '%1%' is not allowed to refer to a store path (such as '%2%')", v.string.s, v.string.context[0]).withTrace(pos, errorCtx).debugThrow<EvalError>();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
@ -2205,14 +2123,15 @@ std::optional<std::string> EvalState::tryAttrsToString(const PosIdx pos, Value &
|
|||
if (i != v.attrs->end()) {
|
||||
Value v1;
|
||||
callFunction(*i->value, v, v1, pos);
|
||||
return coerceToString(pos, v1, context, coerceMore, copyToStore).toOwned();
|
||||
return coerceToString(pos, v1, context, coerceMore, copyToStore,
|
||||
"while evaluating the result of the `toString` attribute").toOwned();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BackedStringView EvalState::coerceToString(const PosIdx pos, Value & v, PathSet & context,
|
||||
bool coerceMore, bool copyToStore, bool canonicalizePath)
|
||||
bool coerceMore, bool copyToStore, bool canonicalizePath, std::string_view errorCtx)
|
||||
{
|
||||
forceValue(v, pos);
|
||||
|
||||
|
@ -2236,12 +2155,12 @@ BackedStringView EvalState::coerceToString(const PosIdx pos, Value & v, PathSet
|
|||
return std::move(*maybeString);
|
||||
auto i = v.attrs->find(sOutPath);
|
||||
if (i == v.attrs->end())
|
||||
throwTypeError(pos, "cannot coerce a set to a string");
|
||||
return coerceToString(pos, *i->value, context, coerceMore, copyToStore);
|
||||
error("cannot coerce a set to a string", showType(v)).withTrace(pos, errorCtx).debugThrow<TypeError>();
|
||||
return coerceToString(pos, *i->value, context, coerceMore, copyToStore, canonicalizePath, errorCtx);
|
||||
}
|
||||
|
||||
if (v.type() == nExternal)
|
||||
return v.external->coerceToString(positions[pos], context, coerceMore, copyToStore);
|
||||
return v.external->coerceToString(positions[pos], context, coerceMore, copyToStore, errorCtx);
|
||||
|
||||
if (coerceMore) {
|
||||
/* Note that `false' is represented as an empty string for
|
||||
|
@ -2255,7 +2174,13 @@ BackedStringView EvalState::coerceToString(const PosIdx pos, Value & v, PathSet
|
|||
if (v.isList()) {
|
||||
std::string result;
|
||||
for (auto [n, v2] : enumerate(v.listItems())) {
|
||||
result += *coerceToString(pos, *v2, context, coerceMore, copyToStore);
|
||||
try {
|
||||
result += *coerceToString(noPos, *v2, context, coerceMore, copyToStore, canonicalizePath,
|
||||
"while evaluating one element of the list");
|
||||
} catch (Error & e) {
|
||||
e.addTrace(positions[pos], errorCtx);
|
||||
throw;
|
||||
}
|
||||
if (n < v.listSize() - 1
|
||||
/* !!! not quite correct */
|
||||
&& (!v2->isList() || v2->listSize() != 0))
|
||||
|
@ -2265,14 +2190,14 @@ BackedStringView EvalState::coerceToString(const PosIdx pos, Value & v, PathSet
|
|||
}
|
||||
}
|
||||
|
||||
throwTypeError(pos, "cannot coerce %1% to a string", v);
|
||||
error("cannot coerce %1% to a string", showType(v)).withTrace(pos, errorCtx).debugThrow<TypeError>();
|
||||
}
|
||||
|
||||
|
||||
StorePath EvalState::copyPathToStore(PathSet & context, const Path & path)
|
||||
{
|
||||
if (nix::isDerivation(path))
|
||||
throwEvalError("file names are not allowed to end in '%1%'", drvExtension);
|
||||
error("file names are not allowed to end in '%1%'", drvExtension).debugThrow<EvalError>();
|
||||
|
||||
auto dstPath = [&]() -> StorePath
|
||||
{
|
||||
|
@ -2293,28 +2218,25 @@ StorePath EvalState::copyPathToStore(PathSet & context, const Path & path)
|
|||
}
|
||||
|
||||
|
||||
Path EvalState::coerceToPath(const PosIdx pos, Value & v, PathSet & context)
|
||||
Path EvalState::coerceToPath(const PosIdx pos, Value & v, PathSet & context, std::string_view errorCtx)
|
||||
{
|
||||
auto path = coerceToString(pos, v, context, false, false).toOwned();
|
||||
auto path = coerceToString(pos, v, context, false, false, true, errorCtx).toOwned();
|
||||
if (path == "" || path[0] != '/')
|
||||
throwEvalError(pos, "string '%1%' doesn't represent an absolute path", path);
|
||||
error("string '%1%' doesn't represent an absolute path", path).withTrace(pos, errorCtx).debugThrow<EvalError>();
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
StorePath EvalState::coerceToStorePath(const PosIdx pos, Value & v, PathSet & context)
|
||||
StorePath EvalState::coerceToStorePath(const PosIdx pos, Value & v, PathSet & context, std::string_view errorCtx)
|
||||
{
|
||||
auto path = coerceToString(pos, v, context, false, false).toOwned();
|
||||
auto path = coerceToString(pos, v, context, false, false, true, errorCtx).toOwned();
|
||||
if (auto storePath = store->maybeParseStorePath(path))
|
||||
return *storePath;
|
||||
throw EvalError({
|
||||
.msg = hintfmt("path '%1%' is not in the Nix store", path),
|
||||
.errPos = positions[pos]
|
||||
});
|
||||
error("path '%1%' is not in the Nix store", path).withTrace(pos, errorCtx).debugThrow<EvalError>();
|
||||
}
|
||||
|
||||
|
||||
bool EvalState::eqValues(Value & v1, Value & v2)
|
||||
bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_view errorCtx)
|
||||
{
|
||||
forceValue(v1, noPos);
|
||||
forceValue(v2, noPos);
|
||||
|
@ -2334,7 +2256,6 @@ bool EvalState::eqValues(Value & v1, Value & v2)
|
|||
if (v1.type() != v2.type()) return false;
|
||||
|
||||
switch (v1.type()) {
|
||||
|
||||
case nInt:
|
||||
return v1.integer == v2.integer;
|
||||
|
||||
|
@ -2353,7 +2274,7 @@ bool EvalState::eqValues(Value & v1, Value & v2)
|
|||
case nList:
|
||||
if (v1.listSize() != v2.listSize()) return false;
|
||||
for (size_t n = 0; n < v1.listSize(); ++n)
|
||||
if (!eqValues(*v1.listElems()[n], *v2.listElems()[n])) return false;
|
||||
if (!eqValues(*v1.listElems()[n], *v2.listElems()[n], pos, errorCtx)) return false;
|
||||
return true;
|
||||
|
||||
case nAttrs: {
|
||||
|
@ -2363,7 +2284,7 @@ bool EvalState::eqValues(Value & v1, Value & v2)
|
|||
Bindings::iterator i = v1.attrs->find(sOutPath);
|
||||
Bindings::iterator j = v2.attrs->find(sOutPath);
|
||||
if (i != v1.attrs->end() && j != v2.attrs->end())
|
||||
return eqValues(*i->value, *j->value);
|
||||
return eqValues(*i->value, *j->value, pos, errorCtx);
|
||||
}
|
||||
|
||||
if (v1.attrs->size() != v2.attrs->size()) return false;
|
||||
|
@ -2371,7 +2292,7 @@ bool EvalState::eqValues(Value & v1, Value & v2)
|
|||
/* Otherwise, compare the attributes one by one. */
|
||||
Bindings::iterator i, j;
|
||||
for (i = v1.attrs->begin(), j = v2.attrs->begin(); i != v1.attrs->end(); ++i, ++j)
|
||||
if (i->name != j->name || !eqValues(*i->value, *j->value))
|
||||
if (i->name != j->name || !eqValues(*i->value, *j->value, pos, errorCtx))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -2388,9 +2309,7 @@ bool EvalState::eqValues(Value & v1, Value & v2)
|
|||
return v1.fpoint == v2.fpoint;
|
||||
|
||||
default:
|
||||
throwEvalError("cannot compare %1% with %2%",
|
||||
showType(v1),
|
||||
showType(v2));
|
||||
error("cannot compare %1% with %2%", showType(v1), showType(v2)).withTrace(pos, errorCtx).debugThrow<EvalError>();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2514,12 +2433,13 @@ void EvalState::printStats()
|
|||
}
|
||||
|
||||
|
||||
std::string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const
|
||||
std::string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore, std::string_view errorCtx) const
|
||||
{
|
||||
throw TypeError({
|
||||
.msg = hintfmt("cannot coerce %1% to a string", showType()),
|
||||
.errPos = pos
|
||||
auto e = TypeError({
|
||||
.msg = hintfmt("cannot coerce %1% to a string", showType())
|
||||
});
|
||||
e.addTrace(pos, errorCtx);
|
||||
throw e;
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue