An internal compiler error occurs while compiling this template code: https://godbolt.org/z/rP9Wf97vP. Error message: <source>: In member function 'typename example::Ac<B>::gt<I>::k& tcc<A>::gr() [with int I = 1; A = {float, int}]': <source>:97:62: internal compiler error: in replace_placeholders_r, at cp/tree.c:3332 97 | return typename example::Ac<A...>::template gt<I>{k}.K; | ~~~~~~~~~^ 0x1cff079 internal_error(char const*, ...) ???:0 0x6bac69 fancy_abort(char const*, int, char const*) ???:0 0x13971f3 walk_tree_1(tree_node**, tree_node* (*)(tree_node**, int*, void*), void*, hash_set<tree_node*, false, default_hash_traits<tree_node*> >*, tree_node* (*)(tree_node**, int*, tree_node* (*)(tree_node**, int*, void*), void*, hash_set<tree_node*, false, default_hash_traits<tree_node*> >*)) ???:0
A more minimal case: https://godbolt.org/z/jxP9e35bz
Even more minimal case: https://godbolt.org/z/M3Tv9oqcn
template<int I> struct E{ int &K; decltype(E<I-1>::k) &k = E<I-1>{K}.k; }; template<> struct E<0>{ int &K; int &k = K; }; int main(){ int r; E<1>{r}.k = 7; }
Started with r216750.
Reduced: struct A { int x; int y = x; }; struct B { int x = 0; int y = A{x}.y; }; B b = {};
GCC 8 branch is being closed.
GCC 9.4 is being released, retargeting bugs to GCC 9.5.
This is tricky, because we end up with {.x=(&<PLACEHOLDER_EXPR struct B>)->x, .y=(&<PLACEHOLDER_EXPR struct A>)->x} that is, two PLACEHOLDER_EXPRs for different types on the same level in one { }, so our CONSTRUCTOR_PLACEHOLDER_BOUNDARY mechanism to avoid replacing unrelated PLACEHOLDER_EXPRs will not work.
When we are cp_parser_late_parsing_nsdmi for "int y = A{x}.y;" we perform finish_compound_literal on type=A, compound_literal={((struct B *) this)->x}. When digesting this initializer, we call get_nsdmi which creates a PLACEHOLDER_EXPR for A -- we don't have any object to refer to yet. After digesting, we have {.x=((struct B *) this)->x, .y=(&<PLACEHOLDER_EXPR struct A>)->x} and since we've created a PLACEHOLDER_EXPR inside it, we marked the whole ctor CONSTRUCTOR_PLACEHOLDER_BOUNDARY. f_c_l creates a TARGET_EXPR and returns TARGET_EXPR <D.2384, {.x=((struct B *) this)->x, .y=(&<PLACEHOLDER_EXPR struct A>)->x}> (*) Then we get to B b = {}; and call store_init_value, which digest the {}, which produces {.x=NON_LVALUE_EXPR <0>, .y=(TARGET_EXPR <D.2395, {.x=(&<PLACEHOLDER_EXPR struct B>)->x, .y=(&<PLACEHOLDER_EXPR struct A>)->x}>).y} The call to replace_placeholders in store_init_value will not do anything: we've marked the inner { } CONSTRUCTOR_PLACEHOLDER_BOUNDARY, and it's only a sub-expression, so replace_placeholders does nothing, so the <PLACEHOLDER_EXPR struct B> stays even though now it was the perfect time to replace it because we have an object for it: 'b'. Later, in cp_gimplify_init_expr the *expr_p is D.2395 = {.x=(&<PLACEHOLDER_EXPR struct B>)->x, .y=(&<PLACEHOLDER_EXPR struct A>)->x} where D.2395 is of type A, but we crash because we hit <PLACEHOLDER_EXPR struct B>, which has a different type. Now here's an idea how we could fix this: at the (*) point in finish_compound_literal we could replace <PLACEHOLDER_EXPR struct A> with D.2384 because now we have an object! Then clear CONSTRUCTOR_PLACEHOLDER_BOUNDARY, because we no longer have a PLACEHOLDER_EXPR in the {}. Then store_init_value will be able to replace <PLACEHOLDER_EXPR struct B> with 'b', and we should be good to go.
Proof of concept: --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -3296,6 +3296,14 @@ finish_compound_literal (tree type, tree compound_literal, if (TREE_CODE (compound_literal) == CONSTRUCTOR) TREE_HAS_CONSTRUCTOR (compound_literal) = false; compound_literal = get_target_expr_sfinae (compound_literal, complain); + if (parsing_nsdmi ()) + { + tree obj = TARGET_EXPR_SLOT (compound_literal); + tree &ctor = TARGET_EXPR_INITIAL (compound_literal); + replace_placeholders (compound_literal, obj); + if (TREE_CODE (ctor) == CONSTRUCTOR) + CONSTRUCTOR_PLACEHOLDER_BOUNDARY (ctor) = false; + } } else /* For e.g. int{42} just make sure it's a prvalue. */
The trunk branch has been updated by Marek Polacek <mpolacek@gcc.gnu.org>: https://gcc.gnu.org/g:1b661f3f5e712c951e774b3b91fffe4dac734cc7 commit r13-765-g1b661f3f5e712c951e774b3b91fffe4dac734cc7 Author: Marek Polacek <polacek@redhat.com> Date: Tue Apr 26 15:52:00 2022 -0400 c++: ICE with temporary of class type in DMI [PR100252] Consider struct A { int x; int y = x; }; struct B { int x = 0; int y = A{x}.y; // #1 }; where for #1 we end up with {.x=(&<PLACEHOLDER_EXPR struct B>)->x, .y=(&<PLACEHOLDER_EXPR struct A>)->x} that is, two PLACEHOLDER_EXPRs for different types on the same level in a {}. This crashes because our CONSTRUCTOR_PLACEHOLDER_BOUNDARY mechanism to avoid replacing unrelated PLACEHOLDER_EXPRs cannot deal with it. Here's why we wound up with those PLACEHOLDER_EXPRs: When we're performing cp_parser_late_parsing_nsdmi for "int y = A{x}.y;" we use finish_compound_literal on type=A, compound_literal={((struct B *) this)->x}. When digesting this initializer, we call get_nsdmi which creates a PLACEHOLDER_EXPR for A -- we don't have any object to refer to yet. After digesting, we have {.x=((struct B *) this)->x, .y=(&<PLACEHOLDER_EXPR struct A>)->x} and since we've created a PLACEHOLDER_EXPR inside it, we marked the whole ctor CONSTRUCTOR_PLACEHOLDER_BOUNDARY. f_c_l creates a TARGET_EXPR and returns TARGET_EXPR <D.2384, {.x=((struct B *) this)->x, .y=(&<PLACEHOLDER_EXPR struct A>)->x}> Then we get to B b = {}; and call store_init_value, which digests the {}, which produces {.x=NON_LVALUE_EXPR <0>, .y=(TARGET_EXPR <D.2395, {.x=(&<PLACEHOLDER_EXPR struct B>)->x, .y=(&<PLACEHOLDER_EXPR struct A>)->x}>).y} lookup_placeholder in constexpr won't find an object to replace the PLACEHOLDER_EXPR for B, because ctx->object will be D.2395 of type A, and we cannot search outward from D.2395 to find 'b'. The call to replace_placeholders in store_init_value will not do anything: we've marked the inner { } CONSTRUCTOR_PLACEHOLDER_BOUNDARY, and it's only a sub-expression, so replace_placeholders does nothing, so the <P_E struct B> stays even though now is the perfect time to replace it because we have an object for it: 'b'. Later, in cp_gimplify_init_expr the *expr_p is D.2395 = {.x=(&<PLACEHOLDER_EXPR struct B>)->x, .y=(&<PLACEHOLDER_EXPR struct A>)->x} where D.2395 is of type A, but we crash because we hit <P_E struct B>, which has a different type. My idea was to replace <P_E struct A> with D.2384 after creating the TARGET_EXPR because that means we have an object we can refer to. Then clear CONSTRUCTOR_PLACEHOLDER_BOUNDARY because we no longer have a PLACEHOLDER_EXPR in the {}. Then store_init_value will be able to replace <P_E struct B> with 'b', and we should be good to go. We must be careful not to break guaranteed copy elision, so this replacement happens in digest_nsdmi_init where we can see the whole initializer, and avoid replacing any placeholders in TARGET_EXPRs used in the context of initialization/copy elision. This is achieved via the new function called potential_prvalue_result_of. While fixing this problem, I found PR105550, thus the FIXMEs in the tests. PR c++/100252 gcc/cp/ChangeLog: * typeck2.cc (potential_prvalue_result_of): New. (replace_placeholders_for_class_temp_r): New. (digest_nsdmi_init): Call it. gcc/testsuite/ChangeLog: * g++.dg/cpp1y/nsdmi-aggr14.C: New test. * g++.dg/cpp1y/nsdmi-aggr15.C: New test. * g++.dg/cpp1y/nsdmi-aggr16.C: New test. * g++.dg/cpp1y/nsdmi-aggr17.C: New test. * g++.dg/cpp1y/nsdmi-aggr18.C: New test. * g++.dg/cpp1y/nsdmi-aggr19.C: New test.
Fixed on trunk so far.
GCC 9 branch is being closed
GCC 10.4 is being released, retargeting bugs to GCC 10.5.
GCC 10 branch is being closed.