Title
Partially initialized variables during constant initialization
Status
open
Section
7.7 [expr.const]
Submitter
Barry Revzin

Created on 2022-02-21.00:00:00 last changed 23 months ago

Messages

Date: 2023-01-15.00:00:00

Additional notes (January, 2023)

The standard does not guarantee stable results when reading padding bits, i.e. bits that are not part of the value representation of some in-lifetime object. In C, explicit rules keep padding bits stable; they are allowed to change only if a store to any class member occurs.

Date: 2023-01-20.22:53:26

CWG 2022-12-02

The resolution shown above would leave padding bits uninitialized. In contrast, zero-initialization does set padding bits to 0 to possibly facilitate memcmp. Additional example:

  struct C { 
    int a;
    int b;
    C() : b(a) {}   // #1
  }; 
  constinit C x;    // OK when zero-initializing first, because #1 reads zero-initialized a?

2022-12-03

Forwarded to EWG with cplusplus/papers#1380.

Date: 2022-11-15.00:00:00

Notes from the November, 2022 meeting

CWG preferred to zero-initialize a.y in the example, and keep #1 well-formed.

Possible resolution:

Change in 6.9.3.2 [basic.start.static] paragraph 2 as follows:

Constant initialization is performed if a variable or temporary object with static or thread storage duration is constant-initialized (7.7 [expr.const]). If constant initialization is not performed, a A variable with static storage duration (6.7.6.2 [basic.stc.static]) or thread storage duration (6.7.6.3 [basic.stc.thread]) or a subobject thereof is zero-initialized (9.4 [dcl.init]) if constant initialization is not performed or if it does not initialize that subobject. Together, zero-initialization and constant initialization are called static initialization; all other initialization is dynamic initialization. All static initialization strongly happens before (6.9.2.2 [intro.races]) any dynamic initialization.
Date: 2022-03-15.00:00:00

Alternative suggested resolution (March, 2022) [SUPERSEDED]:

Change in 7.7 [expr.const] paragraph 11 as follows:

A constant expression is either a glvalue core constant expression that refers to an entity that is a permitted result of a constant expression (as defined below), or a prvalue core constant expression whose value satisfies the following constraints:
  • if the value is an object of class type, each non-static data member of reference type refers to an entity that is a permitted result of a constant expression,
  • if the value is of pointer type, it contains the address of an object with static storage duration, the address past the end of such an object (7.6.6 [expr.add]), the address of a non-immediate function, or a null pointer value,
  • if the value is of pointer-to-member-function type, it does not designate an immediate function, and
  • if the value is an object of class or array type, each subobject is initialized (11.9.3 [class.base.init]) and satisfies these constraints for the value.
Date: 2022-12-01.21:11:16

Suggested resolution [SUPERSEDED]:

Change in 7.7 [expr.const] paragraph 2:
A variable or temporary object o is constant-initialized if
  • either it has an initializer or its default-initialization results in some initialization being performed, and
  • every non-variant non-static data member and base class subobject is initialized (11.9.3 [class.base.init]), and
  • the full-expression of its initialization is a constant expression when interpreted as a constant-expression, except that if o is an object, that full-expression may also invoke constexpr constructors for o and its subobjects even if those objects are of non-literal class types.
Date: 2022-03-29.18:46:44

Consider:

  struct A { int x = 1; int y; };
  constinit A a;                   // static storage duration; #1

The treatment of this example changed with P1331R2 (Permitting trivial default initialization in constexpr contexts), adopted 2019-07. Prior to this paper, the default constructor of A was not constexpr because it left a data member uninitialized. With paper P1331, the restriction was shifted to reading uninitialized objects during constant evaluation, and the variable a now satisfies the requirements for "constant-initialized" in 7.7 [expr.const] paragraph 2:

A variable or temporary object o is constant-initialized if
  • either it has an initializer or its default-initialization results in some initialization being performed, and
  • the full-expression of its initialization is a constant expression when interpreted as a constant-expression, except that if o is an object, that full-expression may also invoke constexpr constructors for o and its subobjects even if those objects are of non-literal class types.

Zero-initialization is not performed prior to constant-initialization per 6.9.3.2 [basic.start.static] paragraph 2:

Constant initialization is performed if a variable or temporary object with static or thread storage duration is constant-initialized (7.7 [expr.const]). If constant initialization is not performed, a variable with static storage duration (6.7.6.2 [basic.stc.static]) or thread storage duration (6.7.6.3 [basic.stc.thread]) is zero-initialized (9.4 [dcl.init]). Together, zero-initialization and constant initialization are called static initialization; all other initialization is dynamic initialization.

Thus, #1 is valid and a is statically initialized, but a.y would remain uninitialized, which is surprising for an object with static storage duration.

Current implementations diagnose an error at #1, because the variable a is actually not considered to be constant-initialized.

This issue is closely related to issue 2558.

History
Date User Action Args
2023-01-20 22:53:26adminsetmessages: + msg7147
2022-12-03 19:19:31adminsetmessages: + msg7076
2022-12-03 14:05:33adminsetstatus: drafting -> open
2022-12-01 21:11:16adminsetmessages: + msg7071
2022-11-20 07:54:16adminsetstatus: open -> drafting
2022-02-21 00:00:00admincreate
2022-02-18 07:47:23adminsetmessages: + msg6740
2022-02-18 07:47:23adminsetmessages: + msg6739