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;