]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: constexpr uninitialized union [PR120577]
authorJason Merrill <jason@redhat.com>
Sat, 12 Jul 2025 09:15:01 +0000 (05:15 -0400)
committerJason Merrill <jason@redhat.com>
Tue, 15 Jul 2025 15:34:39 +0000 (11:34 -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.

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

index eb19784dbbaad9a012a2a4a4f608e19f90cc0055..ee06858f7153d97335c7abe347d39d3170361a48 100644 (file)
@@ -4201,10 +4201,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,
                                            jump_target);
@@ -5793,6 +5793,18 @@ 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);
+      CONSTRUCTOR_ZERO_PADDING_BITS (value)
+       = CONSTRUCTOR_ZERO_PADDING_BITS (whole);
+      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');