On Fri, Mar 29, 2019 at 05:29:27PM -0400, Jason Merrill wrote: > On 3/29/19 4:25 PM, Marek Polacek wrote: > > On Thu, Mar 28, 2019 at 02:55:55PM -0400, Jason Merrill wrote: > > > On 3/27/19 5:45 PM, Marek Polacek wrote: > > > > Here we have a non-dependent constructor in a template: > > > > > > > > { VIEW_CONVERT_EXPR<const A>(j) } > > > > > > > > In digest_init we call massage_init_elt, which calls digest_init_r on > > > > the > > > > element. We convert the element, but we're in a template, so > > > > perform_implicit_conversion added an IMPLICIT_CONV_EXPR around it. And > > > > then > > > > massage_init_elt calls maybe_constant_init on the element and the usual > > > > sadness > > > > ensues. > > > > > > Only after fold_non_dependent_expr. Perhaps we want a > > > fold_non_dependent_init? > > > > Yeah, I recall we talked about adding fold_non_dependent_init a year ago. > > > > Using it here works -- in a template, we won't call maybe_constant_*, so > > there's no crash. > > > > Bootstrapped/regtested on x86_64-linux, ok for trunk? > > > > 2019-03-29 Marek Polacek <pola...@redhat.com> > > > > PR c++/89852 - ICE with C++11 functional cast with { }. > > * constexpr.c (fold_non_dependent_expr_template): New static function > > broken out of... > > (fold_non_dependent_expr): ...here. > > (fold_non_dependent_init): New function. > > * cp-tree.h (fold_non_dependent_init): Declare. > > * typeck2.c (massage_init_elt): Call fold_non_dependent_init instead > > of fold_non_dependent_expr. Don't call maybe_constant_init. > > > > @@ -5604,51 +5656,29 @@ fold_non_dependent_expr (tree t, > > if (t == NULL_TREE) > > return NULL_TREE; > > + if (processing_template_decl) > > + return fold_non_dependent_expr_template (t, complain, > > + manifestly_const_eval); > > + return maybe_constant_value (t, NULL_TREE, manifestly_const_eval); > > +} > > +tree > > +fold_non_dependent_init (tree t, > > + tsubst_flags_t complain /*=tf_warning_or_error*/, > > + bool manifestly_const_eval /*=false*/) > > +{ > > + if (t == NULL_TREE) > > + return NULL_TREE; > > + if (processing_template_decl) > > + return fold_non_dependent_expr_template (t, complain, > > + manifestly_const_eval); > > Don't we still need the maybe_constant_init TARGET_EXPR stripping behavior > in a template?
It would seem that we don't need it, since nothing broke. But this patch includes the stripping. Bootstrap/regtest running on x86_64-linux, ok for trunk? 2019-03-29 Marek Polacek <pola...@redhat.com> PR c++/89852 - ICE with C++11 functional cast with { }. * constexpr.c (fold_non_dependent_expr_template): New static function broken out of... (fold_non_dependent_expr): ...here. (fold_non_dependent_init): New function. * cp-tree.h (fold_non_dependent_init): Declare. * typeck2.c (massage_init_elt): Call fold_non_dependent_init instead of fold_non_dependent_expr. Don't call maybe_constant_init. * g++.dg/cpp0x/initlist115.C: New test. diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c index daf34e10784..53854a8acd4 100644 --- gcc/cp/constexpr.c +++ gcc/cp/constexpr.c @@ -5581,6 +5581,58 @@ clear_cv_and_fold_caches (void) clear_fold_cache (); } +/* Internal function handling expressions in templates for + fold_non_dependent_expr and fold_non_dependent_init. + + If we're in a template, but T isn't value dependent, simplify + it. We're supposed to treat: + + template <typename T> void f(T[1 + 1]); + template <typename T> void f(T[2]); + + as two declarations of the same function, for example. */ + +static tree +fold_non_dependent_expr_template (tree t, tsubst_flags_t complain, + bool manifestly_const_eval) +{ + gcc_assert (processing_template_decl); + + if (is_nondependent_constant_expression (t)) + { + processing_template_decl_sentinel s; + t = instantiate_non_dependent_expr_internal (t, complain); + + if (type_unknown_p (t) || BRACE_ENCLOSED_INITIALIZER_P (t)) + { + if (TREE_OVERFLOW_P (t)) + { + t = build_nop (TREE_TYPE (t), t); + TREE_CONSTANT (t) = false; + } + return t; + } + + tree r = cxx_eval_outermost_constant_expr (t, true, true, + manifestly_const_eval, + NULL_TREE); + /* cp_tree_equal looks through NOPs, so allow them. */ + gcc_checking_assert (r == t + || CONVERT_EXPR_P (t) + || TREE_CODE (t) == VIEW_CONVERT_EXPR + || (TREE_CONSTANT (t) && !TREE_CONSTANT (r)) + || !cp_tree_equal (r, t)); + return r; + } + else if (TREE_OVERFLOW_P (t)) + { + t = build_nop (TREE_TYPE (t), t); + TREE_CONSTANT (t) = false; + } + + return t; +} + /* Like maybe_constant_value but first fully instantiate the argument. Note: this is equivalent to instantiate_non_dependent_expr_sfinae @@ -5604,51 +5656,39 @@ fold_non_dependent_expr (tree t, if (t == NULL_TREE) return NULL_TREE; - /* If we're in a template, but T isn't value dependent, simplify - it. We're supposed to treat: + if (processing_template_decl) + return fold_non_dependent_expr_template (t, complain, + manifestly_const_eval); - template <typename T> void f(T[1 + 1]); - template <typename T> void f(T[2]); + return maybe_constant_value (t, NULL_TREE, manifestly_const_eval); +} - as two declarations of the same function, for example. */ - if (processing_template_decl) - { - if (is_nondependent_constant_expression (t)) - { - processing_template_decl_sentinel s; - t = instantiate_non_dependent_expr_internal (t, complain); - if (type_unknown_p (t) - || BRACE_ENCLOSED_INITIALIZER_P (t)) - { - if (TREE_OVERFLOW_P (t)) - { - t = build_nop (TREE_TYPE (t), t); - TREE_CONSTANT (t) = false; - } - return t; - } +/* Like maybe_constant_init but first fully instantiate the argument. */ - tree r = cxx_eval_outermost_constant_expr (t, true, true, - manifestly_const_eval, - NULL_TREE); - /* cp_tree_equal looks through NOPs, so allow them. */ - gcc_checking_assert (r == t - || CONVERT_EXPR_P (t) - || TREE_CODE (t) == VIEW_CONVERT_EXPR - || (TREE_CONSTANT (t) && !TREE_CONSTANT (r)) - || !cp_tree_equal (r, t)); - return r; - } - else if (TREE_OVERFLOW_P (t)) +tree +fold_non_dependent_init (tree t, + tsubst_flags_t complain /*=tf_warning_or_error*/, + bool manifestly_const_eval /*=false*/) +{ + if (t == NULL_TREE) + return NULL_TREE; + + if (processing_template_decl) + { + t = fold_non_dependent_expr_template (t, complain, + manifestly_const_eval); + /* maybe_constant_init does this stripping, so do it here too. */ + if (TREE_CODE (t) == TARGET_EXPR) { - t = build_nop (TREE_TYPE (t), t); - TREE_CONSTANT (t) = false; + tree init = TARGET_EXPR_INITIAL (t); + if (TREE_CODE (init) == CONSTRUCTOR) + t = init; } return t; } - return maybe_constant_value (t, NULL_TREE, manifestly_const_eval); + return maybe_constant_init (t, NULL_TREE, manifestly_const_eval); } /* Like maybe_constant_value, but returns a CONSTRUCTOR directly, rather diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h index fd612b0dbb1..31218a79be7 100644 --- gcc/cp/cp-tree.h +++ gcc/cp/cp-tree.h @@ -7708,6 +7708,9 @@ extern tree maybe_constant_init (tree, tree = NULL_TREE, bool = false); extern tree fold_non_dependent_expr (tree, tsubst_flags_t = tf_warning_or_error, bool = false); +extern tree fold_non_dependent_init (tree, + tsubst_flags_t = tf_warning_or_error, + bool = false); extern tree fold_simple (tree); extern bool is_sub_constant_expr (tree); extern bool reduced_constant_expression_p (tree); diff --git gcc/cp/typeck2.c gcc/cp/typeck2.c index 7f242ba93da..fa98b1cb8b5 100644 --- gcc/cp/typeck2.c +++ gcc/cp/typeck2.c @@ -1346,8 +1346,7 @@ massage_init_elt (tree type, tree init, int nested, int flags, init = TARGET_EXPR_INITIAL (init); /* When we defer constant folding within a statement, we may want to defer this folding as well. */ - tree t = fold_non_dependent_expr (init, complain); - t = maybe_constant_init (t); + tree t = fold_non_dependent_init (init, complain); if (TREE_CONSTANT (t)) init = t; return init; diff --git gcc/testsuite/g++.dg/cpp0x/initlist115.C gcc/testsuite/g++.dg/cpp0x/initlist115.C new file mode 100644 index 00000000000..ee4b6d4a870 --- /dev/null +++ gcc/testsuite/g++.dg/cpp0x/initlist115.C @@ -0,0 +1,18 @@ +// PR c++/89852 +// { dg-do compile { target c++11 } } + +struct A { + int b; +}; + +struct B { + A g; +}; + +const auto j = A{}; + +template <typename> +void k() +{ + B{j}; +}