lalr1.cc: support move semantics

Modern C++ (i.e., C++11 and later) introduced "move only" types: types such
as std::unique_ptr<T> that can never be duplicated.  They must never be
copied (by assignments and constructors), they must be "moved".  The
implementation of lalr1.cc used to copy symbols (including their semantic
values).  This commit ensures that values are only moved in modern C++, yet
remain compatible with C++98/C++03.

Suggested by Frank Heckenbach, who provided a full implementation on
top of C++17's std::variant.
See http://lists.gnu.org/archive/html/bug-bison/2018-03/msg00002.html,
and https://lists.gnu.org/archive/html/bison-patches/2018-04/msg00002.html.

Symbols (terminal/non terminal) are handled by several functions that used
to take const-refs, which resulted eventually in a copy pushed on the stack.
With modern C++ (C++11 and later) the callers must use std::move, and the
callees must take their arguments as rvalue refs (foo&&).  In order to avoid
duplicating these functions to support both legacy C++ and modern C++, let's
introduce macros (YY_MOVE, YY_RVREF, etc.)  that rely on copy-semantics for
C++98/03, and move-semantics for modern C++.

That's easy for inner types, when the parser's functions pass arguments to
each other.  Functions facing the user (make_NUMBER, make_STRING, etc.)
should support both rvalue-refs (for instance to support move-only types:
make_INT (std::make_unique<int> (1))), and lvalue-refs (so that we can pass
a variable: make_INT (my_int)).  To avoid the multiplication of the
signatures (there is also the location), let's take the argument by value.

See:
https://lists.gnu.org/archive/html/bison-patches/2018-09/msg00024.html.

* data/c++.m4 (b4_cxx_portability): New.
(basic_symbol): In C++11, replace copy-ctors with move-ctors.
In C++11, replace copies with moves.
* data/lalr1.cc (stack_symbol_type, yypush_): Likewise.
Use YY_MOVE to avoid useless copies.
* data/variant.hh (variant): Support move-semantics.
(make_SYMBOL): In C++11, in order to support both read-only lvalues,
and rvalues, take the argument as a copy.
* data/stack.hh (yypush_): Use rvalue-refs in C++11.
* tests/c++.at: Use move semantics.

* tests/headers.at: Adjust to the new macros (YY_MOVE, etc.).

* configure.ac (CXX98_CXXFLAGS, CXX11_CXXFLAGS, CXX14_CXXFLAGS)
(CXX17_CXXFLAGS, ENABLE_CXX11): New.
* tests/atlocal.in: Receive them.

* examples/variant.yy: Don't define things in std.
* examples/variant-11.test, examples/variant-11.yy: New.
Check the support of move-only types.
* examples/README, examples/local.mk: Adjust.
This commit is contained in:
Akim Demaille
2018-08-12 18:05:47 +02:00
parent e3257f88e2
commit f19ecae3b2
13 changed files with 332 additions and 89 deletions

View File

