Skip to content

CWG3127 [expr.const] Evaluation context can be missing synthesized points #810

Description

@katzdm

Full name of submitter: Dan Katz

Reference: [expr.const]

Issue description
The evaluation context ([expr.const]) determines which declarations are found by metafunctions ([meta.reflection]). That context consists of ordinary program points (typically, the point at which a manifestly constant-evaluated expression appears), together with any "synthesized points" that correspond to injected declarations (which in C++26, following the removal of std::meta::annotate from earlier revisions of P3394, are only produced by define_aggregate).

Because the evaluation of a metafunction can trigger the instantiation of a specialization, the notions of "evaluation context" and "instantiation context" are intertwined. For instance, given:

template <typename T>
struct TCls {
  T mem;
};

struct S;
consteval {
  constexpr auto ctx = std::meta::access_context::current();

  if (nonstatic_data_members_of(substitute(^^TCls, {define_aggregate(^^S, {})}), ctx).size() > 1)
    throw "contrived, but whatever";
}
  • The evaluation context of the expression corresponding to the consteval block initially consists of only the point following the consteval block.
  • During the evaluation of that expression, a synthesized point corresponding to the definition of S joins the evaluation context following the evaluation of define_aggregate(^^S).
  • The call to nonstatic_data_members_of then triggers the instantiation of TCls<S>.
  • Importantly, during the instantiation of TCls<S>, [module.reach]/6 guarantees that the synthesized point associated with the definition of S is a part of the instantiation context; this guarantees that S is a complete type during this instantiation:

During the implicit instantiation of any construct that resulted from the evaluation of an expression as a core constant expression, the instantiation context contains each point in the evaluation context ([expr.const]).

However, consider now the following:

template <typename T>
struct TCls {
  T mem;

  static_assert(size_of(^^T) > 0);  // <-- problem
};

struct S;
consteval {
  constexpr auto ctx = std::meta::access_context::current();

  if (nonstatic_data_members_of(substitute(^^TCls, {define_aggregate(^^S, {})}), ctx).size() > 1)
    throw "contrived, but whatever";
}

The operand of the static_assert in TCls<S> is a distinct manifestly constant-evaluated expression, and nothing in the definition of "evaluation context" in [expr.const]/32 succeeds in "pushing" the synthesized point associated with S from the instantiation context of TCls<S> into the evaluation context of the static assertion - without which, size_of(^^T) will throw.

Suggested resolution
Modify [expr.const]/32 as follows:

The evaluation context is a set of program points that determines the behavior of certain functions used for reflection ([meta.reflection]). During the evaluation V of an expression E as a core constant expression, the evaluation context of an evaluation X ([intro.execution]) consists of the following points:

  • The program point EVAL-PT(L), where L is the point at which E appears, and where EVAL-PT(P), for a point P, is a point R determined as follows:
    • If a potentially-evaluated subexpression ([intro.execution]) of a default member initializer I appears at P, and a (possibly aggregate) initialization during V is using I, then R is EVAL-PT(Q) where Q is the point at which that initialization appears.
    • Otherwise, if a potentially-evaluated subexpression of a default argument ([dcl.fct.default]) appears at P, and an invocation of a function ([expr.call]) during V is using that default argument, then R is EVAL-PT(Q) where Q is the point at which that invocation appears.
    • Otherwise, R is P.
  • Each synthesized point corresponding to an injected declaration produced by any evaluation sequenced before X ([intro.execution]).
  • Any synthesized point in the instantiation context of EVAL-PT(L).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions