There were two problems with this testcase: 1) In a template non-dependent context, we try to do full semantic processing and then roll back and build an expression with the original operands and the computed type. This was failing with a CONSTRUCTOR operand, since digest_init changes the contents of an input CONSTRUCTOR rather than return a new one, so the original new-initializer got clobbered. Fixed by copying CONSTRUCTORs as well as the init vector itself.
2) We weren't replacing PLACEHOLDER_EXPR in a new-initializer. And properly doing that replacement prevents us from doing the optimization of evaluating the initializer before allocating the object, since the initializer needs to refer to the object. Tested x86_64-pc-linux-gnu, applying to trunk.
commit f7187e708e1ccf76cbc49df60584b81e07c5f863 Author: Jason Merrill <ja...@redhat.com> Date: Sat Feb 11 07:45:05 2017 -0500 PR c++/77659 - ICE with new and C++14 aggregate NSDMI * init.c (build_new): Make backups of any CONSTRUCTORs in init. (build_new_1): Use replace_placeholders. * tree.c (replace_placeholders_t): Also track whether we've seen a placeholder. (replace_placeholders, replace_placeholders_r): Adjust. * cp-tree.h: Adjust. diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 0146332..6675ee5 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6658,7 +6658,7 @@ extern tree array_type_nelts_total (tree); extern tree array_type_nelts_top (tree); extern tree break_out_target_exprs (tree); extern tree build_ctor_subob_ref (tree, tree, tree); -extern tree replace_placeholders (tree, tree); +extern tree replace_placeholders (tree, tree, bool * = NULL); extern tree get_type_decl (tree); extern tree decl_namespace_context (tree); extern bool decl_anon_ns_mem_p (const_tree); diff --git a/gcc/cp/init.c b/gcc/cp/init.c index 42f1c61..524c583 100644 --- a/gcc/cp/init.c +++ b/gcc/cp/init.c @@ -3282,7 +3282,19 @@ build_new_1 (vec<tree, va_gc> **placement, tree type, tree nelts, init_expr = cp_build_modify_expr (input_location, init_expr, INIT_EXPR, ie, complain); } - stable = stabilize_init (init_expr, &init_preeval_expr); + /* If the initializer uses C++14 aggregate NSDMI that refer to the + object being initialized, replace them now and don't try to + preevaluate. */ + bool had_placeholder = false; + if (cxx_dialect >= cxx14 + && !processing_template_decl + && TREE_CODE (init_expr) == INIT_EXPR) + TREE_OPERAND (init_expr, 1) + = replace_placeholders (TREE_OPERAND (init_expr, 1), + TREE_OPERAND (init_expr, 0), + &had_placeholder); + stable = (!had_placeholder + && stabilize_init (init_expr, &init_preeval_expr)); } if (init_expr == error_mark_node) @@ -3454,7 +3466,17 @@ build_new (vec<tree, va_gc> **placement, tree type, tree nelts, orig_placement = make_tree_vector_copy (*placement); orig_nelts = nelts; if (*init) - orig_init = make_tree_vector_copy (*init); + { + orig_init = make_tree_vector_copy (*init); + /* Also copy any CONSTRUCTORs in *init, since reshape_init and + digest_init clobber them in place. */ + for (unsigned i = 0; i < orig_init->length(); ++i) + { + tree e = (**init)[i]; + if (TREE_CODE (e) == CONSTRUCTOR) + (**init)[i] = copy_node (e); + } + } make_args_non_dependent (*placement); if (nelts) diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index 56c4bba..d3c63b8 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -2728,13 +2728,20 @@ build_ctor_subob_ref (tree index, tree type, tree obj) return obj; } +struct replace_placeholders_t +{ + tree obj; /* The object to be substituted for a PLACEHOLDER_EXPR. */ + bool seen; /* Whether we've encountered a PLACEHOLDER_EXPR. */ +}; + /* Like substitute_placeholder_in_expr, but handle C++ tree codes and build up subexpressions as we go deeper. */ static tree replace_placeholders_r (tree* t, int* walk_subtrees, void* data_) { - tree obj = static_cast<tree>(data_); + replace_placeholders_t *d = static_cast<replace_placeholders_t*>(data_); + tree obj = d->obj; if (TREE_CONSTANT (*t)) { @@ -2753,6 +2760,7 @@ replace_placeholders_r (tree* t, int* walk_subtrees, void* data_) gcc_assert (TREE_CODE (x) == COMPONENT_REF); *t = x; *walk_subtrees = false; + d->seen = true; } break; @@ -2778,9 +2786,10 @@ replace_placeholders_r (tree* t, int* walk_subtrees, void* data_) if (TREE_CODE (*valp) == TARGET_EXPR) valp = &TARGET_EXPR_INITIAL (*valp); } - + d->obj = subob; cp_walk_tree (valp, replace_placeholders_r, - subob, NULL); + data_, NULL); + d->obj = obj; } *walk_subtrees = false; break; @@ -2794,12 +2803,15 @@ replace_placeholders_r (tree* t, int* walk_subtrees, void* data_) } tree -replace_placeholders (tree exp, tree obj) +replace_placeholders (tree exp, tree obj, bool *seen_p) { tree *tp = &exp; + replace_placeholders_t data = { obj, false }; if (TREE_CODE (exp) == TARGET_EXPR) tp = &TARGET_EXPR_INITIAL (exp); - cp_walk_tree (tp, replace_placeholders_r, obj, NULL); + cp_walk_tree (tp, replace_placeholders_r, &data, NULL); + if (seen_p) + *seen_p = data.seen; return exp; } diff --git a/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr6.C b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr6.C new file mode 100644 index 0000000..83fdedd --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr6.C @@ -0,0 +1,14 @@ +// PR c++/77659 +// { dg-do compile { target c++14 } } + +template <typename Type> Type get_max_value(Type); +struct A { + struct B { + int baz = get_max_value(baz); + }; + template <typename> void m_fn1() { new B{}; } +}; +void foo() { + A a; + a.m_fn1<int>(); +}