aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorJason Merrill <jason@redhat.com>2025-01-27 18:30:18 -0500
committerJason Merrill <jason@redhat.com>2025-01-28 12:31:00 -0500
commitd0f230adf0e888d9218a123ac620c9a5b3020c2d (patch)
treec657de3ee932d57a8fa58104c29ede7305be9674 /gcc
parent0204dcf930b5093d0811a007b7f47aa42e55e787 (diff)
downloadgcc-d0f230adf0e888d9218a123ac620c9a5b3020c2d.zip
gcc-d0f230adf0e888d9218a123ac620c9a5b3020c2d.tar.gz
gcc-d0f230adf0e888d9218a123ac620c9a5b3020c2d.tar.bz2
c++: init-list opt and lvalue initializers [PR118673]
When fn returns {extension}, the ArrayRef in the initializer_list is constructed to point to 'extension', the variable with static storage duration. The optimization was copying extension's value into a temporary array and constructing the ArrayRef to point to that temporary copy instead, resulting in a dangling pointer. So suppress this optimization if the element constructor takes a reference and the initializer is a non-mergeable lvalue. PR c++/118673 gcc/cp/ChangeLog: * call.cc (maybe_init_list_as_array): Check for lvalue initializers. * cp-tree.h (enum cp_lvalue_kind_flags): Add clk_mergeable. * tree.cc (lvalue_kind): Return it. (non_mergeable_glvalue_p): New. (test_lvalue_kind): Adjust. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/initlist-opt6.C: New test.
Diffstat (limited to 'gcc')
-rw-r--r--gcc/cp/call.cc9
-rw-r--r--gcc/cp/cp-tree.h4
-rw-r--r--gcc/cp/tree.cc21
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/initlist-opt6.C26
4 files changed, 57 insertions, 3 deletions
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 469af83..3a56a82 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -4374,6 +4374,15 @@ maybe_init_list_as_array (tree elttype, tree init)
/* Let the normal code give the error. */
return NULL_TREE;
+ /* A glvalue initializer might be significant to a reference constructor
+ or conversion operator. */
+ if (!DECL_CONSTRUCTOR_P (c->cand->fn)
+ || (TYPE_REF_P (TREE_VALUE
+ (FUNCTION_FIRST_USER_PARMTYPE (c->cand->fn)))))
+ for (auto &ce : CONSTRUCTOR_ELTS (init))
+ if (non_mergeable_glvalue_p (ce.value))
+ return NULL_TREE;
+
tree first = CONSTRUCTOR_ELT (init, 0)->value;
conversion *fc = implicit_conversion (elttype, init_elttype, first, false,
LOOKUP_IMPLICIT|LOOKUP_NO_NARROWING,
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 7bf9557..21011d0b 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5679,7 +5679,8 @@ enum cp_lvalue_kind_flags {
clk_class = 4, /* A prvalue of class or array type. */
clk_bitfield = 8, /* An lvalue for a bit-field. */
clk_packed = 16, /* An lvalue for a packed field. */
- clk_implicit_rval = 1<<5 /* An lvalue being treated as an xvalue. */
+ clk_implicit_rval = 1<<5, /* An lvalue being treated as an xvalue. */
+ clk_mergeable = 1<<6
};
/* This type is used for parameters and variables which hold
@@ -8191,6 +8192,7 @@ extern bool glvalue_p (const_tree);
extern bool obvalue_p (const_tree);
extern bool xvalue_p (const_tree);
extern bool bitfield_p (const_tree);
+extern bool non_mergeable_glvalue_p (const_tree);
extern tree cp_stabilize_reference (tree);
extern bool builtin_valid_in_constant_expr_p (const_tree);
extern tree build_min (enum tree_code, tree, ...);
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index e35432f..fb6b2b1 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -191,6 +191,8 @@ lvalue_kind (const_tree ref)
return op1_lvalue_kind;
case STRING_CST:
+ return clk_ordinary | clk_mergeable;
+
case COMPOUND_LITERAL_EXPR:
return clk_ordinary;
@@ -210,6 +212,10 @@ lvalue_kind (const_tree ref)
&& DECL_LANG_SPECIFIC (ref)
&& DECL_IN_AGGR_P (ref))
return clk_none;
+
+ if (DECL_MERGEABLE (ref))
+ return clk_ordinary | clk_mergeable;
+
/* FALLTHRU */
case INDIRECT_REF:
case ARROW_EXPR:
@@ -407,6 +413,17 @@ bitfield_p (const_tree ref)
return (lvalue_kind (ref) & clk_bitfield);
}
+/* True if REF is a glvalue with a unique address, excluding mergeable glvalues
+ such as string constants. */
+
+bool
+non_mergeable_glvalue_p (const_tree ref)
+{
+ auto kind = lvalue_kind (ref);
+ return (kind != clk_none
+ && !(kind & (clk_class|clk_mergeable)));
+}
+
/* C++-specific version of stabilize_reference. */
tree
@@ -6495,11 +6512,11 @@ test_lvalue_kind ()
tree string_lit = build_string (4, "foo");
TREE_TYPE (string_lit) = char_array_type_node;
string_lit = fix_string_type (string_lit);
- ASSERT_EQ (clk_ordinary, lvalue_kind (string_lit));
+ ASSERT_EQ (clk_ordinary|clk_mergeable, lvalue_kind (string_lit));
tree wrapped_string_lit = maybe_wrap_with_location (string_lit, loc);
ASSERT_TRUE (location_wrapper_p (wrapped_string_lit));
- ASSERT_EQ (clk_ordinary, lvalue_kind (wrapped_string_lit));
+ ASSERT_EQ (clk_ordinary|clk_mergeable, lvalue_kind (wrapped_string_lit));
tree parm = build_decl (UNKNOWN_LOCATION, PARM_DECL,
get_identifier ("some_parm"),
diff --git a/gcc/testsuite/g++.dg/cpp0x/initlist-opt6.C b/gcc/testsuite/g++.dg/cpp0x/initlist-opt6.C
new file mode 100644
index 0000000..8019289
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/initlist-opt6.C
@@ -0,0 +1,26 @@
+// PR c++/118673
+// { dg-do run { target c++11 } }
+
+#include <initializer_list>
+
+struct ArrayRef {
+ const int *Data = nullptr;
+ ArrayRef(const int &OneElt) : Data(&OneElt) {}
+};
+
+struct Vec
+{
+ ArrayRef Elts[1];
+ Vec(std::initializer_list<ArrayRef> IL)
+ : Elts{*IL.begin()}
+ { }
+};
+
+[[gnu::noinline]] Vec fn() {
+ static const auto extension = 42;
+ return {extension};
+}
+int main() {
+ auto t = fn();
+ if (t.Elts[0].Data[0] != 42) __builtin_abort();
+}