From 6724fb0d3ffaa92fcd2d7f1aa447fd3a2f798a89 Mon Sep 17 00:00:00 2001 From: Dan Katz Date: Fri, 7 Mar 2025 17:12:23 -0500 Subject: [PATCH] Implement 'is_enumerable_type'. --- clang/lib/AST/ExprConstantMeta.cpp | 56 +++++++++++++++---- libcxx/include/experimental/meta | 16 ++++-- .../members-and-subobjects.pass.cpp | 46 +++++++-------- 3 files changed, 78 insertions(+), 40 deletions(-) diff --git a/clang/lib/AST/ExprConstantMeta.cpp b/clang/lib/AST/ExprConstantMeta.cpp index 107438ac6040..97f2cdda82a8 100644 --- a/clang/lib/AST/ExprConstantMeta.cpp +++ b/clang/lib/AST/ExprConstantMeta.cpp @@ -377,6 +377,12 @@ static bool has_complete_definition(APValue &Result, ASTContext &C, ArrayRef 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 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 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(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, diff --git a/libcxx/include/experimental/meta b/libcxx/include/experimental/meta index 8f2b115b5616..50b5308bd7b5 100644 --- a/libcxx/include/experimental/meta +++ b/libcxx/include/experimental/meta @@ -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 { ranges::to(); } -// 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 { - 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 { 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) diff --git a/libcxx/test/std/experimental/reflection/members-and-subobjects.pass.cpp b/libcxx/test/std/experimental/reflection/members-and-subobjects.pass.cpp index 07c71e7fc336..cfb2e75161d3 100644 --- a/libcxx/test/std/experimental/reflection/members-and-subobjects.pass.cpp +++ b/libcxx/test/std/experimental/reflection/members-and-subobjects.pass.cpp @@ -363,62 +363,62 @@ static_assert(is_public(bases_of(^^D2, 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