Implement 'is_enumerable_type'.

This commit is contained in:
Dan Katz
2025-03-07 17:12:23 -05:00
parent 9a9601dad4
commit 6724fb0d3f
3 changed files with 78 additions and 40 deletions

View File

@@ -377,6 +377,12 @@ static bool has_complete_definition(APValue &Result, ASTContext &C,
ArrayRef<Expr *> Args,
Decl *ContainingDecl);
static bool is_enumerable_type(APValue &Result, ASTContext &C,
MetaActions &Meta, EvalFn Evaluator,
DiagFn Diagnoser, bool AllowInjection,
QualType ResultTy, SourceRange Range,
ArrayRef<Expr *> Args, Decl *ContainingDecl);
static bool is_template(APValue &Result, ASTContext &C, MetaActions &Meta,
EvalFn Evaluator, DiagFn Diagnoser, bool AllowInjection,
QualType ResultTy, SourceRange Range,
@@ -772,6 +778,7 @@ static constexpr Metafunction Metafunctions[] = {
{ Metafunction::MFRK_bool, 1, 1, is_alias },
{ Metafunction::MFRK_bool, 1, 1, is_complete_type },
{ Metafunction::MFRK_bool, 1, 1, has_complete_definition },
{ Metafunction::MFRK_bool, 1, 1, is_enumerable_type },
{ Metafunction::MFRK_bool, 1, 1, is_template },
{ Metafunction::MFRK_bool, 1, 1, is_function_template },
{ Metafunction::MFRK_bool, 1, 1, is_variable_template },
@@ -2252,22 +2259,12 @@ bool type_of(APValue &Result, ASTContext &C, MetaActions &Meta,
switch (RV.getReflectionKind()) {
case ReflectionKind::Null:
case ReflectionKind::Type: {
QualType QT = desugarType(RV.getTypeOfReflectedResult(C),
/*UnwrapAliases=*/ true, /*DropCV=*/false,
/*DropRefs=*/false);
return SetAndSucceed(Result, makeReflection(QT));
}
case ReflectionKind::Type:
case ReflectionKind::Template:
case ReflectionKind::Namespace:
return Diagnoser(Range.getBegin(), diag::metafn_no_associated_property)
<< DescriptionOf(RV) << 0 << Range;
case ReflectionKind::Object: {
QualType QT = desugarType(RV.getTypeOfReflectedResult(C),
/*UnwrapAliases=*/ true, /*DropCV=*/false,
/*DropRefs=*/false);
return SetAndSucceed(Result, makeReflection(QT));
}
case ReflectionKind::Object:
case ReflectionKind::Value: {
QualType QT = desugarType(RV.getTypeOfReflectedResult(C),
/*UnwrapAliases=*/true, /*DropCV=*/false,
@@ -3970,6 +3967,41 @@ bool has_complete_definition(APValue &Result, ASTContext &C, MetaActions &Meta,
return SetAndSucceed(Result, makeBool(C, result));
}
bool is_enumerable_type(APValue &Result, ASTContext &C, MetaActions &Meta,
EvalFn Evaluator, DiagFn Diagnoser, bool AllowInjection,
QualType ResultTy, SourceRange Range,
ArrayRef<Expr *> Args, Decl *ContainingDecl) {
assert(Args[0]->getType()->isReflectionType());
assert(ResultTy == C.BoolTy);
APValue RV;
if (!Evaluator(RV, Args[0], true))
return true;
bool result = false;
switch (RV.getReflectionKind()) {
case ReflectionKind::Type:
if (Decl *typeDecl = findTypeDecl(RV.getReflectedType())) {
if (auto *TD = dyn_cast<TagDecl>(typeDecl))
result = (TD->getDefinition() != nullptr &&
!TD->getDefinition()->isBeingDefined());
}
break;
case ReflectionKind::Null:
case ReflectionKind::Object:
case ReflectionKind::Value:
case ReflectionKind::Declaration:
case ReflectionKind::Template:
case ReflectionKind::Namespace:
case ReflectionKind::BaseSpecifier:
case ReflectionKind::DataMemberSpec:
case ReflectionKind::Annotation:
break;
}
return SetAndSucceed(Result, makeBool(C, result));
}
bool is_template(APValue &Result, ASTContext &C, MetaActions &Meta,
EvalFn Evaluator, DiagFn Diagnoser, bool AllowInjection,
QualType ResultTy, SourceRange Range,

View File

@@ -128,7 +128,7 @@ consteval auto is_type(info) -> bool;
consteval auto is_type_alias(info) -> bool;
consteval auto is_namespace_alias(info) -> bool;
consteval auto is_complete_type(info) -> bool;
consteval auto has_complete_definition(info) -> bool;
consteval auto is_enumerable_type(info) -> bool;
consteval auto is_template(info) -> bool;
consteval auto is_function_template(info) -> bool;
consteval auto is_variable_template(info) -> bool;
@@ -483,6 +483,7 @@ enum : unsigned {
__metafn_is_alias,
__metafn_is_complete_type,
__metafn_has_complete_definition,
__metafn_is_enumerable_type,
__metafn_is_template,
__metafn_is_function_template,
__metafn_is_variable_template,
@@ -1076,13 +1077,13 @@ consteval auto nonstatic_data_members_of(info r) -> vector<info> {
ranges::to<vector>();
}
// Returns true if the reflected entity has a complete definition.
consteval auto has_complete_definition(info r) -> bool {
return __metafunction(detail::__metafn_has_complete_definition, r);
// Returns true if the reflected entity is an enumerable type.
consteval auto is_enumerable_type(info r) -> bool {
return __metafunction(detail::__metafn_is_enumerable_type, r);
}
consteval auto enumerators_of(info r) -> vector<info> {
if (!has_complete_definition(r))
if (!is_enumerable_type(r))
throw "Reflection must represent an enumeration with a definition";
using iterator =
@@ -2375,6 +2376,11 @@ consteval auto nonstatic_data_members_of(info r) -> vector<info> {
return nonstatic_data_members_of(r, access_context::unchecked());
}
[[deprecated("replaced with 'is_enumerable_type' in P2996R11")]]
consteval auto has_complete_definition(info r) -> bool {
return __metafunction(detail::__metafn_has_complete_definition, r);
}
#endif // __has_feature(access_contexts)

View File

@@ -363,62 +363,62 @@ static_assert(is_public(bases_of(^^D2<B1, B3>,
namespace enumerators {
enum class EnumCls;
static_assert(!has_complete_definition(^^EnumCls));
static_assert(!is_enumerable_type(^^EnumCls));
enum class EnumCls {
A,
B = has_complete_definition(^^EnumCls) ? 1 : 0,
B = is_enumerable_type(^^EnumCls) ? 1 : 0,
C,
};
static_assert(int(EnumCls::B) == 0);
static_assert(has_complete_definition(^^EnumCls));
static_assert(is_enumerable_type(^^EnumCls));
static_assert(enumerators_of(^^EnumCls) ==
std::vector{^^EnumCls::A, ^^EnumCls::B, ^^EnumCls::C});
struct Cls { enum Enum { A, B, C }; };
static_assert(!has_complete_definition(^^::));
static_assert(has_complete_definition(^^Cls::Enum));
static_assert(!is_enumerable_type(^^::));
static_assert(is_enumerable_type(^^Cls::Enum));
static_assert(enumerators_of(^^Cls::Enum) ==
std::vector{^^Cls::A, ^^Cls::B, ^^Cls::C});
} // namespace enumerators
// ====================
// complete_definitions
// ====================
// ================
// enumerable_types
// ================
namespace complete_definitions {
namespace enumerable_types {
enum E : int;
static_assert(!has_complete_definition(^^E));
static_assert(!is_enumerable_type(^^E));
enum E : int { A = has_complete_definition(^^E) ? 1 : 0 };
enum E : int { A = is_enumerable_type(^^E) ? 1 : 0 };
static_assert(E::A == 0);
static_assert(has_complete_definition(^^E));
static_assert(is_enumerable_type(^^E));
struct S;
static_assert(!has_complete_definition(^^S));
static_assert(!is_enumerable_type(^^S));
struct S {
void fn() {
static_assert(has_complete_definition(^^S));
static_assert(is_enumerable_type(^^S));
}
static_assert(!has_complete_definition(^^S));
static_assert(!is_enumerable_type(^^S));
};
static_assert(has_complete_definition(^^S));
static_assert(is_enumerable_type(^^S));
void fn();
static_assert(!has_complete_definition(^^fn));
static_assert(!is_enumerable_type(^^fn));
void fn() {
static_assert(!has_complete_definition(^^fn));
static_assert(!is_enumerable_type(^^fn));
}
static_assert(has_complete_definition(^^fn));
static_assert(!is_enumerable_type(^^fn));
static_assert(!has_complete_definition(^^::));
static_assert(!has_complete_definition(^^int));
static_assert(!has_complete_definition(^^std::vector));
static_assert(!is_enumerable_type(^^::));
static_assert(!is_enumerable_type(^^int));
static_assert(!is_enumerable_type(^^std::vector));
} // namespace complete_definitions
} // namespace enumrable_types
// ====================
// deduced_return_types