From 4971f6838a5fcb8bfa8844b10fa704f0e891fc49 Mon Sep 17 00:00:00 2001 From: eldritch horrors Date: Sat, 16 Mar 2024 15:15:13 +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. 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 1780f5cb9..e98166f15 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -33,6 +33,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; } 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; } {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; } @@ -274,10 +280,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 247a6bbff..c96e64156 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, 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); @@ -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 26e4bac02..dbfeb38d9 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; }