From 3eb9042a30b6daac3e8a00444ba707f929d2b640 Mon Sep 17 00:00:00 2001 From: Akim Demaille Date: Tue, 18 Sep 2018 19:57:32 +0200 Subject: [PATCH] c++: introduce api.value.automove Based on work by Frank Heckenbach. See http://lists.gnu.org/archive/html/bug-bison/2018-04/msg00000.html and http://lists.gnu.org/archive/html/bug-bison/2018-09/msg00019.html. * data/lalr1.cc (b4_rhs_value): Use YY_MOVE api.rhs.automove is set. * doc/bison.texi (%define Summary): Document api.rhs.automove. * examples/variant-11.yy: Use it. * tests/local.at (AT_AUTOMOVE_IF): New. * tests/c++.at (Variants): Check move semantics. --- data/lalr1.cc | 7 ++++- doc/bison.texi | 60 +++++++++++++++++++++++++++++++++++ examples/variant-11.yy | 5 +-- tests/c++.at | 71 +++++++++++++++++++++++++++++++++++++++++- tests/local.at | 2 ++ 5 files changed, 141 insertions(+), 4 deletions(-) diff --git a/data/lalr1.cc b/data/lalr1.cc index 1a5138bf..eb4d6ca0 100644 --- a/data/lalr1.cc +++ b/data/lalr1.cc @@ -83,9 +83,14 @@ m4_define([b4_rhs_state], # -------------------------------------- # Expansion of $NUM, where the current rule has RULE-LENGTH # symbols on RHS. -m4_define([b4_rhs_value], +m4_define([_b4_rhs_value], [b4_symbol_value([b4_rhs_data([$1], [$2]).value], [$3])]) +m4_define([b4_rhs_value], +[b4_percent_define_ifdef([api.value.automove], + [YY_MOVE(_b4_rhs_value($@))], + [_b4_rhs_value($@)])]) + # b4_rhs_location(RULE-LENGTH, NUM) # --------------------------------- diff --git a/doc/bison.texi b/doc/bison.texi index e486c213..3620c60f 100644 --- a/doc/bison.texi +++ b/doc/bison.texi @@ -5982,6 +5982,66 @@ introduced in Bison 3.0 @c api.token.prefix +@c ================================================== api.value.automove +@deffn Directive {%define api.value.automove} + +@itemize @bullet +@item Language(s): +C++ + +@item Purpose: +Let occurrences of semantic values of the right-hand sides of a rule be +implicitly turned in rvalues. When enabled, a grammar such as: + +@example +exp: + "number" @{ $$ = make_number ($1); @} +| exp "+" exp @{ $$ = make_binary (add, $1, $3); @} +| "(" exp ")" @{ $$ = $2; @} +@end example + +@noindent +is actually compiled as if you had written: + +@example +exp: + "number" @{ $$ = make_number (std::move ($1)); @} +| exp "+" exp @{ $$ = make_binary (add, + std::move ($1), + std::move ($3)); @} +| "(" exp ")" @{ $$ = std::move ($2); @} +@end example + +Using a value several times with automove enabled is typically an error. +For instance, instead of: + +@example +exp: "twice" exp @{ $$ = make_binary (add, $2, $2); @} +@end example + +@noindent +write: + +@example +exp: "twice" exp @{ auto v = $2; $$ = make_binary (add, v, v); @} +@end example + +@noindent +It is tempting to use @code{std::move} on one of the @code{v}, but the +argument evaluation order in C++ is unspecified. + +@item Accepted Values: +Boolean. + +@item Default Value: +@code{false} +@item History: +introduced in Bison 3.2 +@end itemize +@end deffn +@c api.value.automove + + @c ================================================== api.value.type @deffn Directive {%define api.value.type} @var{support} @deffnx Directive {%define api.value.type} @{@var{type}@} diff --git a/examples/variant-11.yy b/examples/variant-11.yy index c78bab07..81b0b1b9 100644 --- a/examples/variant-11.yy +++ b/examples/variant-11.yy @@ -20,6 +20,7 @@ %defines %define api.token.constructor %define api.value.type variant +%define api.value.automove %define parse.assert %locations @@ -96,11 +97,11 @@ result: list: %empty { /* Generates an empty string list */ } -| list item { $$ = std::move ($1); $$.emplace_back (std::move ($2)); } +| list item { $$ = $1; $$.emplace_back ($2); } ; item: - TEXT { $$ = std::move ($1); } + TEXT { $$ = $1; } | NUMBER { $$ = make_string_uptr (to_string ($1)); } ; %% diff --git a/tests/c++.at b/tests/c++.at index d6cca694..780a8415 100644 --- a/tests/c++.at +++ b/tests/c++.at @@ -241,6 +241,21 @@ AT_DATA_GRAMMAR([list.y], return *this; } +#if defined __cplusplus && 201103L <= __cplusplus + string (string&& s) + : val_(std::move(s.val_)) + { + s.val_.clear(); + } + + string& operator= (string&& s) + { + val_ = std::move(s.val_); + s.val_.clear (); + return *this; + } +#endif + friend std::ostream& operator<< (std::ostream& o, const string& s) { @@ -384,9 +399,60 @@ namespace yy ]AT_MAIN_DEFINE[ ]]) +AT_DATA_SOURCE([[modern.cc]], +[[#include +int main() +{ +#if defined __cplusplus && 201103L <= __cplusplus + std::cout << "Modern C++: " << __cplusplus << '\n'; + return 0; +#else + std::cout << "Legac++\n"; + return 1; +#endif +} +]]) + AT_FOR_EACH_CXX([ AT_FULL_COMPILE([list]) -AT_PARSER_CHECK([./list], 0, + +# Are we compiling with modern C++ enabled? +AT_COMPILE_CXX([modern]) +AT_CHECK([./modern], [ignore], [ignore]) +if test $at_status = 0; then + modern=true +else + modern=false +fi + +if AT_AUTOMOVE_IF([$modern], [false]); then + AT_PARSER_CHECK([./list], 0, +[[(0, 1, 2, 4, 6) +]], +[[Destroy: "" +Destroy: "" +Destroy: 1 +Destroy: "" +Destroy: () +Destroy: "" +Destroy: "" +Destroy: () +Destroy: "" +Destroy: 3 +Destroy: () +Destroy: "" +Destroy: "" +Destroy: () +Destroy: () +Destroy: 5 +Destroy: () +Destroy: "" +Destroy: "" +Destroy: () +Destroy: (0, 1, 2, 4, 6) +]]) +else + AT_PARSER_CHECK([./list], 0, [[(0, 1, 2, 4, 6) ]], [[Destroy: "0" @@ -411,6 +477,7 @@ Destroy: "6" Destroy: (0, 1, 2, 4) Destroy: (0, 1, 2, 4, 6) ]]) +fi ]) AT_BISON_OPTION_POPDEFS @@ -419,11 +486,13 @@ AT_CLEANUP AT_TEST([[%skeleton "lalr1.cc"]]) AT_TEST([[%skeleton "lalr1.cc" %define parse.assert]]) +AT_TEST([[%skeleton "lalr1.cc" %define parse.assert %define api.value.automove]]) AT_TEST([[%skeleton "lalr1.cc" %define parse.assert %locations]]) AT_TEST([[%skeleton "lalr1.cc" %define parse.assert %code {\n#define TWO_STAGE_BUILD\n}]]) AT_TEST([[%skeleton "lalr1.cc" %define parse.assert %define api.token.constructor]]) AT_TEST([[%skeleton "lalr1.cc" %define parse.assert %define api.token.constructor %define api.token.prefix {TOK_}]]) AT_TEST([[%skeleton "lalr1.cc" %define parse.assert %define api.token.constructor %define api.token.prefix {TOK_} %locations]]) +AT_TEST([[%skeleton "lalr1.cc" %define parse.assert %define api.token.constructor %define api.token.prefix {TOK_} %locations %define api.value.automove]]) m4_popdef([AT_TEST]) diff --git a/tests/local.at b/tests/local.at index 56f8e11a..9a0bec15 100644 --- a/tests/local.at +++ b/tests/local.at @@ -139,6 +139,8 @@ m4_define([AT_BISON_OPTION_PUSHDEFS], m4_define([_AT_BISON_OPTION_PUSHDEFS], [m4_if([$1$2], $[1]$[2], [], [m4_fatal([$0: Invalid arguments: $@])])dnl +m4_pushdef([AT_AUTOMOVE_IF], +[m4_bmatch([$3], [%define api\.value\.automove], [$1], [$2])]) m4_pushdef([AT_DEFINES_IF], [m4_bmatch([$3], [%defines], [$1], [$2])]) m4_pushdef([AT_DEBUG_IF],