From 44e76d801f30721b0da8769cae4625dc6b92e68f Mon Sep 17 00:00:00 2001 From: Akim Demaille Date: Fri, 17 Aug 2018 19:28:50 +0200 Subject: [PATCH] lalr1.cc: support compilation with disabled support for exceptions Reported by Brooks Moses http://lists.gnu.org/archive/html/bison-patches/2018-02/msg00000.html * data/lalr1.cc (YY_EXCEPTIONS): New. Use it to disable try/catch clauses. * doc/bison.texi (C++ Parser Interface): Document it. * configure.ac (CXXFLAGS_NO_EXCEPTIONS): New. * tests/atlocal.in: Receive it. * tests/local.at (AT_FULL_COMPILE, AT_LANG_COMPILE): Accept a new argument, extra compiler flags. * tests/calc.at: Run the C++ calculator with exception support disabled. --- NEWS | 7 +++++++ THANKS | 1 + configure.ac | 1 + data/lalr1.cc | 21 +++++++++++++++++++++ doc/bison.texi | 17 ++++++++++------- tests/atlocal.in | 3 +++ tests/calc.at | 13 +++++++------ tests/local.at | 33 ++++++++++++++++++--------------- 8 files changed, 68 insertions(+), 28 deletions(-) diff --git a/NEWS b/NEWS index cd5d6ae6..63f40c69 100644 --- a/NEWS +++ b/NEWS @@ -74,6 +74,13 @@ GNU Bison NEWS input: '0' | exp ^^^ +*** C++: Generated parsers can be compiled with -fno-exceptions (lalr1.cc) + + When compiled with exceptions disabled, the generated parsers no longer + uses try/catch clauses. + + Currently only GCC and Clang are supported. + ** Bug fixes *** GLR: Predicates support broken by #line directives diff --git a/THANKS b/THANKS index 48b4ec85..c655e3c6 100644 --- a/THANKS +++ b/THANKS @@ -25,6 +25,7 @@ Bert Deknuydt Bert.Deknuydt@esat.kuleuven.ac.be Bill Allombert Bill.Allombert@math.u-bordeaux1.fr Bob Rossi bob@brasko.net Brandon Lucia blucia@gmail.com +Brooks Moses bmoses@google.com Bruce Lilly blilly@erols.com Bruno Haible bruno@clisp.org Charles-Henri de Boysson de-boy_c@epita.fr diff --git a/configure.ac b/configure.ac index 99f6ad43..1a9dcb00 100644 --- a/configure.ac +++ b/configure.ac @@ -153,6 +153,7 @@ if test "$enable_gcc_warnings" = yes; then # ... possiby in std=c++11 mode. gl_WARN_ADD([-Wno-zero-as-null-pointer-constant], [FLEX_SCANNER_CXXFLAGS]) CXXFLAGS=$save_CXXFLAGS + gl_WARN_ADD([-fno-exceptions], [CXXFLAGS_NO_EXCEPTIONS]) AC_LANG_POP([C++]) fi diff --git a/data/lalr1.cc b/data/lalr1.cc index 44e6166d..eef8176b 100644 --- a/data/lalr1.cc +++ b/data/lalr1.cc @@ -424,6 +424,15 @@ m4_if(b4_prefix, [yy], [], # endif #endif +// Whether we are compiled with exception support. +#ifndef YY_EXCEPTIONS +# if defined __GNUC__ && !defined __EXCEPTIONS +# define YY_EXCEPTIONS 0 +# else +# define YY_EXCEPTIONS 1 +# endif +#endif + ]b4_locations_if([dnl [#define YYRHSLOC(Rhs, K) ((Rhs)[K].location) ]b4_yylloc_default_define])[ @@ -717,7 +726,9 @@ m4_if(b4_prefix, [yy], [], /// The return value of parse (). int yyresult; +#if YY_EXCEPTIONS try +#endif // YY_EXCEPTIONS { YYCDEBUG << "Starting parse\n"; @@ -755,17 +766,21 @@ b4_dollar_popdef])[]dnl if (yyla.empty ()) { YYCDEBUG << "Reading a token: "; +#if YY_EXCEPTIONS try +#endif // YY_EXCEPTIONS {]b4_token_ctor_if([[ symbol_type yylookahead (]b4_lex[); yyla.move (yylookahead);]], [[ yyla.type = yytranslate_ (]b4_lex[);]])[ } +#if YY_EXCEPTIONS catch (const syntax_error& yyexc) { error (yyexc); goto yyerrlab1; } +#endif // YY_EXCEPTIONS } YY_SYMBOL_PRINT ("Next token is", yyla); @@ -835,7 +850,9 @@ b4_dollar_popdef])[]dnl // Perform the reduction. YY_REDUCE_PRINT (yyn); +#if YY_EXCEPTIONS try +#endif // YY_EXCEPTIONS { switch (yyn) { @@ -844,11 +861,13 @@ b4_dollar_popdef])[]dnl break; } } +#if YY_EXCEPTIONS catch (const syntax_error& yyexc) { error (yyexc); YYERROR; } +#endif // YY_EXCEPTIONS YY_SYMBOL_PRINT ("-> $$ =", yylhs); yypop_ (yylen); yylen = 0; @@ -973,6 +992,7 @@ b4_dollar_popdef])[]dnl return yyresult; } +#if YY_EXCEPTIONS catch (...) { YYCDEBUG << "Exception caught: cleaning lookahead and stack\n"; @@ -988,6 +1008,7 @@ b4_dollar_popdef])[]dnl } throw; } +#endif // YY_EXCEPTIONS } void diff --git a/doc/bison.texi b/doc/bison.texi index 952b7588..b463588b 100644 --- a/doc/bison.texi +++ b/doc/bison.texi @@ -10885,13 +10885,12 @@ use @code{yy::parser::token::FOO}. The scanner can use @defcv {Type} {parser} {syntax_error} This class derives from @code{std::runtime_error}. Throw instances of it -from the scanner or from the user actions to raise parse errors. This is -equivalent with first -invoking @code{error} to report the location and message of the syntax -error, and then to invoke @code{YYERROR} to enter the error-recovery mode. -But contrary to @code{YYERROR} which can only be invoked from user actions -(i.e., written in the action itself), the exception can be thrown from -function invoked from the user action. +from the scanner or from the actions to raise parse errors. This is +equivalent with first invoking @code{error} to report the location and +message of the syntax error, and then to invoke @code{YYERROR} to enter the +error-recovery mode. But contrary to @code{YYERROR} which can only be +invoked from user actions (i.e., written in the action itself), the +exception can be thrown from function invoked from the user action. @end defcv @deftypemethod {parser} {} parser (@var{type1} @var{arg1}, ...) @@ -10911,6 +10910,10 @@ Run the syntactic analysis, and return 0 on success, 1 otherwise. The whole function is wrapped in a @code{try}/@code{catch} block, so that when an exception is thrown, the @code{%destructor}s are called to release the lookahead symbol, and the symbols pushed on the stack. + +Exception related code in the generated parser is protected by CPP guards +(@code{#if}) and disabled when exceptions are not supported (i.e., passing +@code{-fno-exceptions} to the C++ compiler). @end deftypemethod @deftypemethod {parser} {std::ostream&} debug_stream () diff --git a/tests/atlocal.in b/tests/atlocal.in index 5db32669..93ba7613 100644 --- a/tests/atlocal.in +++ b/tests/atlocal.in @@ -50,6 +50,9 @@ CXXFLAGS="$NO_WERROR_CXXFLAGS @WERROR_CXXFLAGS@" # If 'exit 77'; skip all C++ tests; otherwise ':'. BISON_CXX_WORKS='@BISON_CXX_WORKS@' +# Compiler flags to disable exception support. +CXXFLAGS_NO_EXCEPTIONS='@CXXFLAGS_NO_EXCEPTIONS@' + # Be sure that the C++ compiler is not broken because of gnulib. This # cannot be checked in configure (gnulib is not parameterized yet), # and checking this in every C++ test in AC_COMPILE_CXX is too costly. diff --git a/tests/calc.at b/tests/calc.at index 3d83ba72..acc27da2 100644 --- a/tests/calc.at +++ b/tests/calc.at @@ -481,20 +481,20 @@ m4_define([AT_CHECK_SPACES], ]) -# AT_CHECK_CALC([BISON-OPTIONS]) -# ------------------------------ +# AT_CHECK_CALC([BISON-OPTIONS], [COMPILER-OPTIONS]) +# -------------------------------------------------- # Start a testing chunk which compiles 'calc' grammar with # BISON-OPTIONS, and performs several tests over the parser. m4_define([AT_CHECK_CALC], -[m4_ifval([$2], [m4_fatal([$0: expected a single argument])]) +[m4_ifval([$3], [m4_fatal([$0: expected at most two arguments])]) # We use integers to avoid dependencies upon the precision of doubles. -AT_SETUP([Calculator $1]) +AT_SETUP([Calculator $1 $2]) AT_BISON_OPTION_PUSHDEFS([$1]) AT_DATA_CALC_Y([$1]) -AT_FULL_COMPILE([calc], AT_DEFINES_IF([[lex], [main]])) +AT_FULL_COMPILE([calc], AT_DEFINES_IF([[lex], [main]]), [], [$2]) AT_CHECK_SPACES(m4_join([ ], [calc.AT_SKEL_CC_IF([cc], [c])], [AT_DEFINES_IF([calc.AT_SKEL_CC_IF([hh], [h])])])) @@ -677,10 +677,11 @@ AT_CHECK_CALC([%skeleton "lalr1.cc" %defines]) # Start a testing chunk which compiles 'calc' grammar with # the C++ skeleton, and performs several tests over the parser. m4_define([AT_CHECK_CALC_LALR1_CC], -[AT_CHECK_CALC([%language "C++"] $@)]) +[AT_CHECK_CALC([%language "C++" $1], [$2])]) AT_CHECK_CALC_LALR1_CC([]) AT_CHECK_CALC_LALR1_CC([%locations]) +AT_CHECK_CALC_LALR1_CC([%locations], [$CXXFLAGS_NO_EXCEPTIONS]) AT_CHECK_CALC_LALR1_CC([%locations %define api.location.type {Span}]) AT_CHECK_CALC_LALR1_CC([%defines %locations %define parse.error verbose %name-prefix "calc" %verbose %yacc]) diff --git a/tests/local.at b/tests/local.at index b1bdb4da..5341549e 100644 --- a/tests/local.at +++ b/tests/local.at @@ -740,8 +740,8 @@ m4_define([AT_QUELL_VALGRIND], ## ------------------------ ## -# AT_COMPILE(OUTPUT, [SOURCES = OUTPUT.c]) -# ---------------------------------------- +# AT_COMPILE(OUTPUT, [SOURCES = OUTPUT.c], [EXTRA-COMPILER-FLAGS]) +# ---------------------------------------------------------------- # Compile SOURCES into OUTPUT. # # If OUTPUT does not contain '.', assume that we are linking too, @@ -758,8 +758,8 @@ AT_CHECK(m4_join([ ], 0, [ignore], [ignore])]) -# AT_COMPILE_CXX(OUTPUT, [SOURCES = OUTPUT.cc]) -# --------------------------------------------- +# AT_COMPILE_CXX(OUTPUT, [SOURCES = OUTPUT.cc], [EXTRA-COMPILER-FLAGS]) +# --------------------------------------------------------------------- # Compile SOURCES into OUTPUT. If the C++ compiler does not work, # ignore the test. # @@ -770,7 +770,7 @@ m4_define([AT_COMPILE_CXX], [AT_KEYWORDS(c++) AT_CHECK([$BISON_CXX_WORKS], 0, ignore, ignore) AT_CHECK(m4_join([ ], - [$CXX $CXXFLAGS $CPPFLAGS], + [$CXX $CXXFLAGS $CPPFLAGS $3], [m4_bmatch([$1], [[.]], [-c], [$LDFLAGS])], [-o $1], [m4_default([$2], [m4_bpatsubst([$1], [\.o$]).cc])], @@ -790,21 +790,21 @@ AT_CHECK([[$SHELL ../../../javacomp.sh ]$1], [[0]], [ignore], [ignore])]) -# AT_LANG_COMPILE(OUTPUT, [SOURCES = OUTPUT.c] -# -------------------------------------------- +# AT_LANG_COMPILE(OUTPUT, [SOURCES = OUTPUT.c], [EXTRA-COMPILER-FLAGS]) +# --------------------------------------------------------------------- # Compile SOURCES into OUTPUT. Skip if compiler does not work. # # If OUTPUT does not contain '.', assume that we are linking too, # otherwise pass "-c"; this is a hack. The default SOURCES is OUTPUT # with trailing .o removed, and ".c"/".cc" appended. m4_define([AT_LANG_COMPILE], [AT_LANG_DISPATCH([$0], $@)]) -m4_define([AT_LANG_COMPILE(c)], [AT_COMPILE([$1], [$2])]) -m4_define([AT_LANG_COMPILE(c++)], [AT_COMPILE_CXX([$1], [$2])]) -m4_define([AT_LANG_COMPILE(java)], [AT_JAVA_COMPILE([$1.java], [$2])]) +m4_define([AT_LANG_COMPILE(c)], [AT_COMPILE([$1], [$2], [$3])]) +m4_define([AT_LANG_COMPILE(c++)], [AT_COMPILE_CXX([$1], [$2], [$3])]) +m4_define([AT_LANG_COMPILE(java)], [AT_JAVA_COMPILE([$1.java], [$2], [$3])]) -# AT_FULL_COMPILE(OUTPUT, [OTHER1], [OTHER2]) -# ------------------------------------------- +# AT_FULL_COMPILE(OUTPUT, [OTHER1], [OTHER2], [EXTRA-COMPILER-FLAGS) +# ------------------------------------------------------------------ # Compile OUTPUT.y to OUTPUT.c, OUTPUT.cc, or OUTPUT.java, and then # compile it to OUTPUT or OUTPUT.class. If OTHER is specified, compile # OUTPUT-OTHER.c, OUTPUT-OTHER.cc, or OUTPUT-OTHER.java to OUTPUT or @@ -817,7 +817,8 @@ m4_define([AT_FULL_COMPILE(c)], m4_join([ ], [$1.c], m4_ifval($2, [[$1-$2.c]]), - m4_ifval($3, [[$1-$3.c]])))]) + m4_ifval($3, [[$1-$3.c]])), + [$4])]) m4_define([AT_FULL_COMPILE(c++)], [AT_BISON_CHECK([-o $1.cc $1.y]) @@ -825,7 +826,8 @@ m4_define([AT_FULL_COMPILE(c++)], m4_join([ ], [$1.cc], m4_ifval($2, [[$1-$2.cc]]), - m4_ifval($3, [[$1-$3.cc]])))]) + m4_ifval($3, [[$1-$3.cc]])), + [$4])]) m4_define([AT_FULL_COMPILE(java)], [AT_BISON_CHECK([-o $1.java $1.y]) @@ -833,7 +835,8 @@ m4_define([AT_FULL_COMPILE(java)], m4_join([ ], [$1.java], m4_ifval($2, [[$1-$2.java]]), - m4_ifval($3, [[$1-$3.java]])))]) + m4_ifval($3, [[$1-$3.java]])), + [$4])])