From dad8bc679e9f7bd97442249293138c7a2af311e4 Mon Sep 17 00:00:00 2001 From: eldritch horrors Date: Sun, 16 Jun 2024 23:10:09 +0200 Subject: [PATCH] libexpr: don't immediately throw parser errors now that destructors are hooked up we want to give the C skeleton every real chance to actually run them. since bison does not call destructors on values that have been passed to semantic actions even when an action causes an abort we will also have to delete some things manually still. Change-Id: Ia22bdaa9e969b74e17a6c496e35e6c2d86b7d750 --- src/libexpr/lexer.l | 18 ++++++++++----- src/libexpr/parser-state.hh | 37 +++++++++++++++-------------- src/libexpr/parser.y | 46 ++++++++++++++++++++++++++----------- 3 files changed, 64 insertions(+), 37 deletions(-) diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index 1c1cdd673..5bc815f00 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -32,6 +32,12 @@ using namespace nix; +#define THROW(...) \ + do { \ + state->error.reset(new auto(__VA_ARGS__)); \ + return YYerror; \ + } while (0) + namespace nix { #define CUR_POS state->at(*yylloc) @@ -135,20 +141,20 @@ or { return OR_KW; } if (numMay.has_value()) { yylval->n = *numMay; } else { - throw ParseError(ErrorInfo{ + THROW(ParseError(ErrorInfo{ .msg = HintFmt("invalid integer '%1%'", yytext), .pos = state->positions[CUR_POS], - }); + })); } return INT; } {FLOAT} { errno = 0; yylval->nf = strtod(yytext, 0); if (errno != 0) - throw ParseError(ErrorInfo{ + THROW(ParseError(ErrorInfo{ .msg = HintFmt("invalid float '%1%'", yytext), .pos = state->positions[CUR_POS], - }); + })); return FLOAT; } @@ -274,10 +280,10 @@ or { return OR_KW; } {ANY} | <> { - throw ParseError(ErrorInfo{ + THROW(ParseError(ErrorInfo{ .msg = HintFmt("path has a trailing slash"), .pos = state->positions[CUR_POS], - }); + })); } {SPATH} { yylval->path = {yytext, (size_t) yyleng}; return SPATH; } diff --git a/src/libexpr/parser-state.hh b/src/libexpr/parser-state.hh index 3305e05b6..94f10064f 100644 --- a/src/libexpr/parser-state.hh +++ b/src/libexpr/parser-state.hh @@ -45,34 +45,35 @@ struct ParserState SourcePath basePath; PosTable::Origin origin; const Expr::AstSymbols & s; + std::unique_ptr error; - void dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos); - void dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos); - void addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * e, const PosIdx pos); - Formals * validateFormals(Formals * formals, PosIdx pos = noPos, Symbol arg = {}); + [[nodiscard]] ParseError dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos); + [[nodiscard]] ParseError dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos); + [[nodiscard]] std::optional addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * e, const PosIdx pos); + [[nodiscard]] std::optional validateFormals(Formals * formals, PosIdx pos = noPos, Symbol arg = {}); Expr * stripIndentation(const PosIdx pos, std::vector>> && es); PosIdx at(const ParserLocation & loc); }; -inline void ParserState::dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos) +inline ParseError ParserState::dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos) { - throw ParseError({ + return ParseError({ .msg = HintFmt("attribute '%1%' already defined at %2%", showAttrPath(symbols, attrPath), positions[prevPos]), .pos = positions[pos] }); } -inline void ParserState::dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos) +inline ParseError ParserState::dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos) { - throw ParseError({ + return ParseError({ .msg = HintFmt("attribute '%1%' already defined at %2%", symbols[attr], positions[prevPos]), .pos = positions[pos] }); } -inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * e, const PosIdx pos) +inline std::optional ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * e, const PosIdx pos) { AttrPath::iterator i; // All attrpaths have at least one attr @@ -85,10 +86,10 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * if (j != attrs->attrs.end()) { if (j->second.kind != ExprAttrs::AttrDef::Kind::Inherited) { ExprAttrs * attrs2 = dynamic_cast(j->second.e); - if (!attrs2) dupAttr({attrPath.begin(), i + 1}, pos, j->second.pos); + if (!attrs2) return dupAttr({attrPath.begin(), i + 1}, pos, j->second.pos); attrs = attrs2; } else - dupAttr({attrPath.begin(), i + 1}, pos, j->second.pos); + return dupAttr({attrPath.begin(), i + 1}, pos, j->second.pos); } else { ExprAttrs * nested = new ExprAttrs; attrs->attrs[i->symbol] = ExprAttrs::AttrDef(nested, pos); @@ -117,7 +118,7 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * for (auto & ad : ae->attrs) { auto j2 = jAttrs->attrs.find(ad.first); if (j2 != jAttrs->attrs.end()) // Attr already defined in iAttrs, error. - dupAttr(ad.first, j2->second.pos, ad.second.pos); + return dupAttr(ad.first, j2->second.pos, ad.second.pos); jAttrs->attrs.emplace(ad.first, ad.second); if (ad.second.kind == ExprAttrs::AttrDef::Kind::InheritedFrom) { auto & sel = dynamic_cast(*ad.second.e); @@ -131,7 +132,7 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * ae->inheritFromExprs->begin(), ae->inheritFromExprs->end()); } } else { - dupAttr(attrPath, pos, j->second.pos); + return dupAttr(attrPath, pos, j->second.pos); } } else { // This attr path is not defined. Let's create it. @@ -141,9 +142,11 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * } else { attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, e, pos)); } + + return {}; } -inline Formals * ParserState::validateFormals(Formals * formals, PosIdx pos, Symbol arg) +inline std::optional ParserState::validateFormals(Formals * formals, PosIdx pos, Symbol arg) { std::sort(formals->formals.begin(), formals->formals.end(), [] (const auto & a, const auto & b) { @@ -158,18 +161,18 @@ inline Formals * ParserState::validateFormals(Formals * formals, PosIdx pos, Sym duplicate = std::min(thisDup, duplicate.value_or(thisDup)); } if (duplicate) - throw ParseError({ + return ParseError({ .msg = HintFmt("duplicate formal function argument '%1%'", symbols[duplicate->first]), .pos = positions[duplicate->second] }); if (arg && formals->has(arg)) - throw ParseError({ + return ParseError({ .msg = HintFmt("duplicate formal function argument '%1%'", symbols[arg]), .pos = positions[pos] }); - return formals; + return {}; } inline Expr * ParserState::stripIndentation(const PosIdx pos, diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 594026028..7783d2c20 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -62,6 +62,13 @@ using namespace nix; // otherwise destructors cause compiler errors #pragma GCC diagnostic ignored "-Wswitch-enum" +#define THROW(err, ...) \ + do { \ + state->error.reset(new auto(err)); \ + [](auto... d) { (delete d, ...); }(__VA_ARGS__); \ + YYABORT; \ + } while (0) + void yyerror(YYLTYPE * loc, yyscan_t scanner, ParserState * state, const char * error) { if (std::string_view(error).starts_with("syntax error, unexpected end of file")) { @@ -153,16 +160,19 @@ expr_function : ID ':' expr_function { $$ = new ExprLambda(CUR_POS, state->symbols.create($1), 0, $3); } | '{' formals '}' ':' expr_function - { $$ = new ExprLambda(CUR_POS, state->validateFormals($2), $5); } + { if (auto e = state->validateFormals($2)) THROW(*e); + $$ = new ExprLambda(CUR_POS, $2, $5); } | '{' formals '}' '@' ID ':' expr_function { auto arg = state->symbols.create($5); - $$ = new ExprLambda(CUR_POS, arg, state->validateFormals($2, CUR_POS, arg), $7); + if (auto e = state->validateFormals($2, CUR_POS, arg)) THROW(*e, $2, $7); + $$ = new ExprLambda(CUR_POS, arg, $2, $7); } | ID '@' '{' formals '}' ':' expr_function { auto arg = state->symbols.create($1); - $$ = new ExprLambda(CUR_POS, arg, state->validateFormals($4, CUR_POS, arg), $7); + if (auto e = state->validateFormals($4, CUR_POS, arg)) THROW(*e, $4, $7); + $$ = new ExprLambda(CUR_POS, arg, $4, $7); } | ASSERT expr ';' expr_function { $$ = new ExprAssert(CUR_POS, $2, $4); } @@ -170,10 +180,10 @@ expr_function { $$ = new ExprWith(CUR_POS, $2, $4); } | LET binds IN expr_function { if (!$2->dynamicAttrs.empty()) - throw ParseError({ + THROW(ParseError({ .msg = HintFmt("dynamic attributes not allowed in let"), .pos = state->positions[CUR_POS] - }); + }), $2, $4); $$ = new ExprLet($2, $4); } | expr_if @@ -260,10 +270,10 @@ expr_simple | URI { static bool noURLLiterals = experimentalFeatureSettings.isEnabled(Xp::NoUrlLiterals); if (noURLLiterals) - throw ParseError({ + THROW(ParseError({ .msg = HintFmt("URL literals are disabled"), .pos = state->positions[CUR_POS] - }); + })); $$ = new ExprString(std::string($1)); } | '(' expr ')' { $$ = $2; } @@ -306,10 +316,10 @@ path_start } | HPATH { if (evalSettings.pureEval) { - throw Error( + THROW(Error( "the path '%s' can not be resolved in pure mode", std::string_view($1.p, $1.l) - ); + )); } Path path(getHome() + std::string($1.p + 1, $1.l - 1)); $$ = new ExprPath(path); @@ -323,12 +333,16 @@ ind_string_parts ; binds - : binds attrpath '=' expr ';' { $$ = $1; state->addAttr($$, std::move(*$2), $4, state->at(@2)); delete $2; } + : binds attrpath '=' expr ';' + { $$ = $1; + if (auto e = state->addAttr($$, std::move(*$2), $4, state->at(@2))) THROW(*e, $1, $2); + delete $2; + } | binds INHERIT attrs ';' { $$ = $1; for (auto & [i, iPos] : *$3) { if ($$->attrs.find(i.symbol) != $$->attrs.end()) - state->dupAttr(i.symbol, iPos, $$->attrs[i.symbol].pos); + THROW(state->dupAttr(i.symbol, iPos, $$->attrs[i.symbol].pos), $1); $$->attrs.emplace( i.symbol, ExprAttrs::AttrDef(new ExprVar(iPos, i.symbol), iPos, ExprAttrs::AttrDef::Kind::Inherited)); @@ -343,7 +357,7 @@ binds auto from = new nix::ExprInheritFrom(state->at(@4), $$->inheritFromExprs->size() - 1); for (auto & [i, iPos] : *$6) { if ($$->attrs.find(i.symbol) != $$->attrs.end()) - state->dupAttr(i.symbol, iPos, $$->attrs[i.symbol].pos); + THROW(state->dupAttr(i.symbol, iPos, $$->attrs[i.symbol].pos), $1); $$->attrs.emplace( i.symbol, ExprAttrs::AttrDef( @@ -365,10 +379,10 @@ attrs $$->emplace_back(AttrName(state->symbols.create(str->s)), state->at(@2)); delete str; } else - throw ParseError({ + THROW(ParseError({ .msg = HintFmt("dynamic attributes not allowed in inherit"), .pos = state->positions[state->at(@2)] - }); + }), $1, $2); } | { $$ = new std::vector>; } ; @@ -457,6 +471,10 @@ Expr * parseExprFromBuf( yy_scan_buffer(text, length, scanner); yyparse(scanner, &state); + if (state.error) { + delete state.result; + throw *state.error; + } return state.result; }