From e4947d8b266a9943f6d2bd1a9b6d88b4b4da634c Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski Date: Sat, 8 Jul 2023 06:26:45 -0400 Subject: [PATCH] feat: improved declaration resolution --- lib/AST/ASTVisitor.cpp | 307 +++- lib/AST/ASTVisitorHelpers.hpp | 49 +- ...p => class-template-specializations-1.cpp} | 0 .../class-template-specializations-1.xml | 870 +++++++++++ .../class-template-specializations-2.cpp | 50 + .../class-template-specializations-2.xml | 106 ++ .../class-template-specializations-3.cpp | 74 + .../class-template-specializations-3.xml | 185 +++ .../class-template-specializations.xml | 1337 ----------------- .../implicit-instantiation-member-ref.xml | 18 - 10 files changed, 1585 insertions(+), 1411 deletions(-) rename test-files/old-tests/{class-template-specializations.cpp => class-template-specializations-1.cpp} (100%) create mode 100644 test-files/old-tests/class-template-specializations-1.xml create mode 100644 test-files/old-tests/class-template-specializations-2.cpp create mode 100644 test-files/old-tests/class-template-specializations-2.xml create mode 100644 test-files/old-tests/class-template-specializations-3.cpp create mode 100644 test-files/old-tests/class-template-specializations-3.xml delete mode 100644 test-files/old-tests/class-template-specializations.xml diff --git a/lib/AST/ASTVisitor.cpp b/lib/AST/ASTVisitor.cpp index 68190e63a..bbb7fd1a5 100644 --- a/lib/AST/ASTVisitor.cpp +++ b/lib/AST/ASTVisitor.cpp @@ -18,9 +18,8 @@ #include "Support/Debug.hpp" #include "Lib/Diagnostics.hpp" #include +#include #include -#include -#include #include #include #include @@ -154,8 +153,8 @@ class ASTVisitor ASTContext& context, Sema& sema) noexcept : config_(config) - , compiler_(compiler) , diags_(diags) + , compiler_(compiler) , context_(context) , source_(context.getSourceManager()) , sema_(sema) @@ -398,12 +397,8 @@ class ASTVisitor ! isa(N) && ! isa(N)) { - if(auto* R = dyn_cast(N)) - { - if(auto* P = R->getTemplateInstantiationPattern()) - N = P; - } - else if(auto* TD = dyn_cast(N)) + N = cast(getInstantiatedFrom(N)); + if(auto* TD = dyn_cast(N)) { if(auto* PTD = lookupTypedefInPrimary(TD)) N = PTD; @@ -663,8 +658,26 @@ class ASTVisitor auto* T = cast(type); auto name = T->getTemplateName(); MRDOX_ASSERT(! name.isNull()); - auto I = makeTypeInfo( - name.getAsTemplateDecl(), quals); + NamedDecl* ND = name.getAsTemplateDecl(); + // if this is a specialization of a alias template, + // the canonical type will be the named type. in such cases, + // we will use the template name. otherwise, we use the + // canonical type whenever possible. + if(! T->isTypeAlias()) + { + auto* CT = qt.getCanonicalType().getTypePtrOrNull(); + if(auto* ICT = dyn_cast_or_null< + InjectedClassNameType>(CT)) + { + ND = ICT->getDecl(); + } + if(auto* RT = dyn_cast_or_null< + RecordType>(CT)) + { + ND = RT->getDecl(); + } + } + auto I = makeTypeInfo(ND, quals); buildTemplateArgs(I->TemplateArgs, T->template_arguments()); return I; } @@ -724,6 +737,213 @@ class ASTVisitor } } + /** Get the user-written `Decl` for a `Decl` + + Given a `Decl` `D`, `getInstantiatedFrom` will return the + user-written `Decl` corresponding to `D`. For specializations + which were implicitly instantiated, this will be whichever `Decl` + was used as the pattern for instantiation. + */ + Decl* + getInstantiatedFrom( + Decl* D) + { + if(! D) + return nullptr; + + // KRYSTIAN TODO: support enums & aliases/alias templates + return visit(D, [&]( + DeclTy* DT) -> Decl* + { + constexpr Decl::Kind kind = DeclToKind(); + + // ------------------------------------------------ + + // FunctionTemplate + if constexpr(kind == Decl::FunctionTemplate) + { + while(auto* MT = DT->getInstantiatedFromMemberTemplate()) + { + if(DT->isMemberSpecialization()) + break; + DT = MT; + } + return DT; + } + else if constexpr(kind == Decl::ClassScopeFunctionSpecialization) + { + // ClassScopeFunctionSpecializationDecls only exist within the lexical + // definition of a ClassTemplateDecl or ClassTemplatePartialSpecializationDecl. + // they are never created during instantiation -- not even during the instantiation + // of a class template with a member class template containing such a declaration. + return DT; + } + // FunctionDecl + // CXXMethodDecl + // CXXConstructorDecl + // CXXConversionDecl + // CXXDeductionGuideDecl + // CXXDestructorDecl + if constexpr(std::derived_from) + { + FunctionDecl* FD = DT; + + for(auto* R : FD->redecls()) + { + if(MemberSpecializationInfo* MSI = + R->getMemberSpecializationInfo()) + { + if(MSI->isExplicitSpecialization()) + FD = R; + else + FD = cast( + MSI->getInstantiatedFrom()); + break; + } + else if(R->getTemplateSpecializationKind() == + TSK_ImplicitInstantiation) + { + if(auto* FTD = FD->getPrimaryTemplate()) + { + FTD = cast( + getInstantiatedFrom(FTD)); + FD = FTD->getTemplatedDecl(); + } + break; + } + } + + if(FunctionDecl* DD = FD->getDefinition()) + FD = DD; + + return FD; + } + + // ------------------------------------------------ + + // ClassTemplate + if constexpr(kind == Decl::ClassTemplate) + { + while(auto* MT = DT->getInstantiatedFromMemberTemplate()) + { + if(DT->isMemberSpecialization()) + break; + DT = MT; + } + return DT; + } + // ClassTemplatePartialSpecialization + else if constexpr(kind == Decl::ClassTemplatePartialSpecialization) + { + while(auto* MT = DT->getInstantiatedFromMember()) + { + if(DT->isMemberSpecialization()) + break; + DT = MT; + } + } + // ClassTemplateSpecialization + else if constexpr(kind == Decl::ClassTemplateSpecialization) + { + if(! DT->isExplicitSpecialization()) + { + auto inst_from = DT->getSpecializedTemplateOrPartial(); + if(auto* CTPSD = inst_from.template dyn_cast< + ClassTemplatePartialSpecializationDecl*>()) + { + MRDOX_ASSERT(DT != CTPSD); + return getInstantiatedFrom(CTPSD); + } + // explicit instantiation declaration/definition + else if(auto* CTD = inst_from.template dyn_cast< + ClassTemplateDecl*>()) + { + return getInstantiatedFrom(CTD); + } + } + } + // CXXRecordDecl + // ClassTemplateSpecialization + // ClassTemplatePartialSpecialization + if constexpr(std::derived_from) + { + CXXRecordDecl* RD = DT; + while(MemberSpecializationInfo* MSI = + RD->getMemberSpecializationInfo()) + { + // if this is a member of an explicit specialization, + // then we have the correct declaration + if(MSI->isExplicitSpecialization()) + break; + RD = cast(MSI->getInstantiatedFrom()); + } + return RD; + } + + // ------------------------------------------------ + + // VarTemplate + if constexpr(kind == Decl::VarTemplate) + { + while(auto* MT = DT->getInstantiatedFromMemberTemplate()) + { + if(DT->isMemberSpecialization()) + break; + DT = MT; + } + return DT; + } + // VarTemplatePartialSpecialization + else if constexpr(kind == Decl::VarTemplatePartialSpecialization) + { + while(auto* MT = DT->getInstantiatedFromMember()) + { + if(DT->isMemberSpecialization()) + break; + DT = MT; + } + } + // VarTemplateSpecialization + else if constexpr(kind == Decl::VarTemplateSpecialization) + { + if(! DT->isExplicitSpecialization()) + { + auto inst_from = DT->getSpecializedTemplateOrPartial(); + if(auto* VTPSD = inst_from.template dyn_cast< + VarTemplatePartialSpecializationDecl*>()) + { + MRDOX_ASSERT(DT != VTPSD); + return getInstantiatedFrom(VTPSD); + } + // explicit instantiation declaration/definition + else if(auto* VTD = inst_from.template dyn_cast< + VarTemplateDecl*>()) + { + return getInstantiatedFrom(VTD); + } + } + } + // VarDecl + // VarTemplateSpecialization + // VarTemplatePartialSpecialization + if constexpr(std::derived_from) + { + VarDecl* VD = DT; + while(MemberSpecializationInfo* MSI = + VD->getMemberSpecializationInfo()) + { + if(MSI->isExplicitSpecialization()) + break; + VD = cast(MSI->getInstantiatedFrom()); + } + return VD; + } + + return DT; + }); + } + + template Integer getValue(const llvm::APInt& V) @@ -867,16 +1087,10 @@ class ASTVisitor void parseTemplateArgs( TemplateInfo& I, - const ClassTemplateSpecializationDecl* spec) + ClassTemplateSpecializationDecl* spec) { - // KRYSTIAN FIXME: should this use getTemplateInstantiationPattern? - // ID of the primary template - if(ClassTemplateDecl* primary = spec->getSpecializedTemplate()) - { - if(auto* MT = primary->getInstantiatedFromMemberTemplate()) - primary = MT; + if(Decl* primary = getInstantiatedFrom(spec->getSpecializedTemplate())) extractSymbolID(primary, I.Primary.emplace()); - } // KRYSTIAN NOTE: when this is a partial specialization, we could use // ClassTemplatePartialSpecializationDecl::getTemplateArgsAsWritten const TypeSourceInfo* type_written = spec->getTypeAsWritten(); @@ -891,20 +1105,15 @@ class ASTVisitor void parseTemplateArgs( TemplateInfo& I, - const VarTemplateSpecializationDecl* spec) - { - // KRYSTIAN FIXME: should this use getTemplateInstantiationPattern? - // ID of the primary template - if(VarTemplateDecl* primary = spec->getSpecializedTemplate()) - { - if(auto* MT = primary->getInstantiatedFromMemberTemplate()) - primary = MT; - // unlike function and class templates, the USR generated - // for variable templates differs from that of the VarDecl - // returned by getTemplatedDecl. this might be a clang bug. - // the USR of the templated VarDecl seems to be the correct one. + VarTemplateSpecializationDecl* spec) + { + // unlike function and class templates, the USR generated + // for variable templates differs from that of the VarDecl + // returned by getTemplatedDecl. this might be a clang bug. + // the USR of the templated VarDecl seems to be the correct one. + if(auto* primary = dyn_cast( + getInstantiatedFrom(spec->getSpecializedTemplate()))) extractSymbolID(primary->getTemplatedDecl(), I.Primary.emplace()); - } const ASTTemplateArgumentListInfo* args_written = nullptr; // getTemplateArgsInfo returns nullptr for partial specializations, // so we use getTemplateArgsAsWritten if this is a partial specialization @@ -922,17 +1131,11 @@ class ASTVisitor void parseTemplateArgs( TemplateInfo& I, - const FunctionTemplateSpecializationInfo* spec) + FunctionTemplateSpecializationInfo* spec) { - // KRYSTIAN FIXME: should this use getTemplateInstantiationPattern? - // ID of the primary template // KRYSTIAN NOTE: do we need to check I->Primary.has_value()? - if(FunctionTemplateDecl* primary = spec->getTemplate()) - { - if(auto* MT = primary->getInstantiatedFromMemberTemplate()) - primary = MT; + if(Decl* primary = getInstantiatedFrom(spec->getTemplate())) extractSymbolID(primary, I.Primary.emplace()); - } // TemplateArguments is used instead of TemplateArgumentsAsWritten // because explicit specializations of function templates may have // template arguments deduced from their return type and parameters @@ -1134,9 +1337,6 @@ class ASTVisitor DeclContext* parent_context = child->getDeclContext(); do { - // Decl* parent = getInstantiatedFrom( - // cast(parent_context)); - // parent_context = cast(parent); Decl* parent = cast(parent_context); SymbolID parent_id = extractSymbolID(parent); switch(parent_context->getDeclKind()) @@ -1243,17 +1443,16 @@ class ASTVisitor if(! created) return; - const CXXRecordDecl* RD = - D->getTemplateInstantiationPattern(); - MRDOX_ASSERT(RD); - + NamedDecl* PD = cast( + getInstantiatedFrom(D)); + buildTemplateArgs(I.Args, D->getTemplateArgs().asArray()); - extractSymbolID(RD, I.Primary); - I.Name = extractName(RD); + extractSymbolID(PD, I.Primary); + I.Name = extractName(PD); - getParentNamespaces(I, D); + getParentNamespaces(I, D); } //------------------------------------------------ @@ -1596,7 +1795,7 @@ class ASTVisitor I.ReturnType = buildTypeInfo( D->getReturnType()); - if(const auto* ftsi = D->getTemplateSpecializationInfo()) + if(auto* ftsi = D->getTemplateSpecializationInfo()) { if(! I.Template) I.Template = std::make_unique(); @@ -2021,6 +2220,7 @@ traverse(ClassTemplateDecl* D, AccessSpecifier A) { CXXRecordDecl* RD = D->getTemplatedDecl(); + if(! shouldExtract(RD)) return true; @@ -2086,6 +2286,7 @@ traverse(FunctionTemplateDecl* D, AccessSpecifier A) { FunctionDecl* FD = D->getTemplatedDecl(); + // check whether to extract using the templated declaration. // this is done because the template-head may be implicit // (e.g. for an abbreviated function template with no template-head) @@ -2376,6 +2577,8 @@ class ASTVisitorConsumer // traverse the translation unit visitor.traverseContext( Context.getTranslationUnitDecl()); + + // dumpDeclTree(Context.getTranslationUnitDecl()); // convert results to bitcode for(auto& info : visitor.results()) @@ -2433,7 +2636,7 @@ class ASTVisitorConsumer } void HandleInlineFunctionDefinition(FunctionDecl* D) override { } - void HandleTagDeclDefinition(TagDecl* D) { } + void HandleTagDeclDefinition(TagDecl* D) override { } void HandleTagDeclRequiredDefinition(const TagDecl* D) override { } void HandleInterestingDecl(DeclGroupRef DG) override { } void CompleteTentativeDefinition(VarDecl* D) override { } diff --git a/lib/AST/ASTVisitorHelpers.hpp b/lib/AST/ASTVisitorHelpers.hpp index 0ece13688..7fe9f226f 100644 --- a/lib/AST/ASTVisitorHelpers.hpp +++ b/lib/AST/ASTVisitorHelpers.hpp @@ -11,11 +11,12 @@ #define MRDOX_TOOL_AST_ASTVISITORHELPERS_HPP #include -#include -#include +#include +#include +#include #include -#include -#include +#include +#include #include namespace clang { @@ -237,6 +238,46 @@ convertToQualifierKind( } +template< + typename Visitor, + typename... Args, + typename Dependent = void> +decltype(auto) +visit( + Decl* D, + Visitor&& visitor, + Args&&... args) +{ + switch(D->getKind()) + { + #define ABSTRACT_DECL(DECL) + #define DECL(DERIVED, BASE) \ + case Decl::DERIVED: \ + return std::forward(visitor)( \ + static_cast(D), \ + std::forward(args)...); + + #include + + default: + MRDOX_UNREACHABLE(); + } +} + +template +consteval +Decl::Kind +DeclToKind() = delete; + +#define ABSTRACT_DECL(DECL) +#define DECL(DERIVED, BASE) \ + template<> \ + consteval \ + Decl::Kind \ + DeclToKind() { return Decl::DERIVED; } + +#include + } // mrdox } // clang diff --git a/test-files/old-tests/class-template-specializations.cpp b/test-files/old-tests/class-template-specializations-1.cpp similarity index 100% rename from test-files/old-tests/class-template-specializations.cpp rename to test-files/old-tests/class-template-specializations-1.cpp diff --git a/test-files/old-tests/class-template-specializations-1.xml b/test-files/old-tests/class-template-specializations-1.xml new file mode 100644 index 000000000..0cb04a454 --- /dev/null +++ b/test-files/old-tests/class-template-specializations-1.xml @@ -0,0 +1,870 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test-files/old-tests/class-template-specializations-2.cpp b/test-files/old-tests/class-template-specializations-2.cpp new file mode 100644 index 000000000..f1b06733f --- /dev/null +++ b/test-files/old-tests/class-template-specializations-2.cpp @@ -0,0 +1,50 @@ +template +struct A { }; + +template +struct A +{ + template + struct B { }; + + template + struct B { struct C { }; }; + + template<> + struct B { }; +}; + +template<> +struct A +{ + template + struct D + { + template + struct E { }; + + template + struct E + { + struct F { }; + }; + }; + + template<> + struct D + { + template + struct G { }; + }; +}; + +template<> +template<> +struct A::B::C { }; + +template +struct A::D::G { }; + +template<> +template<> +struct A::D::E::F { }; diff --git a/test-files/old-tests/class-template-specializations-2.xml b/test-files/old-tests/class-template-specializations-2.xml new file mode 100644 index 000000000..05dc162b8 --- /dev/null +++ b/test-files/old-tests/class-template-specializations-2.xml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + diff --git a/test-files/old-tests/class-template-specializations-3.cpp b/test-files/old-tests/class-template-specializations-3.cpp new file mode 100644 index 000000000..65995f610 --- /dev/null +++ b/test-files/old-tests/class-template-specializations-3.cpp @@ -0,0 +1,74 @@ +template +struct A +{ + template + struct B + { + struct C { }; + + template + struct D { }; + + template<> + struct D { }; + }; + + template<> + struct B + { + struct C { }; + + template + struct D { }; + + template<> + struct D { }; + }; +}; + +template<> +template +struct A::B +{ + struct C { }; + + template + struct D { }; + + template<> + struct D { }; +}; + +template<> +template<> +struct A::B +{ + struct C { }; + + template + struct D { }; + + template<> + struct D { }; +}; + +struct E +{ + A::B m0; + A::B m1; + A::B m2; + A::B m3; + A::B m4; + + A::B::C m5; + A::B::C m6; + A::B::C m7; + A::B::C m8; + A::B::C m9; + + A::B::D m10; + A::B::D m11; + A::B::D m12; + A::B::D m13; + A::B::D m14; +}; diff --git a/test-files/old-tests/class-template-specializations-3.xml b/test-files/old-tests/class-template-specializations-3.xml new file mode 100644 index 000000000..ee3980e49 --- /dev/null +++ b/test-files/old-tests/class-template-specializations-3.xml @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test-files/old-tests/class-template-specializations.xml b/test-files/old-tests/class-template-specializations.xml deleted file mode 100644 index 56fde3939..000000000 --- a/test-files/old-tests/class-template-specializations.xml +++ /dev/null @@ -1,1337 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/test-files/old-tests/implicit-instantiation-member-ref.xml b/test-files/old-tests/implicit-instantiation-member-ref.xml index 094a65d13..3c888053b 100644 --- a/test-files/old-tests/implicit-instantiation-member-ref.xml +++ b/test-files/old-tests/implicit-instantiation-member-ref.xml @@ -88,24 +88,6 @@ - - - - - - - -