Addressing incomplete consteval-only types

Document #: P4132R0 [Latest] [Status]
Date: 2026-03-23
Project: Programming Language C++
Audience: EWG, CWG
Reply-to: Dan Katz
<>
Wyatt Childers
<>

1 Introduction

CWG3150 points out the difficulty of determining whether an incomplete class, or a pointer to such a class, is a consteval-only type. Discussion of the issue has surfaced two challenges:

  1. Because pointers to consteval-only types (e.g., std::meta::info *) are themselves consteval-only, the implementation cannot know whether a class whose data member has some S<int> * type is consteval-only until S<int> has been instantiated. As this can “affect the semantics of the program”, this has the potential to require many instantiations that are not required today, which would be expensive and would surely break existing code.
  2. We somehow forgot that classes can be incomplete, and that this hampers the implementation’s capacity to determine whether that class (or one holding a pointer to it) is consteval-only. Whoops!

The wording changes proposed by this paper attempt to address these issues.

2 Proposed changes

TODO: Flesh out if there’s time.

2.1 Wording

Modify [basic.types.general]/12 and split into separate paragraphs as follows:

12 A type is consteval-only if it is

  • (12.1) std::meta::info,
  • (12.2) cv T, pointer to T, reference to T, or array of T, where T is a consteval-only type,
  • a pointer or reference to a consteval-only type,
  • an array of consteval-only type,
  • (12.3) a function type having a return type or any parameter type that is consteval-only,
  • (12.4) a class type C for which with any non-static data member having consteval-only type
    • (12.4.1) C is complete from some point in the program,
    • (12.4.2) C has a non-static data member whose type is consteval-only, and
    • (12.4.3) if C is a specialization of a templated class, then C is either an explicit specialization or is referenced in manner that requires the complete definition of C for a purpose other than determining whether C is a consteval-only type, or
  • (12.5) a type “pointer to member of class C of type T”, where at least one of C or T is a consteval-only type.

A type is observably consteval-only from a program point P if it is

  • (12.5) std::meta::info,

  • (12.6) cv T, pointer to T, reference to T, or array of T, where T is observably consteval-only from P,

  • (12.7) a class type C for which

    • (12.7.1) C is complete from P,
    • (12.7.2) C has a non-static data member whose type is observably consteval-only from P, and
    • (12.7.3) if C is a specialization of a templated class for which no declared specialization is reachable from P, then C is referenced at or prior to P in a context that requires the complete definition of C for a purpose other than determining whether C is an observably consteval-only type, or
  • (12.8) a type “pointer to member of class C of type T”, where at least one of C or T is observably consteval-only from P.

[ Note: Every type which is observably consteval-only from some program point is also consteval-only.end note ]

[ Example:
// a.cpp
struct S { std::meta::info m; };
struct T;
struct U { T *m; };

// b.cpp
struct S;
struct T { S *m; };

S, T, and U are all consteval-only types, even though neither T nor U is observably consteval-only from any point in the program.end example ]

Every object of consteval-only type shall be

  • the object associated with a constexpr variable or a subobject thereof,
  • a template parameter object ([temp.param]) or a subobject thereof, or
  • an object whose lifetime begins and ends during the evaluation of a core constant expression.

Every function of consteval-only type shall be an immediate function ([expr.const]).

For each declaration D of a variable whose type T is consteval-only, one of the following shall hold:

  • (12.9) D is constexpr; or
  • (12.10) the lifetime of each object or reference introduced by the variable begins and ends within a manifestly constant-evaluated expression;

a diagnostic is required only if T is observably consteval-only from the end of the definition domain in which D appears.

For each definition D of a non-consteval function whose type T is consteval-only, T shall be observably consteval-only from the point following D; a diagnostic is only required if T is observably consteval-only from the end of the definition domain in which D appears.

Each potentially-evaluated expression or conversion E of consteval-only type T shall be in an immediate function context; a diagnostic is required only if T is observably consteval-only from the end of the definition domain in which E appears.

[ Note: An expression is immediate-escalating if its type is observably consteval-only from the program point following the expression ([expr.const]).end note ]

For each manifestly constant-evaluated expression or conversion E whose result has a constituent value or a constituent reference that is, points to, or refers to an object whose complete object is of type T that is consteval-only, T shall be observably consteval-only from the point following where E appears; a diagnostic is only required if T is observably consteval-only from the end of the definition domain in which E appears.

Modify [expr.const]/21 as follows:

21 A constant expression is either

  • (21.1) a glvalue core constant expression E for which

    • (21.1.1) E refers to a non-immediate function,

    • (21.1.2) E designates an object o, and if the complete object of o is of observably consteval-only type from the program point immediately following E then so is E,

      [ Example: end example ]

    or

  • (21.2) a prvalue core constant expression E whose result object ([basic.lval]) satisfies the following constraints:

    • (21.2.1) each constituent reference refers to an object or a non-immediate function,
    • (21.2.2) no constituent value of scalar type is an indeterminate or erroneous value ([basic.indet]),
    • (21.2.3) no constituent value of pointer type is a pointer to an immediate function or an invalid pointer value ([basic.compound]),
    • (21.2.4) no constituent value of pointer-to-member-type designates an immediate function, and
    • (21.2.5) unless the value is of consteval-only type,
      • (21.2.5.1) no constituent value of pointer-to-member type points to a direct member of a class that is observably consteval-only from the program point immediately following E class type,
      • (21.2.5.2) no constituent value of pointer type points to or past an object whose complete object is of observably consteval-only type from the program point immediately following E, and
      • (21.2.5.3) no constituent reference refers to an object whose complete object is of observably consteval-only type from the program point immediately following E.

Modify [expr.const]/24 as follows:

24 A potentially-evaluated expression or conversion E is immediate-escalating if it is neither initially in an immediate function context nor a subexpression of an immediate invocation, and

  • (24.1) it is an id-expression or splice-expression that designates an immediate function,
  • (24.2) it is an immediate invocation that is not a constant expression, or
  • (24.3) it is of observably consteval-only type from the program point immediately following E ([basic.types.general]).

Modify [expr.const]/26 as follows:

26 An immediate function is a function that is either

  • (26.1) declared with the consteval specifier, or

  • (26.2) an immediate-escalating function whose type is observably consteval-only ([basic.types.general]) from the program point immediately following that function’s definition, or

  • (26.3) an immediate-escalating function F whose function body contains either

    • (26.3.1) an immediate-escalating expression or
    • (26.3.2) a definition of a non-constexpr variable with whose type is observably consteval-only from the program point immediately following that definition

    whose innermost enclosing non-block scope is F’s function parameter scope.

    [ Note: Default member initializers used to initialize a base or member subobject ([class.base.init]) are considered to be part of the function body ([dcl.fct.def.general]).end note ]

Modify [class.virtual]/18 as follows:

18 A class C with a consteval virtual function that overrides a virtual function that is not consteval shall have be observably consteval-only type from the point following the definition of C. A consteval virtual function shall not be overriden by a virtual function that is not consteval.