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.
This commit is contained in:
Akim Demaille
2018-09-18 19:57:32 +02:00
parent aa5de5728c
commit 3eb9042a30
5 changed files with 141 additions and 4 deletions

View File

@@ -83,9 +83,14 @@ m4_define([b4_rhs_state],
# -------------------------------------- # --------------------------------------
# Expansion of $<TYPE>NUM, where the current rule has RULE-LENGTH # Expansion of $<TYPE>NUM, where the current rule has RULE-LENGTH
# symbols on RHS. # symbols on RHS.
m4_define([b4_rhs_value], m4_define([_b4_rhs_value],
[b4_symbol_value([b4_rhs_data([$1], [$2]).value], [$3])]) [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) # b4_rhs_location(RULE-LENGTH, NUM)
# --------------------------------- # ---------------------------------

View File

@@ -5982,6 +5982,66 @@ introduced in Bison 3.0
@c api.token.prefix @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 @c ================================================== api.value.type
@deffn Directive {%define api.value.type} @var{support} @deffn Directive {%define api.value.type} @var{support}
@deffnx Directive {%define api.value.type} @{@var{type}@} @deffnx Directive {%define api.value.type} @{@var{type}@}

View File

@@ -20,6 +20,7 @@
%defines %defines
%define api.token.constructor %define api.token.constructor
%define api.value.type variant %define api.value.type variant
%define api.value.automove
%define parse.assert %define parse.assert
%locations %locations
@@ -96,11 +97,11 @@ result:
list: list:
%empty { /* Generates an empty string list */ } %empty { /* Generates an empty string list */ }
| list item { $$ = std::move ($1); $$.emplace_back (std::move ($2)); } | list item { $$ = $1; $$.emplace_back ($2); }
; ;
item: item:
TEXT { $$ = std::move ($1); } TEXT { $$ = $1; }
| NUMBER { $$ = make_string_uptr (to_string ($1)); } | NUMBER { $$ = make_string_uptr (to_string ($1)); }
; ;
%% %%

View File

@@ -241,6 +241,21 @@ AT_DATA_GRAMMAR([list.y],
return *this; 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 friend
std::ostream& operator<< (std::ostream& o, const string& s) std::ostream& operator<< (std::ostream& o, const string& s)
{ {
@@ -384,8 +399,59 @@ namespace yy
]AT_MAIN_DEFINE[ ]AT_MAIN_DEFINE[
]]) ]])
AT_DATA_SOURCE([[modern.cc]],
[[#include <iostream>
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_FOR_EACH_CXX([
AT_FULL_COMPILE([list]) AT_FULL_COMPILE([list])
# 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, AT_PARSER_CHECK([./list], 0,
[[(0, 1, 2, 4, 6) [[(0, 1, 2, 4, 6)
]], ]],
@@ -411,6 +477,7 @@ Destroy: "6"
Destroy: (0, 1, 2, 4) Destroy: (0, 1, 2, 4)
Destroy: (0, 1, 2, 4, 6) Destroy: (0, 1, 2, 4, 6)
]]) ]])
fi
]) ])
AT_BISON_OPTION_POPDEFS AT_BISON_OPTION_POPDEFS
@@ -419,11 +486,13 @@ AT_CLEANUP
AT_TEST([[%skeleton "lalr1.cc"]]) AT_TEST([[%skeleton "lalr1.cc"]])
AT_TEST([[%skeleton "lalr1.cc" %define parse.assert]]) 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 %locations]])
AT_TEST([[%skeleton "lalr1.cc" %define parse.assert %code {\n#define TWO_STAGE_BUILD\n}]]) 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]])
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_}]])
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]])
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]) m4_popdef([AT_TEST])

View File

@@ -139,6 +139,8 @@ m4_define([AT_BISON_OPTION_PUSHDEFS],
m4_define([_AT_BISON_OPTION_PUSHDEFS], m4_define([_AT_BISON_OPTION_PUSHDEFS],
[m4_if([$1$2], $[1]$[2], [], [m4_if([$1$2], $[1]$[2], [],
[m4_fatal([$0: Invalid arguments: $@])])dnl [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_pushdef([AT_DEFINES_IF],
[m4_bmatch([$3], [%defines], [$1], [$2])]) [m4_bmatch([$3], [%defines], [$1], [$2])])
m4_pushdef([AT_DEBUG_IF], m4_pushdef([AT_DEBUG_IF],