remove more debug code

This commit is contained in:
Ben Burdette 2021-10-22 14:27:04 -06:00
parent cbc2f0fe31
commit e54f17eb46
7 changed files with 9 additions and 128 deletions

View file

@ -67,7 +67,6 @@ extern std::function<void(const Error & error, const Env & env, const Expr & exp
ref<EvalState> EvalCommand::getEvalState() ref<EvalState> EvalCommand::getEvalState()
{ {
std::cout << "EvalCommand::getEvalState()" << startReplOnEvalErrors << std::endl;
if (!evalState) { if (!evalState) {
evalState = std::make_shared<EvalState>(searchPath, getStore()); evalState = std::make_shared<EvalState>(searchPath, getStore());
if (startReplOnEvalErrors) if (startReplOnEvalErrors)

View file

@ -249,20 +249,6 @@ void completeFlakeRefWithFragment(
completeFlakeRef(evalState->store, prefix); completeFlakeRef(evalState->store, prefix);
} }
/*
ref<EvalState> EvalCommand::getEvalState()
{
if (!evalState)
evalState = std::make_shared<EvalState>(searchPath, getStore());
return ref<EvalState>(evalState);
}
EvalCommand::~EvalCommand()
{
if (evalState)
evalState->printStats();
}
*/
void completeFlakeRef(ref<Store> store, std::string_view prefix) void completeFlakeRef(ref<Store> store, std::string_view prefix)
{ {
@ -618,11 +604,7 @@ std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
if (file) if (file)
state->evalFile(lookupFileArg(*state, *file), *vFile); state->evalFile(lookupFileArg(*state, *file), *vFile);
else { else {
// std::cout << "pre parseExprFromString" << std::endl;
auto e = state->parseExprFromString(*expr, absPath(".")); auto e = state->parseExprFromString(*expr, absPath("."));
// std::cout << "pre eval" << std::endl;
state->eval(e, *vFile); state->eval(e, *vFile);
} }

View file

@ -54,7 +54,6 @@ struct NixRepl
const static int envSize = 32768; const static int envSize = 32768;
std::shared_ptr<StaticEnv> staticEnv; std::shared_ptr<StaticEnv> staticEnv;
// StaticEnv staticEnv;
Env * env; Env * env;
int displ; int displ;
StringSet varNames; StringSet varNames;

View file

@ -35,7 +35,6 @@
namespace nix { namespace nix {
// std::function<void(const Error & error, const std::map<std::string, Value *> & env)> debuggerHook;
std::function<void(const Error & error, const Env & env, const Expr & expr)> debuggerHook; std::function<void(const Error & error, const Env & env, const Expr & expr)> debuggerHook;
static char * dupString(const char * s) static char * dupString(const char * s)
@ -407,7 +406,8 @@ EvalState::EvalState(const Strings & _searchPath, ref<Store> store)
assert(gcInitialised); assert(gcInitialised);
static_assert(sizeof(Env) <= 16 + sizeof(std::unique_ptr<void*>), "environment must be <= 16 bytes"); // static_assert(sizeof(Env) <= 16 + sizeof(std::unique_ptr<void*>), "environment must be <= 16 bytes");
static_assert(sizeof(Env) <= 16, "environment must be <= 16 bytes");
/* Initialise the Nix expression search path. */ /* Initialise the Nix expression search path. */
if (!evalSettings.pureEval) { if (!evalSettings.pureEval) {
@ -646,13 +646,13 @@ void printStaticEnvBindings(const Expr &expr)
void mapStaticEnvBindings(const StaticEnv &se, const Env &env, valmap & vm) void mapStaticEnvBindings(const StaticEnv &se, const Env &env, valmap & vm)
{ {
// add bindings for the next level up first. // add bindings for the next level up first, so that the bindings for this level
// override the higher levels.
if (env.up && se.up) { if (env.up && se.up) {
mapStaticEnvBindings( *se.up, *env.up,vm); mapStaticEnvBindings( *se.up, *env.up,vm);
} }
// iterate through staticenv bindings. // iterate through staticenv bindings and add them.
auto map = valmap(); auto map = valmap();
for (auto iter = se.vars.begin(); iter != se.vars.end(); ++iter) for (auto iter = se.vars.begin(); iter != se.vars.end(); ++iter)
{ {
@ -679,7 +679,6 @@ valmap * mapStaticEnvBindings(const StaticEnv &se, const Env &env)
LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, Env & env, Expr *expr)) LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, Env & env, Expr *expr))
{ {
// auto delenv = std::unique_ptr<valmap>(env);
auto error = EvalError(s, s2); auto error = EvalError(s, s2);
if (debuggerHook && expr) if (debuggerHook && expr)
@ -689,7 +688,6 @@ LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, Env
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const string & s2, Env & env, Expr *expr)) LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const string & s2, Env & env, Expr *expr))
{ {
// auto delenv = std::unique_ptr<valmap>(env);
auto error = EvalError({ auto error = EvalError({
.msg = hintfmt(s, s2), .msg = hintfmt(s, s2),
.errPos = pos .errPos = pos
@ -703,7 +701,6 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const
LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, const string & s3, Env & env, Expr *expr)) LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, const string & s3, Env & env, Expr *expr))
{ {
// auto delenv = std::unique_ptr<valmap>(env);
auto error = EvalError(s, s2, s3); auto error = EvalError(s, s2, s3);
if (debuggerHook && expr) if (debuggerHook && expr)
@ -714,7 +711,6 @@ LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, con
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const string & s2, const string & s3, Env & env, Expr *expr)) LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const string & s2, const string & s3, Env & env, Expr *expr))
{ {
// auto delenv = std::unique_ptr<valmap>(env);
auto error = EvalError({ auto error = EvalError({
.msg = hintfmt(s, s2, s3), .msg = hintfmt(s, s2, s3),
.errPos = pos .errPos = pos
@ -729,7 +725,6 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const
LocalNoInlineNoReturn(void throwEvalError(const Pos & p1, const char * s, const Symbol & sym, const Pos & p2, Env & env, Expr *expr)) LocalNoInlineNoReturn(void throwEvalError(const Pos & p1, const char * s, const Symbol & sym, const Pos & p2, Env & env, Expr *expr))
{ {
// p1 is where the error occurred; p2 is a position mentioned in the message. // p1 is where the error occurred; p2 is a position mentioned in the message.
// auto delenv = std::unique_ptr<valmap>(env);
auto error = EvalError({ auto error = EvalError({
.msg = hintfmt(s, sym, p2), .msg = hintfmt(s, sym, p2),
.errPos = p1 .errPos = p1
@ -743,7 +738,6 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & p1, const char * s, const
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, Env & env, Expr *expr)) LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, Env & env, Expr *expr))
{ {
// auto delenv = std::unique_ptr<valmap>(env);
auto error = TypeError({ auto error = TypeError({
.msg = hintfmt(s), .msg = hintfmt(s),
.errPos = pos .errPos = pos
@ -757,7 +751,6 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, Env &
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const Value & v, Env & env, Expr *expr)) LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const Value & v, Env & env, Expr *expr))
{ {
// auto delenv = std::unique_ptr<valmap>(env);
auto error = TypeError({ auto error = TypeError({
.msg = hintfmt(s, v), .msg = hintfmt(s, v),
.errPos = pos .errPos = pos
@ -771,7 +764,6 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const string &s2, Env & env, Expr *expr)) LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const string &s2, Env & env, Expr *expr))
{ {
// auto delenv = std::unique_ptr<valmap>(env);
auto error = TypeError({ auto error = TypeError({
.msg = hintfmt(s, s2), .msg = hintfmt(s, s2),
.errPos = pos .errPos = pos
@ -785,7 +777,6 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const ExprLambda & fun, const Symbol & s2, Env & env, Expr *expr)) LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const ExprLambda & fun, const Symbol & s2, Env & env, Expr *expr))
{ {
// auto delenv = std::unique_ptr<valmap>(env);
auto error = TypeError({ auto error = TypeError({
.msg = hintfmt(s, fun.showNamePos(), s2), .msg = hintfmt(s, fun.showNamePos(), s2),
.errPos = pos .errPos = pos
@ -799,7 +790,6 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const
LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s, const string & s1, Env & env, Expr *expr)) LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s, const string & s1, Env & env, Expr *expr))
{ {
// auto delenv = std::unique_ptr<valmap>(env);
auto error = AssertionError({ auto error = AssertionError({
.msg = hintfmt(s, s1), .msg = hintfmt(s, s1),
.errPos = pos .errPos = pos
@ -813,16 +803,11 @@ LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s,
LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char * s, const string & s1, Env & env, Expr *expr)) LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char * s, const string & s1, Env & env, Expr *expr))
{ {
// std::cout << "throwUndefinedVarError" << std::endl;
// auto delenv = std::unique_ptr<valmap>(env);
auto error = UndefinedVarError({ auto error = UndefinedVarError({
.msg = hintfmt(s, s1), .msg = hintfmt(s, s1),
.errPos = pos .errPos = pos
}); });
// std::cout << "pre debuggerHook" << std::endl;
if (debuggerHook && expr) if (debuggerHook && expr)
debuggerHook(error, env, *expr); debuggerHook(error, env, *expr);
@ -831,7 +816,6 @@ LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char *
LocalNoInlineNoReturn(void throwMissingArgumentError(const Pos & pos, const char * s, const string & s1, Env & env, Expr *expr)) LocalNoInlineNoReturn(void throwMissingArgumentError(const Pos & pos, const char * s, const string & s1, Env & env, Expr *expr))
{ {
// auto delenv = std::unique_ptr<valmap>(env);
auto error = MissingArgumentError({ auto error = MissingArgumentError({
.msg = hintfmt(s, s1), .msg = hintfmt(s, s1),
.errPos = pos .errPos = pos
@ -881,8 +865,6 @@ void mkPath(Value & v, const char * s)
inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval) inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
{ {
// std::cout << " EvalState::lookupVar" << var << std::endl;
for (size_t l = var.level; l; --l, env = env->up) ; for (size_t l = var.level; l; --l, env = env->up) ;
if (!var.fromWith) return env->values[var.displ]; if (!var.fromWith) return env->values[var.displ];
@ -901,7 +883,6 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
return j->value; return j->value;
} }
if (!env->prevWith) { if (!env->prevWith) {
// std::cout << "pre throwUndefinedVarError" << std::endl;
throwUndefinedVarError(var.pos, "undefined variable '%1%'", var.name, *env, 0); throwUndefinedVarError(var.pos, "undefined variable '%1%'", var.name, *env, 0);
} }
for (size_t l = env->prevWith; l; --l, env = env->up) ; for (size_t l = env->prevWith; l; --l, env = env->up) ;
@ -930,25 +911,9 @@ Env & EvalState::allocEnv(size_t size)
nrEnvs++; nrEnvs++;
nrValuesInEnvs += size; nrValuesInEnvs += size;
// if (debuggerHook)
// {
// Env * env = (Env *) allocBytes(sizeof(DebugEnv) + size * sizeof(Value *));
// // Env * env = new DebugEnv;
// env->type = Env::Plain;
// /* We assume that env->values has been cleared by the allocator; maybeThunk() and lookupVar fromWith expect this. */
// return *env;
// } else {
Env * env = (Env *) allocBytes(sizeof(Env) + size * sizeof(Value *)); Env * env = (Env *) allocBytes(sizeof(Env) + size * sizeof(Value *));
env->type = Env::Plain; env->type = Env::Plain;
// env->size = size;
/* We assume that env->values has been cleared by the allocator; maybeThunk() and lookupVar fromWith expect this. */
return *env; return *env;
// }
} }
Env & fakeEnv(size_t size) Env & fakeEnv(size_t size)
@ -1237,7 +1202,6 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
if (j != v.attrs->end()) if (j != v.attrs->end())
throwEvalError(i.pos, "dynamic attribute '%1%' already defined at %2%", nameSym, *j->pos, throwEvalError(i.pos, "dynamic attribute '%1%' already defined at %2%", nameSym, *j->pos,
env, this); env, this);
// map1("value", &v)); // TODO dynamicAttrs to env?
i.valueExpr->setName(nameSym); i.valueExpr->setName(nameSym);
/* Keep sorted order so find can catch duplicates */ /* Keep sorted order so find can catch duplicates */
@ -1594,8 +1558,6 @@ values, or passed explicitly with '--arg' or '--argstr'. See
https://nixos.org/manual/nix/stable/#ss-functions.)", https://nixos.org/manual/nix/stable/#ss-functions.)",
i.name, i.name,
*fun.lambda.env, fun.lambda.fun); *fun.lambda.env, fun.lambda.fun);
// mapBindings(args));
// map1("fun", &fun)); // todo add bindings + fun
} }
} }
} }
@ -1608,25 +1570,12 @@ https://nixos.org/manual/nix/stable/#ss-functions.)",
void ExprWith::eval(EvalState & state, Env & env, Value & v) void ExprWith::eval(EvalState & state, Env & env, Value & v)
{ {
// std::cout << "ExprWith::eval" << std::endl;
Env & env2(state.allocEnv(1)); Env & env2(state.allocEnv(1));
env2.up = &env; env2.up = &env;
env2.prevWith = prevWith; env2.prevWith = prevWith;
env2.type = Env::HasWithExpr; env2.type = Env::HasWithExpr;
env2.values[0] = (Value *) attrs; // ok DAG nasty. just smoosh this in. env2.values[0] = (Value *) attrs; // ok DAG nasty. just smoosh this in.
// presumably evaluate later, lazily. // presumably evaluate later, lazily.
// std::cout << "ExprWith::eval2" << std::endl;
// can't load the valuemap until they've been evaled, which is not yet.
// if (debuggerHook) {
// std::cout << "ExprWith::eval3.0" << std::endl;
// std::cout << "ExprWith attrs" << *attrs << std::endl;
// state.forceAttrs(*(Value*) attrs);
// std::cout << "ExprWith::eval3.5" << std::endl;
// env2.valuemap.reset(mapBindings(*env2.values[0]->attrs));
// std::cout << "ExprWith::eval4" << std::endl;
// }
body->eval(state, env2, v); body->eval(state, env2, v);
} }
@ -1867,7 +1816,6 @@ NixInt EvalState::forceInt(Value & v, const Pos & pos)
if (v.type() != nInt) if (v.type() != nInt)
throwTypeError(pos, "value is %1% while an integer was expected", v, throwTypeError(pos, "value is %1% while an integer was expected", v,
fakeEnv(1), 0); fakeEnv(1), 0);
// map1("value", &v));
return v.integer; return v.integer;
} }
@ -1880,7 +1828,6 @@ NixFloat EvalState::forceFloat(Value & v, const Pos & pos)
else if (v.type() != nFloat) else if (v.type() != nFloat)
throwTypeError(pos, "value is %1% while a float was expected", v, throwTypeError(pos, "value is %1% while a float was expected", v,
fakeEnv(1), 0); fakeEnv(1), 0);
// map1("value", &v));
return v.fpoint; return v.fpoint;
} }
@ -1891,7 +1838,6 @@ bool EvalState::forceBool(Value & v, const Pos & pos)
if (v.type() != nBool) if (v.type() != nBool)
throwTypeError(pos, "value is %1% while a Boolean was expected", v, throwTypeError(pos, "value is %1% while a Boolean was expected", v,
fakeEnv(1), 0); fakeEnv(1), 0);
// map1("value", &v));
return v.boolean; return v.boolean;
} }
@ -1908,7 +1854,6 @@ void EvalState::forceFunction(Value & v, const Pos & pos)
if (v.type() != nFunction && !isFunctor(v)) if (v.type() != nFunction && !isFunctor(v))
throwTypeError(pos, "value is %1% while a function was expected", v, throwTypeError(pos, "value is %1% while a function was expected", v,
fakeEnv(1), 0); fakeEnv(1), 0);
// map1("value", &v));
} }
@ -1918,7 +1863,6 @@ string EvalState::forceString(Value & v, const Pos & pos)
if (v.type() != nString) { if (v.type() != nString) {
throwTypeError(pos, "value is %1% while a string was expected", v, throwTypeError(pos, "value is %1% while a string was expected", v,
fakeEnv(1), 0); fakeEnv(1), 0);
// map1("value", &v));
} }
return string(v.string.s); return string(v.string.s);
} }
@ -1970,37 +1914,15 @@ string EvalState::forceStringNoCtx(Value & v, const Pos & pos)
if (pos) if (pos)
throwEvalError(pos, "the string '%1%' is not allowed to refer to a store path (such as '%2%')", throwEvalError(pos, "the string '%1%' is not allowed to refer to a store path (such as '%2%')",
v.string.s, v.string.context[0], v.string.s, v.string.context[0],
// b.has_value() ? mapBindings(*b.get()) : map0());
fakeEnv(1), 0); fakeEnv(1), 0);
// map1("value", &v));
else else
throwEvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%')", throwEvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%')",
v.string.s, v.string.context[0], v.string.s, v.string.context[0],
// b.has_value() ? mapBindings(*b.get()) : map0());
fakeEnv(1), 0); fakeEnv(1), 0);
// map1("value", &v));
} }
return s; return s;
} }
/*string EvalState::forceStringNoCtx(std::optional<Bindings*> b, Value & v, const Pos & pos)
{
string s = forceString(v, pos);
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],
b.has_value() ? mapBindings(*b.get()) : map0());
// map1("value", &v));
else
throwEvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%')",
v.string.s, v.string.context[0],
b.has_value() ? mapBindings(*b.get()) : map0());
// map1("value", &v));
}
return s;
}*/
bool EvalState::isDerivation(Value & v) bool EvalState::isDerivation(Value & v)
{ {
@ -2084,7 +2006,6 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
throwTypeError(pos, "cannot coerce %1% to a string", v, throwTypeError(pos, "cannot coerce %1% to a string", v,
fakeEnv(1), 0); fakeEnv(1), 0);
// map1("value", &v));
} }
@ -2094,7 +2015,6 @@ string EvalState::copyPathToStore(PathSet & context, const Path & path)
throwEvalError("file names are not allowed to end in '%1%'", throwEvalError("file names are not allowed to end in '%1%'",
drvExtension, drvExtension,
fakeEnv(1), 0); fakeEnv(1), 0);
// map0());
Path dstPath; Path dstPath;
auto i = srcToStore.find(path); auto i = srcToStore.find(path);
@ -2120,7 +2040,6 @@ Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context)
if (path == "" || path[0] != '/') if (path == "" || path[0] != '/')
throwEvalError(pos, "string '%1%' doesn't represent an absolute path", path, throwEvalError(pos, "string '%1%' doesn't represent an absolute path", path,
fakeEnv(1), 0); fakeEnv(1), 0);
// map1("value", &v));
return path; return path;
} }
@ -2203,7 +2122,6 @@ bool EvalState::eqValues(Value & v1, Value & v2)
showType(v1), showType(v1),
showType(v2), showType(v2),
fakeEnv(1), 0); fakeEnv(1), 0);
// map2("value1", &v1, "value2", &v2));
} }
} }

