https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127235
Bug ID: 127235
Summary: C++20 rejects-valid with uninitialized member
Product: gcc
Version: 16.2.1
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: c++
Assignee: unassigned at gcc dot gnu.org
Reporter: jakub at gcc dot gnu.org
Target Milestone: ---
struct A { int a[2]; };
constexpr A fA () { A a; a.a[0] = 1; return a; }
constexpr A a = fA (); // { dg-error "is not a constant expression" ""
{ xfail *-*-* } }
is incorrectly silently accepted for C++20 and later.
For C++17 we correctly reject it, but that is during the construction of the a
variable:
test.C: In function ‘constexpr A fA()’:
test.C:2:23: error: uninitialized variable ‘a’ in ‘constexpr’ function
2 | constexpr A fA () { A a; a.a[0] = 1; return a; }
| ^
test.C:1:8: note: ‘struct A’ has no user-provided default constructor
1 | struct A { int a[2]; };
| ^
test.C:1:16: note: and the implicitly-defined constructor does not initialize
‘int A::a [2]’
1 | struct A { int a[2]; };
| ^
test.C: At global scope:
test.C:3:20: error: ‘constexpr A fA()’ called in a constant expression
3 | constexpr A a = fA (); // { dg-error "is not a constant
expression" "" { xfail *-*-* } }
| ~~~^~
For C++20, reduced_constant_expression_p correctly figures out it is not a
constant expression, but we only use it to make it non-cacheable:
/* Only cache a permitted result of a constant expression. */
if (cacheable && !reduced_constant_expression_p (result))
cacheable = false;
Later on, we trigger
/* The result of a constexpr function must be completely initialized.
However, in C++20, a constexpr constructor doesn't necessarily have
to initialize all the fields, so we don't clear CONSTRUCTOR_NO_CLEARING
in order to detect reading an uninitialized object in constexpr instead
of value-initializing it. (reduced_constant_expression_p is expected to
take care of clearing the flag.) */
if (TREE_CODE (result) == CONSTRUCTOR
&& (cxx_dialect < cxx20
|| !DECL_CONSTRUCTOR_P (fun)))
clear_no_implicit_zero (result);
fun here is fA, so not a constructor, so we silently clear the
CONSTRUCTOR_NO_CLEARING flag.