@@ -153,13 +153,17 @@ m4_define([b4_shared_declarations],
# include <iostream>
# include <stdexcept>
# include <string>
# include <vector>]b4_defines_if([[
# include <vector>
]b4_cxx_portability[
]b4_defines_if([[
# include "stack.hh"
]b4_bison_locations_if([[# include "location.hh"]])])[
]b4_variant_if([b4_variant_includes])[
]b4_attribute_define[
]b4_null_define[
]b4_YYDEBUG_define[
]b4_namespace_open[
@@ -318,12 +322,12 @@ b4_location_define])])[
typedef basic_symbol<by_state> super_type;
/// Construct an empty symbol.
stack_symbol_type ();
/// Copy construct (for efficiency).
stack_symbol_type (const stack_symbol_type& that);
/// Move or copy construction.
stack_symbol_type (YY_RVREF (stack_symbol_type) that);
/// Steal the contents from \a sym to build this.
stack_symbol_type (state_type s, symbol_type& sym);
stack_symbol_type (state_type s, YY_MOVE_REF (symbol_type) sym);
/// Assignment, needed by push_back.
stack_symbol_type& operator= (stack_symbol_type& that);
stack_symbol_type& operator= (YY_MOVE_REF (stack_symbol_type) that);
};
/// Stack type.
@@ -337,7 +341,7 @@ b4_location_define])])[
/// if null, no trace is output.
/// \param sym the symbol
/// \warning the contents of \a s.value is stolen.
void yypush_ (const char* m, stack_symbol_type& sym);
void yypush_ (const char* m, YY_MOVE_REF (stack_symbol_type) sym);
/// Push a new look ahead token on the state on the stack.
/// \param m a debug message to display
@@ -345,7 +349,7 @@ b4_location_define])])[
/// \param s the state
/// \param sym the symbol (for its value and location).
/// \warning the contents of \a sym.value is stolen.
void yypush_ (const char* m, state_type s, symbol_type& sym);
void yypush_ (const char* m, state_type s, YY_MOVE_REF (symbol_type) sym);
/// Pop \a n symbols the three stacks.
void yypop_ (unsigned n = 1);
@@ -588,34 +592,33 @@ m4_if(b4_prefix, [yy], [],
]b4_parser_class_name[::stack_symbol_type::stack_symbol_type ()
{}
]b4_parser_class_name[::stack_symbol_type::stack_symbol_type (const stack_symbol_type& that)
: super_type (that.state]b4_variant_if([], [, that.value])[]b4_locations_if([, that.location])[)
]b4_parser_class_name[::stack_symbol_type::stack_symbol_type (YY_RVREF (stack_symbol_type) that)
: super_type (YY_MOVE (that.state)]b4_variant_if([], [, YY_MOVE (that.value)])b4_locations_if([, YY_MOVE (that.location)])[)
{]b4_variant_if([
b4_symbol_variant([that.type_get ()],
[value], [copy], [that.value])])[
[value], [YY_MOVE_OR_COPY], [YY_MOVE (that.value)])])[
}
]b4_parser_class_name[::stack_symbol_type::stack_symbol_type (state_type s, symbol_type& that)
: super_type (s]b4_variant_if([], [, that.value])[]b4_locations_if([, that.location])[)
]b4_parser_class_name[::stack_symbol_type::stack_symbol_type (state_type s, YY_MOVE_REF (symbol_type) that)
: super_type (s]b4_variant_if([], [, YY_MOVE (that.value)])[]b4_locations_if([, YY_MOVE (that.location)])[)
{]b4_variant_if([
b4_symbol_variant([that.type_get ()],
[value], [move], [that.value])])[
[value], [move], [YY_MOVE (that.value)])])[
// that is emptied.
that.type = empty_symbol;
}
]b4_parser_class_name[::stack_symbol_type&
]b4_parser_class_name[::stack_symbol_type::operator= (stack_symbol_type& that)
]b4_parser_class_name[::stack_symbol_type::operator= (YY_MOVE_REF (stack_symbol_type) that)
{
state = that.state;
]b4_variant_if([b4_symbol_variant([that.type_get ()],
[value], [move], [that.value])],
[[value = that.value;]])[]b4_locations_if([
location = that.location;])[
[value], [move], [YY_MOVE (that.value)])],
[[value = YY_MOVE (that.value);]])[]b4_locations_if([
location = YY_MOVE (that.location);])[
return *this;
}
template <typename Base>
void
]b4_parser_class_name[::yy_destroy_ (const char* yymsg, basic_symbol<Base>& yysym) const
@@ -649,18 +652,22 @@ m4_if(b4_prefix, [yy], [],
#endif
void
]b4_parser_class_name[::yypush_ (const char* m, stack_symbol_type& sym)
]b4_parser_class_name[::yypush_ (const char* m, YY_MOVE_REF (stack_symbol_type) sym)
{
if (m)
YY_SYMBOL_PRINT (m, sym);
yystack_.push (sym);
yystack_.push (YY_MOVE (sym));
}
void
]b4_parser_class_name[::yypush_ (const char* m, state_type s, symbol_type& sym)
]b4_parser_class_name[::yypush_ (const char* m, state_type s, YY_MOVE_REF (symbol_type) sym)
{
stack_symbol_type t (s, sym);
yypush_ (m, t);
#if defined __cplusplus && 201103L <= __cplusplus
yypush_ (m, stack_symbol_type (s, YY_MOVE (sym)));
#else
stack_symbol_type ss(s, sym);
yypush_ (m, ss);
#endif
}
void
@@ -756,7 +763,7 @@ b4_dollar_popdef])[]dnl
location values to have been already stored, initialize these
stacks with a primary value. */
yystack_.clear ();
yypush_ (YY_NULLPTR, 0, yyla);
yypush_ (YY_NULLPTR, 0, YY_MOVE (yyla));
// A new symbol was pushed on the stack.
yynewstate:
@@ -818,7 +825,7 @@ b4_dollar_popdef])[]dnl
--yyerrstatus_;
// Shift the lookahead token.
yypush_ ("Shifting", yyn, yyla);
yypush_ ("Shifting", yyn, YY_MOVE (yyla));
goto yynewstate;
/*-----------------------------------------------------------.
@@ -887,7 +894,7 @@ b4_dollar_popdef])[]dnl
YY_STACK_PRINT ();
// Shift the result of the reduction.
yypush_ (YY_NULLPTR, yylhs);
yypush_ (YY_NULLPTR, YY_MOVE (yylhs));
}
goto yynewstate;
@@ -976,7 +983,7 @@ b4_dollar_popdef])[]dnl
// Shift the error token.
error_token.state = yyn;
yypush_ ("Shifting", error_token);
yypush_ ("Shifting", YY_MOVE (error_token));
}
goto yynewstate;