Since r268321 we can call digest_init even in a template, when the compound literal isn't instantiation-dependent. Consequently, when we get to case RANGE_FOR_STMT in tsubst_expr, RANGE_FOR_EXPR might already have been digested, as in this case, where before digesting it was
{*((struct S *) this)->r} and now it's TARGET_EXPR <D.2334, {.r=(struct R &) (struct R *) ((struct S *) this)->r}> (that's correct). Now, in tsubst_expr, we recurse on the latter: 17095 expr = RECUR (RANGE_FOR_EXPR (t)); and since the expression contains a COMPONENT_REF, we end up calling finish_non_static_data_member which calls build_class_member_access_expr with preserve_reference=false. Thus, after we've tsubst'd the RANGE_FOR_EXPR, we have TARGET_EXPR <D.2344, {.r=(struct R &) (struct R *) (struct R &) (struct R *) *((struct S *) this)->r}> Nevermind those casts, but "(struct R *) *((struct S *) this)->r" causes problems later in cp_fold -> cp_convert_to_pointer because we're trying to convert "*((struct S *) this)->r" of type R to R *. That errors and the error_mark_node causes grief. My first attempt was to handle this in tsubst_copy_and_build's case COMPONENT_REF, after the call to finish_non_static_data_member, but that breaks -- we can't have a LHS of a MODIFY_EXPR of a reference type. So it seems this needs to be handled closer to where it actually fails, for instance in ocp_convert. Bootstrapped/regtested on x86_64-linux, ok for trunk? 2019-02-07 Marek Polacek <pola...@redhat.com> PR c++/89217 - ICE with list-initialization in range-based for loop. * cvt.c (ocp_convert): Unwrap REFERENCE_REF_P in a COMPONENT_REF. * g++.dg/cpp0x/range-for37.C: New test. diff --git gcc/cp/cvt.c gcc/cp/cvt.c index 82a44f353c7..92677cff0c5 100644 --- gcc/cp/cvt.c +++ gcc/cp/cvt.c @@ -857,7 +857,15 @@ ocp_convert (tree type, tree expr, int convtype, int flags, return ignore_overflows (converted, e); } if (INDIRECT_TYPE_P (type) || TYPE_PTRMEM_P (type)) - return cp_convert_to_pointer (type, e, dofold, complain); + { + /* Undo what finish_non_static_data_member might have done, i.e. + turning e.g. (R *)((S *)this)->r into (R *)*((S *)this)->r, + rendering the conversion invalid. */ + if (REFERENCE_REF_P (e) + && TREE_CODE (TREE_OPERAND (e, 0)) == COMPONENT_REF) + e = TREE_OPERAND (e, 0); + return cp_convert_to_pointer (type, e, dofold, complain); + } if (code == VECTOR_TYPE) { tree in_vtype = TREE_TYPE (e); diff --git gcc/testsuite/g++.dg/cpp0x/range-for37.C gcc/testsuite/g++.dg/cpp0x/range-for37.C new file mode 100644 index 00000000000..d5c7c091d96 --- /dev/null +++ gcc/testsuite/g++.dg/cpp0x/range-for37.C @@ -0,0 +1,24 @@ +// PR c++/89217 +// { dg-do compile { target c++11 } } + +struct R {}; + +struct C +{ + R* begin() const { return &r; } + R* end() const { return &r; } + + R& r; +}; + +struct S +{ + void f1() { f2<true>(); } + R& r; + + template<bool> + void f2() + { + for (auto i : C{r}) {} + } +};