-
Notifications
You must be signed in to change notification settings - Fork 8
Description
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
Sjoins the evaluation context following the evaluation ofdefine_aggregate(^^S). - The call to
nonstatic_data_members_ofthen triggers the instantiation ofTCls<S>. - Importantly, during the instantiation of
TCls<S>, [module.reach]/6 guarantees that the synthesized point associated with the definition ofSis a part of the instantiation context; this guarantees thatSis 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).