In C++, C99 a compound literal creates a temporary object, unlike C, where it creates an automatic or static object. As a result, using an array compound literal to initialize a pointer variable leads to undefined behavior, as the array's lifetime ends after the declaration statement. This patch changes the C++ front end to reject decay of temporary array compound literals to pointers, and the C front end to warn about it with -Wc++-compat.

Tested x86_64-pc-linux-gnu.  Is the C front-end change OK?
commit 7edd980a3738458159819d6423a690913fe6899f
Author: Jason Merrill <ja...@redhat.com>
Date:   Tue May 22 15:57:52 2012 -0400

    	PR c++/53220
    gcc/
    	* c-typeck.c (array_to_pointer_conversion): Give -Wc++-compat warning
    	about array compound literals.
    gcc/cp/
    	* call.c (convert_like_real) [ck_list]: Take array address directly.
    	* typeck.c (decay_conversion): Reject decay of an array compound
    	literal.

diff --git a/gcc/c-typeck.c b/gcc/c-typeck.c
index 0398b75..d3fa68e 100644
--- a/gcc/c-typeck.c
+++ b/gcc/c-typeck.c
@@ -1785,6 +1785,18 @@ array_to_pointer_conversion (location_t loc, tree exp)
   if (TREE_CODE (exp) == INDIRECT_REF)
     return convert (ptrtype, TREE_OPERAND (exp, 0));
 
+  /* In C++ array compound literals are temporary objects unless they are
+     const or appear in namespace scope, so they are destroyed too soon
+     to use them for much of anything  (c++/53220).  */
+  if (warn_cxx_compat && TREE_CODE (exp) == COMPOUND_LITERAL_EXPR)
+    {
+      tree decl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+      if (!TREE_READONLY (decl) && !TREE_STATIC (decl))
+	warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wc___compat,
+		    "converting an array compound literal to a pointer "
+		    "is ill-formed in C++");
+    }
+
   adr = build_unary_op (loc, ADDR_EXPR, exp, 1);
   return convert (ptrtype, adr);
 }
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index daaae2b..5efa57c 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -5849,11 +5849,15 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
 	  (elttype, cp_type_quals (elttype) | TYPE_QUAL_CONST);
 	array = build_array_of_n_type (elttype, len);
 	array = finish_compound_literal (array, new_ctor, complain);
+	/* Take the address explicitly rather than via decay_conversion
+	   to avoid the error about taking the address of a temporary.  */
+	array = cp_build_addr_expr (array, complain);
+	array = cp_convert (build_pointer_type (elttype), array);
 
 	/* Build up the initializer_list object.  */
 	totype = complete_type (totype);
 	field = next_initializable_field (TYPE_FIELDS (totype));
-	CONSTRUCTOR_APPEND_ELT (vec, field, decay_conversion (array, complain));
+	CONSTRUCTOR_APPEND_ELT (vec, field, array);
 	field = next_initializable_field (DECL_CHAIN (field));
 	CONSTRUCTOR_APPEND_ELT (vec, field, size_int (len));
 	new_ctor = build_constructor (totype, vec);
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 92794ea..901b15f 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -1886,6 +1886,15 @@ decay_conversion (tree exp, tsubst_flags_t complain)
 	  return error_mark_node;
 	}
 
+      /* Don't let an array compound literal decay to a pointer.  It can
+	 still be used to initialize an array or bind to a reference.  */
+      if (TREE_CODE (exp) == TARGET_EXPR)
+	{
+	  if (complain & tf_error)
+	    error_at (loc, "taking address of temporary array");
+	  return error_mark_node;
+	}
+
       ptrtype = build_pointer_type (TREE_TYPE (type));
 
       if (TREE_CODE (exp) == VAR_DECL)
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index d9efab9..d3fc2d8 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -1759,7 +1759,8 @@ ISO C99 supports compound literals.  A compound literal looks like
 a cast containing an initializer.  Its value is an object of the
 type specified in the cast, containing the elements specified in
 the initializer; it is an lvalue.  As an extension, GCC supports
-compound literals in C90 mode and in C++.
+compound literals in C90 mode and in C++, though the semantics are
+somewhat different in C++.
 
 Usually, the specified type is a structure.  Assume that
 @code{struct foo} and @code{structure} are declared as shown:
@@ -1785,8 +1786,9 @@ This is equivalent to writing the following:
 @}
 @end smallexample
 
-You can also construct an array.  If all the elements of the compound literal
-are (made up of) simple constant expressions, suitable for use in
+You can also construct an array, though this is dangerous in C++, as
+explained below.  If all the elements of the compound literal are
+(made up of) simple constant expressions, suitable for use in
 initializers of objects of static storage duration, then the compound
 literal can be coerced to a pointer to its first element and used in
 such an initializer, as shown here:
@@ -1822,6 +1824,25 @@ static int y[] = @{1, 2, 3@};
 static int z[] = @{1, 0, 0@};
 @end smallexample
 
+In C, a compound literal designates an unnamed object with static or
+automatic storage duration.  In C++, a compound literal designates a
+temporary object, which only lives until the end of its
+full-expression.  As a result, well-defined C code that takes the
+address of a subobject of a compound literal can be undefined in C++.
+For instance, if the array compound literal example above appeared
+inside a function, any subsequent use of @samp{foo} in C++ has
+undefined behavior because the lifetime of the array ends after the
+declaration of @samp{foo}.  As a result, the C++ compiler now rejects
+the conversion of a temporary array to a pointer.
+
+As an optimization, the C++ compiler sometimes gives array compound
+literals longer lifetimes: when the array either appears outside a
+function or has const-qualified type.  If @samp{foo} and its
+initializer had elements of @samp{char *const} type rather than
+@samp{char *}, or if @samp{foo} were a global variable, the array
+would have static storage duration.  But it is probably safest just to
+avoid the use of array compound literals in code compiled as C++.
+
 @node Designated Inits
 @section Designated Initializers
 @cindex initializers with labeled elements
diff --git a/gcc/testsuite/c-c++-common/array-lit.c b/gcc/testsuite/c-c++-common/array-lit.c
new file mode 100644
index 0000000..6505c20
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/array-lit.c
@@ -0,0 +1,12 @@
+/* { dg-options "-std=c99 -Wc++-compat -Werror" { target c } } */
+/* { dg-prune-output "treated as errors" } */
+#include <stdio.h>
+
+int main()
+{
+  for (int *p = (int[]){ 1, 2, 3, 0 }; /* { dg-error "array" } */
+       *p; ++p) {
+    printf("%d\n", *p);
+  }
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/ext/complit12.C b/gcc/testsuite/g++.dg/ext/complit12.C
index 29c9af1..5c6a731 100644
--- a/gcc/testsuite/g++.dg/ext/complit12.C
+++ b/gcc/testsuite/g++.dg/ext/complit12.C
@@ -53,12 +53,14 @@ int main ()
     T t;
     if (c != 11)
       return 5;
-    MA ma = bar ((M[2]) { M(), M() }, m);
-    if (c != 12)
-      return 7;
     M mm[2] = ((M[2]) { f(M()), f(M()) });
-    if (c != 14)
+    if (c != 13)
       return 8;
+#if 0
+    MA ma = bar ((M[2]) { M(), M() }, m);
+    if (c != 14)
+      return 7;
+#endif
   }
   if (c != 0)
     return 6;

Reply via email to