The issue here was that we've been trying to treat C++ list-initialized temporaries (which are rvalues) the same as C99 compound literals (which are lvalues). This patch distinguishes between them so we can treat them each correctly.
This leaves open the question of how compound literals ought to work in C++; currently we mostly treat them as rvalues, like other temporaries, except for constant arrays, which we treat as lvalues, as in C. Now that we distinguish between C and C++ syntax for list-initialization of a temporary object, we could go back to treating all C-style compound literals as lvalues for better C compatibility. Or we could decide that following the C++ temporary model consistently is more important. But this patch doesn't do either. Tested x86_64-pc-linux-gnu, applying to trunk.
commit 929e7c870d235fea4a475928001f073b76829580 Author: Jason Merrill <ja...@redhat.com> Date: Mon Apr 17 10:30:15 2017 -0400 PR c++/70167 - array prvalue treated as lvalue * cp-tree.h (CONSTRUCTOR_C99_COMPOUND_LITERAL): New. (enum fcl_t): New. * semantics.c (finish_compound_literal): Add fcl_context parameter. Only make a static variable for C99 syntax. * parser.c (cp_parser_postfix_expression): Pass it. * pt.c (tsubst_copy_and_build): Likewise. * call.c (extend_ref_init_temps): Set DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P. diff --git a/gcc/cp/call.c b/gcc/cp/call.c index d9accd1..dee236e 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -10516,6 +10516,9 @@ extend_ref_init_temps (tree decl, tree init, vec<tree, va_gc> **cleanups) FOR_EACH_VEC_SAFE_ELT (elts, i, p) p->value = extend_ref_init_temps (decl, p->value, cleanups); } + recompute_constructor_flags (ctor); + if (decl_maybe_constant_var_p (decl) && TREE_CONSTANT (ctor)) + DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl) = true; } } diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index b64fa6d..100f85c 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -369,6 +369,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX]; DECL_NON_TRIVIALLY_INITIALIZED_P (in VAR_DECL) CALL_EXPR_ORDERED_ARGS (in CALL_EXPR, AGGR_INIT_EXPR) DECLTYPE_FOR_REF_CAPTURE (in DECLTYPE_TYPE) + CONSTUCTOR_C99_COMPOUND_LITERAL (in CONSTRUCTOR) 4: TREE_HAS_CONSTRUCTOR (in INDIRECT_REF, SAVE_EXPR, CONSTRUCTOR, CALL_EXPR, or FIELD_DECL). IDENTIFIER_TYPENAME_P (in IDENTIFIER_NODE) @@ -3898,6 +3899,11 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter) #define CONSTRUCTOR_MUTABLE_POISON(NODE) \ (TREE_LANG_FLAG_2 (CONSTRUCTOR_CHECK (NODE))) +/* True if this typed CONSTRUCTOR represents C99 compound-literal syntax rather + than C++11 functional cast syntax. */ +#define CONSTRUCTOR_C99_COMPOUND_LITERAL(NODE) \ + (TREE_LANG_FLAG_3 (CONSTRUCTOR_CHECK (NODE))) + #define DIRECT_LIST_INIT_P(NODE) \ (BRACE_ENCLOSED_INITIALIZER_P (NODE) && CONSTRUCTOR_IS_DIRECT_INIT (NODE)) @@ -6483,7 +6489,10 @@ extern tree finish_this_expr (void); extern tree finish_pseudo_destructor_expr (tree, tree, tree, location_t); extern cp_expr finish_unary_op_expr (location_t, enum tree_code, cp_expr, tsubst_flags_t); -extern tree finish_compound_literal (tree, tree, tsubst_flags_t); +/* Whether this call to finish_compound_literal represents a C++11 functional + cast or a C99 compound literal. */ +enum fcl_t { fcl_functional, fcl_c99 }; +extern tree finish_compound_literal (tree, tree, tsubst_flags_t, fcl_t = fcl_functional); extern tree finish_fname (tree); extern void finish_translation_unit (void); extern tree finish_template_type_parm (tree, tree); diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index ab56f12..1951452 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -6770,7 +6770,7 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p, /* Form the representation of the compound-literal. */ postfix_expression = finish_compound_literal (type, initializer, - tf_warning_or_error); + tf_warning_or_error, fcl_c99); postfix_expression.set_location (initializer.get_location ()); break; } @@ -26834,7 +26834,7 @@ cp_parser_functional_cast (cp_parser* parser, tree type) type = TREE_TYPE (type); cast = finish_compound_literal (type, expression_list, - tf_warning_or_error); + tf_warning_or_error, fcl_functional); /* Create a location of the form: type_name{i, f} ^~~~~~~~~~~~~~~ diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index b42b1fc..a4a0d83 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -17709,7 +17709,12 @@ tsubst_copy_and_build (tree t, CONSTRUCTOR_IS_DIRECT_INIT (r) = CONSTRUCTOR_IS_DIRECT_INIT (t); if (TREE_HAS_CONSTRUCTOR (t)) - RETURN (finish_compound_literal (type, r, complain)); + { + fcl_t cl = fcl_functional; + if (CONSTRUCTOR_C99_COMPOUND_LITERAL (t)) + cl = fcl_c99; + RETURN (finish_compound_literal (type, r, complain, cl)); + } TREE_TYPE (r) = type; RETURN (r); diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index b15763d..238dfff 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -2647,12 +2647,14 @@ finish_unary_op_expr (location_t op_loc, enum tree_code code, cp_expr expr, return result; } -/* Finish a compound-literal expression. TYPE is the type to which - the CONSTRUCTOR in COMPOUND_LITERAL is being cast. */ +/* Finish a compound-literal expression or C++11 functional cast with aggregate + initializer. TYPE is the type to which the CONSTRUCTOR in COMPOUND_LITERAL + is being cast. */ tree finish_compound_literal (tree type, tree compound_literal, - tsubst_flags_t complain) + tsubst_flags_t complain, + fcl_t fcl_context) { if (type == error_mark_node) return error_mark_node; @@ -2661,7 +2663,7 @@ finish_compound_literal (tree type, tree compound_literal, { compound_literal = finish_compound_literal (TREE_TYPE (type), compound_literal, - complain); + complain, fcl_context); return cp_build_c_cast (type, compound_literal, complain); } @@ -2682,6 +2684,8 @@ finish_compound_literal (tree type, tree compound_literal, TREE_TYPE (compound_literal) = type; /* Mark the expression as a compound literal. */ TREE_HAS_CONSTRUCTOR (compound_literal) = 1; + if (fcl_context == fcl_c99) + CONSTRUCTOR_C99_COMPOUND_LITERAL (compound_literal) = 1; return compound_literal; } @@ -2717,10 +2721,17 @@ finish_compound_literal (tree type, tree compound_literal, compound_literal = digest_init_flags (type, compound_literal, LOOKUP_NORMAL, complain); if (TREE_CODE (compound_literal) == CONSTRUCTOR) - TREE_HAS_CONSTRUCTOR (compound_literal) = true; + { + TREE_HAS_CONSTRUCTOR (compound_literal) = true; + if (fcl_context == fcl_c99) + CONSTRUCTOR_C99_COMPOUND_LITERAL (compound_literal) = 1; + } /* Put static/constant array temporaries in static variables. */ + /* FIXME all C99 compound literals should be variables rather than C++ + temporaries, unless they are used as an aggregate initializer. */ if ((!at_function_scope_p () || CP_TYPE_CONST_P (type)) + && fcl_context == fcl_c99 && TREE_CODE (type) == ARRAY_TYPE && !TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type) && initializer_constant_valid_p (compound_literal, type)) diff --git a/gcc/testsuite/g++.dg/cpp0x/initlist-array6.C b/gcc/testsuite/g++.dg/cpp0x/initlist-array6.C new file mode 100644 index 0000000..fdfde0a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/initlist-array6.C @@ -0,0 +1,11 @@ +// PR c++/70167 +// { dg-do compile { target c++11 } } + +template<class T, unsigned S> void f(T(&&)[S]) { } + +using arr = const int[2]; + +int main() +{ + f(arr{1, 2}); +}