C++ PATCH for c++/89217 - ICE with list-initialization in range-based for loop

Marek Polacek polacek@redhat.com
Thu Feb 7 23:03:00 GMT 2019


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  <polacek@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}) {}
+    }
+};



More information about the Gcc-patches mailing list