View file

@ -574,10 +574,6 @@ namespace nix {
Expr * EvalState::parse(const char * text, FileOrigin origin, Expr * EvalState::parse(const char * text, FileOrigin origin,
const Path & path, const Path & basePath, std::shared_ptr<StaticEnv> & staticEnv) const Path & path, const Path & basePath, std::shared_ptr<StaticEnv> & staticEnv)
{ {
// std::cout << "EvalState::parse " << text << std::endl;
// for (auto i = staticEnv->vars.begin(); i != staticEnv->vars.end(); ++i)
// std::cout << "EvalState::parse staticEnv " << i->first << std::endl;
yyscan_t scanner; yyscan_t scanner;
ParseData data(*this); ParseData data(*this);
data.origin = origin; data.origin = origin;
@ -601,13 +597,8 @@ Expr * EvalState::parse(const char * text, FileOrigin origin,
if (res) throw ParseError(data.error.value()); if (res) throw ParseError(data.error.value());
// std::cout << "EvalState::parse pre bindvars " << std::endl;
data.result->bindVars(staticEnv); data.result->bindVars(staticEnv);
// std::cout << "EvalState::parse post bindVars " << std::endl;
return data.result; return data.result;
} }

View file

@ -110,11 +110,6 @@ static void mkOutputString(EvalState & state, Value & v,
argument. */ argument. */
static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vScope, Value & v) static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vScope, Value & v)
{ {
// std::cout << " IMPORT " << std::endl;
// std::cout << " import " << std::endl;
// std::cout << " IMPORT " << std::endl;
// std::cout << " import " << std::endl;
PathSet context; PathSet context;
Path path = state.coerceToPath(pos, vPath, context); Path path = state.coerceToPath(pos, vPath, context);
@ -199,8 +194,6 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS
env->values[displ++] = attr.value; env->values[displ++] = attr.value;
} }
std::cout << "import staticenv: {} " << staticEnv << std::endl;
printTalkative("evaluating file '%1%'", realPath); printTalkative("evaluating file '%1%'", realPath);
Expr * e = state.parseExprFromFile(resolveExprPath(realPath), staticEnv); Expr * e = state.parseExprFromFile(resolveExprPath(realPath), staticEnv);

View file

@ -408,7 +408,6 @@ struct CmdFlakeCheck : FlakeCommand
if (auto attr = v.attrs->get(state->symbols.create("description"))) if (auto attr = v.attrs->get(state->symbols.create("description")))
state->forceStringNoCtx(*attr->value, *attr->pos); state->forceStringNoCtx(*attr->value, *attr->pos);
// state->forceStringNoCtx(std::optional(v.attrs), *attr->value, *attr->pos);
else else
throw Error("template '%s' lacks attribute 'description'", attrPath); throw Error("template '%s' lacks attribute 'description'", attrPath);