From: Jason Merrill Date: Sat, 12 Jul 2025 09:15:01 +0000 (-0400) Subject: c++: constexpr uninitialized union [PR120577] X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=f23b5df56e237df9f66b615ca4babc564d5f75de;p=thirdparty%2Fgcc.git c++: constexpr uninitialized union [PR120577] 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. --- diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index eb19784dbba..ee06858f715 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -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 index 00000000000..7db1030ab20 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-union9.C @@ -0,0 +1,33 @@ +// PR c++/120577 +// { dg-do compile { target c++20 } } + +template struct optional { + union { + _Tp __val_; + }; + template + constexpr optional(_Args... __args) + : __val_(__args...) {} +}; +template +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(4, 'X');