From fc1480d51fc40423beac47df16e0b5c3c4f3817a Mon Sep 17 00:00:00 2001 From: Dan Katz Date: Sat, 3 May 2025 16:57:13 -0400 Subject: [PATCH 1/7] Fix 'bases_of' with aliases. --- clang/lib/AST/ExprConstantMeta.cpp | 6 +++++- .../experimental/reflection/members-and-subobjects.pass.cpp | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/clang/lib/AST/ExprConstantMeta.cpp b/clang/lib/AST/ExprConstantMeta.cpp index b393fcd7d2f2..1cfa83f58204 100644 --- a/clang/lib/AST/ExprConstantMeta.cpp +++ b/clang/lib/AST/ExprConstantMeta.cpp @@ -1767,7 +1767,11 @@ bool get_ith_base_of(APValue &Result, ASTContext &C, MetaActions &Meta, switch (RV.getReflectionKind()) { case ReflectionKind::Type: { - Decl *typeDecl = findTypeDecl(RV.getReflectedType()); + QualType QT = RV.getReflectedType(); + QT = desugarType(QT, /*UnwrapAliases=*/true, /*DropCV=*/false, + /*DropRefs=*/false); + + Decl *typeDecl = findTypeDecl(QT); if (auto cxxRecordDecl = dyn_cast_or_null(typeDecl)) { Meta.EnsureInstantiated(typeDecl, Range); 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 a31d6c74247c..32cf1a4c6378 100644 --- a/libcxx/test/std/experimental/reflection/members-and-subobjects.pass.cpp +++ b/libcxx/test/std/experimental/reflection/members-and-subobjects.pass.cpp @@ -329,6 +329,7 @@ struct B1 {}; struct B2 {}; struct B3 {}; struct D1 : public B1, virtual protected B2, private B3 {}; +using Alias = D1; static_assert(bases_of(^^B1, access_context::unchecked()).size() == 0); static_assert(type_of(bases_of(^^D1, access_context::unchecked())[0]) == ^^B1); static_assert(type_of(bases_of(^^D1, access_context::unchecked())[1]) == ^^B2); @@ -346,6 +347,9 @@ static_assert(!is_protected(bases_of(^^D1, access_context::unchecked())[2])); static_assert(is_private(bases_of(^^D1, access_context::unchecked())[2])); static_assert(!is_virtual(bases_of(^^D1, access_context::unchecked())[2])); +static_assert(type_of(bases_of(^^Alias, + access_context::unchecked())[0]) == ^^B1); + template struct D2 : Bases... {}; static_assert(type_of(bases_of(^^D2, access_context::unchecked())[0]) == ^^B1); From a11f0d2d142b73182a01b3ee9f28781d082f0d67 Mon Sep 17 00:00:00 2001 From: Dan Katz Date: Mon, 5 May 2025 18:13:50 -0400 Subject: [PATCH 2/7] Fix annotations bug. --- clang/include/clang/Basic/Attr.td | 1 + clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 12 ++++++++++++ .../reflection/p3394-annotations.pass.cpp | 16 ++++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 4a8dde84d498..a83dfcd955f0 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1025,6 +1025,7 @@ public: let HasCustomParsing = 1; let TemplateDependent = 1; + let MeaningfulToClassTemplateDefinition = 1; let Documentation = [InternalOnly]; } diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 20a98193d961..1add715713b6 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -737,10 +737,16 @@ void Sema::InstantiateAttrsForDecl( // FIXME: This function is called multiple times for the same template // specialization. We should only instantiate attributes that were added // since the previous instantiation. + bool AddAnnotations = New->attrs().empty(); for (const auto *TmplAttr : Tmpl->attrs()) { if (!isRelevantAttr(*this, New, TmplAttr)) continue; + if (isa(TmplAttr) && !AddAnnotations) + // See https://github.com/llvm/llvm-project/issues/138596 + // Just skip here to avoid duplication of the attribute. + continue; + // FIXME: If any of the special case versions from InstantiateAttrs become // applicable to template declaration, we'll need to add them here. CXXThisScopeRAII ThisScope( @@ -778,6 +784,7 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs, const Decl *Tmpl, Decl *New, LateInstantiatedAttrVec *LateAttrs, LocalInstantiationScope *OuterMostScope) { + bool AddAnnotations = New->attrs().empty(); for (const auto *TmplAttr : Tmpl->attrs()) { if (!isRelevantAttr(*this, New, TmplAttr)) continue; @@ -879,6 +886,11 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs, continue; } + if (isa(TmplAttr) && !AddAnnotations) + // See https://github.com/llvm/llvm-project/issues/138596 + // Just skip here to avoid duplication of the attribute. + continue; + // Existing DLL attribute on the instantiation takes precedence. if (TmplAttr->getKind() == attr::DLLExport || TmplAttr->getKind() == attr::DLLImport) { diff --git a/libcxx/test/std/experimental/reflection/p3394-annotations.pass.cpp b/libcxx/test/std/experimental/reflection/p3394-annotations.pass.cpp index dad55d989af8..84e11b7c1161 100644 --- a/libcxx/test/std/experimental/reflection/p3394-annotations.pass.cpp +++ b/libcxx/test/std/experimental/reflection/p3394-annotations.pass.cpp @@ -70,6 +70,7 @@ static_assert((annotations_of(^^TCls) | std::meta::reflect_value(5), std::meta::reflect_value(6), std::meta::reflect_value(3.0f)}); + static_assert((annotations_of(^^TFn) | std::views::transform(std::meta::value_of) | std::ranges::to()) == @@ -251,4 +252,19 @@ static_assert(c3 == -1); static_assert(c4 == 4); } // namespace ledger_based_consteval_variable + // =========================== + // templated_class_annotations + // =========================== + +namespace templated_class_annotations { +template +struct X { + struct [[=1]] C; + struct [[=2]] D { }; +}; + +static_assert(annotations_of(^^X::C).size() == 1); +static_assert(annotations_of(^^X::D).size() == 1); +} // namespace templated_class_annotations + int main() { } From 16f4b134248113ffeaf88563c3812fb44f60b3a2 Mon Sep 17 00:00:00 2001 From: Dan Katz Date: Tue, 6 May 2025 15:52:57 -0400 Subject: [PATCH 3/7] Fixes/improvements to 'substitute'. --- .../clang/Basic/DiagnosticMetafnKinds.td | 3 + clang/lib/AST/ExprConstantMeta.cpp | 100 ++++-------------- clang/lib/Sema/SemaReflect.cpp | 1 - libcxx/include/experimental/meta | 15 +-- ...stitute.pass.cpp => substitute.verify.cpp} | 26 ++++- 5 files changed, 58 insertions(+), 87 deletions(-) rename libcxx/test/std/experimental/reflection/{substitute.pass.cpp => substitute.verify.cpp} (96%) diff --git a/clang/include/clang/Basic/DiagnosticMetafnKinds.td b/clang/include/clang/Basic/DiagnosticMetafnKinds.td index 33832fb42236..cc3a162b88c2 100644 --- a/clang/include/clang/Basic/DiagnosticMetafnKinds.td +++ b/clang/include/clang/Basic/DiagnosticMetafnKinds.td @@ -51,6 +51,9 @@ def metafn_parent_of_extern_c : Note< // Substitution and invocation. def metafn_cannot_be_arg : Note< "a reflection of %0 cannot represent a %select{function|template}1 argument">; +def metafn_undeduced_placeholder : Note< + "cannot form a reflection of function %0 whose type %1 contains an undeduced " + "placeholder">; def metafn_cannot_invoke : Note<"cannot invoke %0">; def metafn_no_specialization_found : Note< "no specialization of the function template %0 matched the provided " diff --git a/clang/lib/AST/ExprConstantMeta.cpp b/clang/lib/AST/ExprConstantMeta.cpp index 1cfa83f58204..7dafb758f9e6 100644 --- a/clang/lib/AST/ExprConstantMeta.cpp +++ b/clang/lib/AST/ExprConstantMeta.cpp @@ -146,12 +146,6 @@ static bool template_of(APValue &Result, ASTContext &C, MetaActions &Meta, QualType ResultTy, SourceRange Range, ArrayRef Args, Decl *ContainingDecl); -static bool can_substitute(APValue &Result, ASTContext &C, MetaActions &Meta, - EvalFn Evaluator, DiagFn Diagnoser, - bool AllowInjection, QualType ResultTy, - SourceRange Range, ArrayRef Args, - Decl *ContainingDecl); - static bool substitute(APValue &Result, ASTContext &C, MetaActions &Meta, EvalFn Evaluator, DiagFn Diagnoser, bool AllowInjection, QualType ResultTy, SourceRange Range, @@ -744,8 +738,7 @@ static constexpr Metafunction Metafunctions[] = { { Metafunction::MFRK_metaInfo, 1, 1, object_of }, { Metafunction::MFRK_metaInfo, 1, 1, value_of }, { Metafunction::MFRK_metaInfo, 1, 1, template_of }, - { Metafunction::MFRK_bool, 3, 3, can_substitute }, - { Metafunction::MFRK_metaInfo, 3, 3, substitute }, + { Metafunction::MFRK_metaInfo, 4, 4, substitute }, { Metafunction::MFRK_spliceFromArg, 2, 2, extract }, { Metafunction::MFRK_bool, 1, 1, is_public }, { Metafunction::MFRK_bool, 1, 1, is_protected }, @@ -880,6 +873,10 @@ static APValue makeBool(ASTContext &C, bool B) { return APValue(C.MakeIntValue(B, C.BoolTy)); } +static APValue makeReflection(std::nullptr_t) { + return APValue(ReflectionKind::Null, nullptr); +} + static APValue makeReflection(QualType QT) { return APValue(ReflectionKind::Type, QT.getAsOpaquePtr()); } @@ -2765,71 +2762,6 @@ static TemplateArgument TArgFromReflection(ASTContext &C, EvalFn Evaluator, return TemplateArgument(); } -// TODO(P2996): Abstract this out, and use as an implementation detail of -// 'substitute'. -bool can_substitute(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( - Args[1]->getType()->getPointeeOrArrayElementType()->isReflectionType()); - assert(Args[2]->getType()->isIntegerType()); - - APValue Template; - if (!Evaluator(Template, Args[0], true)) - return true; - - if (!Template.isReflectedTemplate()) - return DiagnoseReflectionKind(Diagnoser, Range, "a template", - DescriptionOf(Template)); - TemplateDecl *TDecl = Template.getReflectedTemplate().getAsTemplateDecl(); - if (TDecl->isInvalidDecl()) - return true; - - SmallVector TArgs; - { - // Evaluate how many template arguments were provided. - APValue NumArgs; - if (!Evaluator(NumArgs, Args[2], true)) - return true; - size_t nArgs = NumArgs.getInt().getExtValue(); - TArgs.reserve(nArgs); - - for (uint64_t k = 0; k < nArgs; ++k) { - llvm::APInt Idx(C.getTypeSize(C.getSizeType()), k, false); - Expr *Synthesized = IntegerLiteral::Create(C, Idx, C.getSizeType(), - Args[1]->getExprLoc()); - - Synthesized = new (C) ArraySubscriptExpr(Args[1], Synthesized, - C.MetaInfoTy, VK_LValue, - OK_Ordinary, Range.getBegin()); - if (Synthesized->isValueDependent() || Synthesized->isTypeDependent()) - return true; - - APValue Unwrapped; - if (!Evaluator(Unwrapped, Synthesized, true) || - !Unwrapped.isReflection()) - return true; - Unwrapped = MaybeUnproxy(C, Unwrapped); - if (!CanActAsTemplateArg(Unwrapped)) - return SetAndSucceed(Result, makeBool(C, false)); - - TemplateArgument TArg = TArgFromReflection(C, Evaluator, Unwrapped, - Range.getBegin()); - if (TArg.isNull()) - return true; - TArgs.push_back(TArg); - } - } - SmallVector ExpandedTArgs; - expandTemplateArgPacks(TArgs, ExpandedTArgs); - - bool CanSub = Meta.CheckTemplateArgumentList(TDecl, ExpandedTArgs, true, - Args[0]->getExprLoc()); - return SetAndSucceed(Result, makeBool(C, CanSub)); -} - bool substitute(APValue &Result, ASTContext &C, MetaActions &Meta, EvalFn Evaluator, DiagFn Diagnoser, bool AllowInjection, QualType ResultTy, SourceRange Range, ArrayRef Args, @@ -2851,6 +2783,14 @@ bool substitute(APValue &Result, ASTContext &C, MetaActions &Meta, if (TDecl->isInvalidDecl()) return true; + APValue DiagnoseAPV; + if (!Evaluator(DiagnoseAPV, Args[3], true)) + return true; + bool NoDiagnose = !DiagnoseAPV.getInt().getBoolValue(); + auto ElideDiagnosis = [&] { + return SetAndSucceed(Result, makeReflection(nullptr)); + }; + SmallVector TArgs; { // Evaluate how many template arguments were provided. @@ -2877,8 +2817,9 @@ bool substitute(APValue &Result, ASTContext &C, MetaActions &Meta, return true; Unwrapped = MaybeUnproxy(C, Unwrapped); if (!CanActAsTemplateArg(Unwrapped)) - return Diagnoser(Range.getBegin(), diag::metafn_cannot_be_arg) - << DescriptionOf(Unwrapped) << 1 << Range; + return NoDiagnose ? ElideDiagnosis() : + Diagnoser(Range.getBegin(), diag::metafn_cannot_be_arg) + << DescriptionOf(Unwrapped) << 1 << Range; TemplateArgument TArg = TArgFromReflection(C, Evaluator, Unwrapped, Range.getBegin()); @@ -2902,9 +2843,9 @@ bool substitute(APValue &Result, ASTContext &C, MetaActions &Meta, if (C.checkCachedSubstitution(SubstitutionHash, &Result)) return false; - if (!Meta.CheckTemplateArgumentList(TDecl, ExpandedTArgs, false, + if (!Meta.CheckTemplateArgumentList(TDecl, ExpandedTArgs, NoDiagnose, Args[0]->getExprLoc())) - return true; + return NoDiagnose ? ElideDiagnosis() : true; for (const auto &TArg : ExpandedTArgs) if (TArg.getKind() == TemplateArgument::Expression && TArg.getAsExpr()->containsErrors()) @@ -2942,6 +2883,11 @@ bool substitute(APValue &Result, ASTContext &C, MetaActions &Meta, FunctionDecl *Spec = Meta.Substitute(FTD, ExpandedTArgs, Range.getBegin()); assert(Spec && "substitution failed after validating arguments?"); + if (Spec->getReturnType()->isUndeducedType()) + return NoDiagnose ? ElideDiagnosis() : + Diagnoser(Range.getBegin(), diag::metafn_undeduced_placeholder) + << Spec << Spec->getType() << Range; + APValue RV = makeReflection(Spec); //C.recordCachedSubstitution(SubstitutionHash, RV); return SetAndSucceed(Result, RV); diff --git a/clang/lib/Sema/SemaReflect.cpp b/clang/lib/Sema/SemaReflect.cpp index 79f2ad0cebbb..56af1491e5f9 100644 --- a/clang/lib/Sema/SemaReflect.cpp +++ b/clang/lib/Sema/SemaReflect.cpp @@ -26,7 +26,6 @@ #include "clang/Sema/Template.h" #include "clang/Sema/TemplateDeduction.h" #include "llvm/Support/raw_ostream.h" -#include using namespace clang; using namespace sema; diff --git a/libcxx/include/experimental/meta b/libcxx/include/experimental/meta index ae9267067893..bdf01fc33a17 100644 --- a/libcxx/include/experimental/meta +++ b/libcxx/include/experimental/meta @@ -446,7 +446,6 @@ enum : unsigned { __metafn_object_of, __metafn_value_of, __metafn_template_of, - __metafn_can_substitute, __metafn_substitute, __metafn_extract, __metafn_is_public, @@ -907,14 +906,16 @@ consteval auto is_data_member_spec(info r) -> bool { // Returns whether 'templ' substituted with 'args' forms a valid template-id. template > consteval auto can_substitute(info templ, R &&args) -> bool { + info sub; if constexpr (ranges::contiguous_range) { - return __metafunction(detail::__metafn_can_substitute, templ, - ranges::data(args), ranges::size(args)); + sub = __metafunction(detail::__metafn_substitute, templ, + ranges::data(args), ranges::size(args), false); } else { vector vargs = args | ranges::to(); - return __metafunction(detail::__metafn_can_substitute, templ, - vargs.data(), vargs.size()); + sub = __metafunction(detail::__metafn_substitute, templ, + vargs.data(), vargs.size(), false); } + return sub != info{}; } // Returns a reflection representing the template instantiation of the entity @@ -923,11 +924,11 @@ template > consteval auto substitute(info templ, R &&args) -> info { if constexpr (ranges::contiguous_range) { return __metafunction(detail::__metafn_substitute, templ, - ranges::data(args), ranges::size(args)); + ranges::data(args), ranges::size(args), true); } else { vector vargs = args | ranges::to(); return __metafunction(detail::__metafn_substitute, templ, vargs.data(), - vargs.size()); + vargs.size(), true); } } diff --git a/libcxx/test/std/experimental/reflection/substitute.pass.cpp b/libcxx/test/std/experimental/reflection/substitute.verify.cpp similarity index 96% rename from libcxx/test/std/experimental/reflection/substitute.pass.cpp rename to libcxx/test/std/experimental/reflection/substitute.verify.cpp index de553ce603fa..974bfbc101b2 100644 --- a/libcxx/test/std/experimental/reflection/substitute.pass.cpp +++ b/libcxx/test/std/experimental/reflection/substitute.verify.cpp @@ -10,7 +10,6 @@ // UNSUPPORTED: c++03 || c++11 || c++14 || c++17 || c++20 // ADDITIONAL_COMPILE_FLAGS: -freflection -// ADDITIONAL_COMPILE_FLAGS: -Wno-unneeded-internal-declaration // // @@ -348,7 +347,7 @@ template