]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: constexpr uninitialized union [PR120577]
authorJason Merrill <jason@redhat.com>
Sat, 26 Jul 2025 00:49:17 +0000 (20:49 -0400)
committerJason Merrill <jason@redhat.com>
Sat, 26 Jul 2025 00:49:17 +0000 (20:49 -0400)
This was failing for two reasons:

1) We were wrongly treating the basic_string constructor as
zero-initializing the object, which it doesn't.
2) Given that, when we went to look for a value for the anonymous union,
we concluded that it was value-initialized, and trying to evaluate that
broke because we weren't setting ctx->ctor for it.

This patch fixes both issues, #1 by setting CONSTRUCTOR_NO_CLEARING and #2
by inserting a new CONSTRUCTOR for the member rather than evaluate it out of
context, which is consistent with cxx_eval_store_expression.

PR c++/120577

gcc/cp/ChangeLog:

* constexpr.cc (cxx_eval_call_expression): Set
CONSTRUCTOR_NO_CLEARING on initial value for ctor.
(cxx_eval_component_reference): Make value-initialization
of an aggregate member explicit.

gcc/testsuite/ChangeLog:

* g++.dg/cpp2a/constexpr-union9.C: New test.

(cherry picked from commit f23b5df56e237df9f66b615ca4babc564d5f75de)

gcc/cp/constexpr.cc
gcc/testsuite/g++.dg/cpp2a/constexpr-union9.C [new file with mode: 0644]

index df5bdb96862e235d812e9212593af179f4e99825..6d368b2ae6f3fcb3a08d681eb1c2879ef678f777 100644 (file)
@@ -3337,10 +3337,10 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
              && TREE_CODE (new_obj) == COMPONENT_REF
              && TREE_CODE (TREE_TYPE (TREE_OPERAND (new_obj, 0))) == UNION_TYPE)
            {
+             tree ctor = build_constructor (TREE_TYPE (new_obj), NULL);
+             CONSTRUCTOR_NO_CLEARING (ctor) = true;
              tree activate = build2 (INIT_EXPR, TREE_TYPE (new_obj),
-                                     new_obj,
-                                     build_constructor (TREE_TYPE (new_obj),
-                                                        NULL));
+                                     new_obj, ctor);
              cxx_eval_constant_expression (ctx, activate,
                                            lval, non_constant_p, overflow_p);
              ggc_free (activate);
@@ -4731,6 +4731,16 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t,
     }
 
   /* If there's no explicit init for this field, it's value-initialized.  */
+
+  if (AGGREGATE_TYPE_P (TREE_TYPE (t)))
+    {
+      /* As in cxx_eval_store_expression, insert an empty CONSTRUCTOR
+        and copy the flags.  */
+      constructor_elt *e = get_or_insert_ctor_field (whole, part);
+      e->value = value = build_constructor (TREE_TYPE (part), NULL);
+      return value;
+    }
+
   value = build_value_init (TREE_TYPE (t), tf_warning_or_error);
   return cxx_eval_constant_expression (ctx, value,
                                       lval,
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-union9.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-union9.C
new file mode 100644 (file)
index 0000000..7db1030
--- /dev/null
@@ -0,0 +1,33 @@
+// PR c++/120577
+// { dg-do compile { target c++20 } }
+
+template <class _Tp> struct optional {
+  union {
+    _Tp __val_;
+  };
+  template <class... _Args>
+  constexpr optional(_Args... __args)
+      : __val_(__args...) {}
+};
+template <class _Tp, class... _Args>
+constexpr optional<_Tp> make_optional(_Args... __args) {
+  return optional<_Tp>(__args...);
+}
+
+struct __non_trivial_if {
+  constexpr __non_trivial_if() {}
+};
+struct allocator : __non_trivial_if {};
+struct __padding {};
+struct __short {
+  [[__no_unique_address__]] __padding __padding_;
+  int __data_;
+};
+struct basic_string {
+  union {
+    __short __s;
+  };
+  [[__no_unique_address__]] allocator __alloc_;
+  constexpr basic_string(int, int) {}
+};
+auto opt = make_optional<basic_string>(4, 'X');