From 6bcdd2c39a76fe1c00b7e046c8d0d1bc33eb67c3 Mon Sep 17 00:00:00 2001 From: pennae Date: Sun, 11 Feb 2024 12:09:07 +0100 Subject: [PATCH] don't immediately throw parser errors now that destructors are hooked up we want to give the C skeleton a chance to actually run them. since bison does not call destructors on values that have been passed to semantic actions even when the action causes an abort we will also have to do some manual deleting. partially reverts e8d9de967fe47a7f9324b0022a2ef50df59f419d. --- 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 05e754cf1..da1dcdbcf 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -27,6 +27,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) @@ -129,20 +135,20 @@ or { return OR_KW; } try { yylval->n = boost::lexical_cast(yytext); } catch (const boost::bad_lexical_cast &) { - throw ParseError(ErrorInfo{ + THROW(ParseError({ .msg = HintFmt("invalid integer '%1%'", yytext), .pos = state->positions[CUR_POS], - }); + })); } return INT_LIT; } {FLOAT} { errno = 0; yylval->nf = strtod(yytext, 0); if (errno != 0) - throw ParseError(ErrorInfo{ + THROW(ParseError({ .msg = HintFmt("invalid float '%1%'", yytext), .pos = state->positions[CUR_POS], - }); + })); return FLOAT_LIT; } @@ -268,10 +274,10 @@ or { return OR_KW; } {ANY} | <> { - throw ParseError(ErrorInfo{ + THROW(ParseError({ .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 024e79c43..b4b8e08e2 100644 --- a/src/libexpr/parser-state.hh +++ b/src/libexpr/parser-state.hh @@ -46,34 +46,35 @@ struct ParserState PosTable::Origin origin; const ref rootFS; 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 @@ -86,10 +87,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, pos, j->second.pos); + if (!attrs2) return dupAttr(attrPath, pos, j->second.pos); attrs = attrs2; } else - dupAttr(attrPath, pos, j->second.pos); + return dupAttr(attrPath, pos, j->second.pos); } else { ExprAttrs * nested = new ExprAttrs; attrs->attrs[i->symbol] = ExprAttrs::AttrDef(nested, pos); @@ -118,7 +119,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); @@ -132,7 +133,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. @@ -142,9 +143,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) { @@ -159,18 +162,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 d0f09081e..7c58f0142 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -64,6 +64,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")) { @@ -155,16 +162,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); } @@ -172,10 +182,10 @@ expr_function { $$ = new ExprWith(CUR_POS, $2, $4); } | LET binds IN_KW 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 @@ -262,10 +272,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; } @@ -308,10 +318,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(ref(state->rootFS), std::move(path)); @@ -325,12 +335,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)); @@ -345,7 +359,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( @@ -367,10 +381,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>; } ; @@ -461,6 +475,10 @@ Expr * parseExprFromBuf( yy_scan_buffer(text, length, scanner); yyparse(scanner, &state); + if (state.error) { + delete state.result; + throw *state.error; + } return state.result; }