diff --git a/ChangeLog b/ChangeLog index 5da2b92b..c5e77034 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +2006-01-05 Joel E. Denny + + * data/glr.c (yyresolveValue): When merging semantic options, if at + least one user action succeeds but a later one cuts the parse, then + destroy the semantic value before returning rather than leaking it. + (yyresolveStates): If a user action cuts the parse and thus + yyresolveValue fails, ignore the (unset) semantic value rather than + corrupting the yyGLRState, and empty the semantic options list since + the user actions should have called all necessary destructors. + Simplify code with YYCHK. + * tests/glr-regression.at (Corrupted semantic options if user action + cuts parse): New test case. + (Undesirable destructors if user action cuts parse): New test case. + Before applying any of this patch, this test case never actually failed + for me... but only because the corrupted semantic options usually + masked this bug. + (Leaked merged semantic value if user action cuts parse): New test + case. + 2006-01-05 Akim Demaille * src/reader.c, src/symlist.h, src/symlist.c: s/mid_rule/midrule/. diff --git a/data/glr.c b/data/glr.c index 20f5043f..975ec578 100644 --- a/data/glr.c +++ b/data/glr.c @@ -1612,7 +1612,7 @@ yypreference (yySemanticOption* y0, yySemanticOption* y1) return 0; } -static YYRESULTTAG yyresolveValue (yySemanticOption* yyoptionList, +static YYRESULTTAG yyresolveValue (yyGLRState* yys, yyGLRStack* yystackp, YYSTYPE* yyvalp, YYLTYPE* yylocp]b4_user_formals[); @@ -1620,20 +1620,21 @@ static YYRESULTTAG yyresolveStates (yyGLRState* yys, int yyn, yyGLRStack* yystackp]b4_user_formals[) { - YYRESULTTAG yyflag; if (0 < yyn) { YYASSERT (yys->yypred); - yyflag = yyresolveStates (yys->yypred, yyn-1, yystackp]b4_user_args[); - if (yyflag != yyok) - return yyflag; + YYCHK (yyresolveStates (yys->yypred, yyn-1, yystackp]b4_user_args[)); if (! yys->yyresolved) { - yyflag = yyresolveValue (yys->yysemantics.yyfirstVal, yystackp, - &yys->yysemantics.yysval, &yys->yyloc - ]b4_user_args[); + YYSTYPE yysval; + YYRESULTTAG yyflag = yyresolveValue (yys, yystackp, &yysval, + &yys->yyloc]b4_user_args[); if (yyflag != yyok) - return yyflag; + { + yys->yysemantics.yyfirstVal = NULL; + return yyflag; + } + yys->yysemantics.yysval = yysval; yys->yyresolved = yytrue; } } @@ -1728,12 +1729,13 @@ yyreportAmbiguity (yySemanticOption* yyx0, yySemanticOption* yyx1, } -/** Resolve the ambiguity represented by OPTIONLIST, perform the indicated +/** Resolve the ambiguity represented in state S, perform the indicated * actions, and return the result. */ static YYRESULTTAG -yyresolveValue (yySemanticOption* yyoptionList, yyGLRStack* yystackp, - YYSTYPE* yyvalp, YYLTYPE* yylocp]b4_user_formals[) +yyresolveValue (yyGLRState* yys, yyGLRStack* yystackp, YYSTYPE* yyvalp, + YYLTYPE* yylocp]b4_user_formals[) { + yySemanticOption* yyoptionList = yys->yysemantics.yyfirstVal; yySemanticOption* yybest; yySemanticOption** yypp; yybool yymerge; @@ -1786,8 +1788,15 @@ yyresolveValue (yySemanticOption* yyoptionList, yyGLRStack* yystackp, { YYSTYPE yyval1; YYLTYPE yydummy; - YYCHK (yyresolveAction (yyp, yystackp, &yyval1, - &yydummy]b4_user_args[)); + YYRESULTTAG yyflag = yyresolveAction (yyp, yystackp, &yyval1, + &yydummy]b4_user_args[); + if (yyflag != yyok) + { + yydestruct ("Cleanup: discarding merged value", + yystos[yys->yylrState], + yyvalp]b4_location_if([, yylocp])[]b4_user_args[); + return yyflag; + } yyuserMerge (yymerger[yyp->yyrule], yyvalp, &yyval1); } } diff --git a/tests/glr-regression.at b/tests/glr-regression.at index ad8f6d86..b1e91180 100644 --- a/tests/glr-regression.at +++ b/tests/glr-regression.at @@ -815,3 +815,203 @@ AT_CHECK([[./glr-regr9]], 0, [], ]) AT_CLEANUP + + +## ------------------------------------------------------------------------- ## +## Corrupted semantic options if user action cuts parse. ## +## ------------------------------------------------------------------------- ## + +AT_SETUP([Corrupted semantic options if user action cuts parse.]) + +AT_DATA_GRAMMAR([glr-regr10.y], +[[ +%{ +# include + static void yyerror (char const *); + static int yylex (void); + #define GARBAGE_SIZE 50 + static char garbage[GARBAGE_SIZE]; +%} + +%glr-parser +%union { char *ptr; } +%type start + +%% + +start: + %dprec 2 { $$ = garbage; YYACCEPT; } + | %dprec 1 { $$ = garbage; YYACCEPT; } + ; + +%% + +static void +yyerror (char const *msg) +{ + fprintf (stderr, "%s\n", msg); +} + +static int +yylex (void) +{ + return 0; +} + +int +main (void) +{ + int index; + for (index = 0; index < GARBAGE_SIZE; index+=1) + garbage[index] = 132; + return yyparse (); +} +]]) + +AT_CHECK([[bison -t -o glr-regr10.c glr-regr10.y]], 0, [], +[glr-regr10.y: conflicts: 1 reduce/reduce +]) +AT_COMPILE([glr-regr10]) + +AT_CHECK([[./glr-regr10]], 0, [], []) + +AT_CLEANUP + + +## ------------------------------------------------------------------------- ## +## Undesirable destructors if user action cuts parse. ## +## ------------------------------------------------------------------------- ## + +AT_SETUP([Undesirable destructors if user action cuts parse.]) + +AT_DATA_GRAMMAR([glr-regr11.y], +[[ +%{ +# include + static void yyerror (char const *); + static int yylex (void); + static int destructors = 0; +# define USE(val) +%} + +%glr-parser +%union { int dummy; } +%type 'a' +%destructor { destructors += 1; } 'a' + +%% + +start: + 'a' %dprec 2 { USE ($1); destructors += 1; YYACCEPT; } + | 'a' %dprec 1 { USE ($1); destructors += 1; YYACCEPT; } + ; + +%% + +static void +yyerror (char const *msg) +{ + fprintf (stderr, "%s\n", msg); +} + +static int +yylex (void) +{ + static char const *input = "a"; + return *input++; +} + +int +main (void) +{ + int exit_status = yyparse (); + if (destructors != 1) + { + fprintf (stderr, "Destructor calls: %d\n", destructors); + return 1; + } + return exit_status; +} +]]) + +AT_CHECK([[bison -t -o glr-regr11.c glr-regr11.y]], 0, [], +[glr-regr11.y: conflicts: 1 reduce/reduce +]) +AT_COMPILE([glr-regr11]) + +AT_CHECK([[./glr-regr11]], 0, [], []) + +AT_CLEANUP + + +## ------------------------------------------------------------------------- ## +## Leaked merged semantic value if user action cuts parse. ## +## ------------------------------------------------------------------------- ## + +AT_SETUP([Leaked merged semantic value if user action cuts parse.]) + +AT_DATA_GRAMMAR([glr-regr12.y], +[[ +%glr-parser +%union { int dummy; } +%type start +%destructor { has_value = 0; } start + +%{ +# include + static int merge (YYSTYPE, YYSTYPE); + static void yyerror (char const *); + static int yylex (void); + static int has_value = 0; +# define USE(val) +%} + +%% + +start: + %merge { has_value = 1; USE ($$); } + | %merge { USE ($$); YYACCEPT; } + ; + +%% + +static int +merge (YYSTYPE s1, YYSTYPE s2) +{ + /* Not invoked. */ + return 0; +} + +static void +yyerror (char const *msg) +{ + fprintf (stderr, "%s\n", msg); +} + +static int +yylex (void) +{ + return 0; +} + +int +main (void) +{ + int exit_status = yyparse (); + if (has_value) + { + fprintf (stderr, "Destructor not called.\n"); + return 1; + } + return exit_status; +} +]]) + +AT_CHECK([[bison -t -o glr-regr12.c glr-regr12.y]], 0, [], +[glr-regr12.y: conflicts: 1 reduce/reduce +]) +AT_COMPILE([glr-regr12]) + +AT_CHECK([[./glr-regr12]], 0, [], []) + +AT_CLEANUP