diff mbox

fix c++/78551 ICE in constexpr

Message ID 4dc1a06a-35d5-0f01-5880-ecfb7a587d30@acm.org
State New
Headers show

Commit Message

Nathan Sidwell Dec. 7, 2016, 8:31 p.m. UTC
This patch fixes 78551.  The union was a red herring.  The underlying 
problem is that we first initialize a char array with a string constant, 
and then zap a random element of that array.  That zapping expects to 
see a CONSTRUCTOR, not a STRING_CST.  Fixed by expanding the STRING_CST 
into a CONSTRUCTOR for each element, when we encounter this situation.

Patch tested on HEAD and gcc-6 branch.  Is gcc-5 still live (testcase is 
marked as a  5/6/7 regression)?

nathan
-- 
Nathan Sidwell

Comments

Jason Merrill Dec. 8, 2016, 6:03 p.m. UTC | #1
On Wed, Dec 7, 2016 at 3:31 PM, Nathan Sidwell <nathan@acm.org> wrote:
> This patch fixes 78551.  The union was a red herring.  The underlying

> problem is that we first initialize a char array with a string constant, and

> then zap a random element of that array.  That zapping expects to see a

> CONSTRUCTOR, not a STRING_CST.  Fixed by expanding the STRING_CST into a

> CONSTRUCTOR for each element, when we encounter this situation.

>

> Patch tested on HEAD and gcc-6 branch.


OK.

> Is gcc-5 still live (testcase is marked as a  5/6/7 regression)?


It is; you can check gcc.gnu.org to see current release status.

Incidentally, would you mind naming your attachments .diff rather than
.patch?  Gmail mishandles the latter.

Jason
diff mbox

Patch

2016-12-07  Nathan Sidwell  <nathan@acm.org>

	PR c++/78551
	* constexpr.c (extract_string_elt): New.  Broken out of ...
	(cxx_eval_array_reference): ... here.  Call it.
	(cxx_eval_store_expression): Convert init by STRING_CST into
	CONSTRUCTOR, if needed.

	PR c++/78551
	* g++.dg/cpp1y/pr78551.C: New.

Index: cp/constexpr.c
===================================================================
--- cp/constexpr.c	(revision 243344)
+++ cp/constexpr.c	(working copy)
@@ -2149,6 +2149,27 @@  diag_array_subscript (const constexpr_ct
     }
 }
 
+/* Extract element INDEX consisting of CHARS_PER_ELT chars from
+   STRING_CST STRING.  */
+
+static tree
+extract_string_elt (tree string, unsigned chars_per_elt, unsigned index)
+{
+  tree type = cv_unqualified (TREE_TYPE (TREE_TYPE (string)));
+  tree r;
+
+  if (chars_per_elt == 1)
+    r = build_int_cst (type, TREE_STRING_POINTER (string)[index]);
+  else
+    {
+      const unsigned char *ptr
+	= ((const unsigned char *)TREE_STRING_POINTER (string)
+	   + index * chars_per_elt);
+      r = native_interpret_expr (type, ptr, chars_per_elt);
+    }
+  return r;
+}
+
 /* Subroutine of cxx_eval_constant_expression.
    Attempt to reduce a reference to an array slot.  */
 
@@ -2244,16 +2265,9 @@  cxx_eval_array_reference (const constexp
 	r = (*CONSTRUCTOR_ELTS (ary))[i].value;
       else if (TREE_CODE (ary) == VECTOR_CST)
 	r = VECTOR_CST_ELT (ary, i);
-      else if (elem_nchars == 1)
-	r = build_int_cst (cv_unqualified (TREE_TYPE (TREE_TYPE (ary))),
-			   TREE_STRING_POINTER (ary)[i]);
       else
-	{
-	  tree type = cv_unqualified (TREE_TYPE (TREE_TYPE (ary)));
-	  r = native_interpret_expr (type, (const unsigned char *)
-				     TREE_STRING_POINTER (ary)
-				     + i * elem_nchars, elem_nchars);
-	}
+	r = extract_string_elt (ary, elem_nchars, i);
+
       if (r)
 	/* Don't VERIFY_CONSTANT here.  */
 	return r;
@@ -3326,6 +3340,35 @@  cxx_eval_store_expression (const constex
 	  *valp = build_constructor (type, NULL);
 	  CONSTRUCTOR_NO_IMPLICIT_ZERO (*valp) = no_zero_init;
 	}
+      else if (TREE_CODE (*valp) == STRING_CST)
+	{
+	  /* An array was initialized with a string constant, and now
+	     we're writing into one of its elements.  Explode the
+	     single initialization into a set of element
+	     initializations.  */
+	  gcc_assert (TREE_CODE (type) == ARRAY_TYPE);
+
+	  tree string = *valp;
+	  tree elt_type = TREE_TYPE (type);
+	  unsigned chars_per_elt = (TYPE_PRECISION (elt_type)
+				    / TYPE_PRECISION (char_type_node));
+	  unsigned num_elts = TREE_STRING_LENGTH (string) / chars_per_elt;
+	  tree ary_ctor = build_constructor (type, NULL);
+
+	  vec_safe_reserve (CONSTRUCTOR_ELTS (ary_ctor), num_elts);
+	  for (unsigned ix = 0; ix != num_elts; ix++)
+	    {
+	      constructor_elt elt = 
+		{
+		  build_int_cst (size_type_node, ix),
+		  extract_string_elt (string, chars_per_elt, ix)
+		};
+	      CONSTRUCTOR_ELTS (ary_ctor)->quick_push (elt);
+	    }
+	  
+	  *valp = ary_ctor;
+	}
+
       /* If the value of object is already zero-initialized, any new ctors for
 	 subobjects will also be zero-initialized.  */
       no_zero_init = CONSTRUCTOR_NO_IMPLICIT_ZERO (*valp);
Index: testsuite/g++.dg/cpp1y/pr78551.C
===================================================================
--- testsuite/g++.dg/cpp1y/pr78551.C	(nonexistent)
+++ testsuite/g++.dg/cpp1y/pr78551.C	(working copy)
@@ -0,0 +1,32 @@ 
+// { dg-do compile { target c++14 } }
+
+// PR c++/78551 ICE in constexpr evaluation overwriting array
+// intialized by string constant.
+
+constexpr char Foo (char x, int ix)
+{
+  char d[4] = "012";
+  d[0] = x;
+  return d[ix];
+}
+
+static const char a = Foo ('a', 1);
+static const char b = Foo ('a', 0);
+
+static_assert (a == '1', "");
+static_assert (b == 'a', "");
+
+struct A {
+  union {
+    long s;
+    char d[4];
+  };
+  constexpr A (char x)
+    : d("012")
+  { d[0] = x; }
+};
+
+static constexpr A c{'a'};
+
+static_assert (c.d[0] == 'a', "");
+static_assert (c.d[1] == '1', "");