Skip to content

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

@katzdm

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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions