From 6ba1b9075dc1fef6c32eafa71495bfec803321e4 Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Tue, 12 Apr 2022 15:51:16 +0200 Subject: [PATCH] Reland "[AST] Add a new TemplateKind for template decls found via a using decl."" This is the template version of https://reviews.llvm.org/D114251. This patch introduces a new template name kind (UsingTemplateName). The UsingTemplateName stores the found using-shadow decl (and underlying template can be retrieved from the using-shadow decl). With the new template name, we can be able to find the using decl that a template typeloc (e.g. TemplateSpecializationTypeLoc) found its underlying template, which is useful for tooling use cases (include cleaner etc). This patch merely focuses on adding the node to the AST. Next steps: - support using-decl in qualified template name; - update the clangd and other tools to use this new node; - add ast matchers for matching different kinds of template names; Differential Revision: https://reviews.llvm.org/D123127 --- clang-tools-extra/clangd/DumpAST.cpp | 1 + clang-tools-extra/clangd/SemanticHighlighting.cpp | 1 + clang/include/clang/AST/PropertiesBase.td | 10 ++++ clang/include/clang/AST/TemplateName.h | 19 ++++++- clang/include/clang/AST/TextNodeDumper.h | 2 + clang/lib/AST/ASTContext.cpp | 4 ++ clang/lib/AST/ASTImporter.cpp | 6 +++ clang/lib/AST/ASTStructuralEquivalence.cpp | 1 + clang/lib/AST/ItaniumMangle.cpp | 7 +++ clang/lib/AST/ODRHash.cpp | 1 + clang/lib/AST/TemplateName.cpp | 46 +++++++++++++++-- clang/lib/AST/TextNodeDumper.cpp | 13 +++++ clang/lib/AST/Type.cpp | 3 +- clang/lib/Sema/SemaDecl.cpp | 22 +++++--- clang/lib/Sema/SemaDeclCXX.cpp | 2 + clang/lib/Sema/SemaTemplate.cpp | 14 +++-- clang/test/AST/ast-dump-using-template.cpp | 35 +++++++++++++ clang/test/CXX/temp/temp.deduct.guide/p3.cpp | 3 ++ clang/tools/libclang/CIndex.cpp | 1 + clang/unittests/AST/ASTImporterTest.cpp | 12 +++++ clang/unittests/AST/CMakeLists.txt | 1 + clang/unittests/AST/TemplateNameTest.cpp | 62 +++++++++++++++++++++++ 22 files changed, 250 insertions(+), 16 deletions(-) create mode 100644 clang/test/AST/ast-dump-using-template.cpp create mode 100644 clang/unittests/AST/TemplateNameTest.cpp diff --git a/clang-tools-extra/clangd/DumpAST.cpp b/clang-tools-extra/clangd/DumpAST.cpp index 4c36de8..8f640ad 100644 --- a/clang-tools-extra/clangd/DumpAST.cpp +++ b/clang-tools-extra/clangd/DumpAST.cpp @@ -184,6 +184,7 @@ class DumpVisitor : public RecursiveASTVisitor { TEMPLATE_KIND(DependentTemplate); TEMPLATE_KIND(SubstTemplateTemplateParm); TEMPLATE_KIND(SubstTemplateTemplateParmPack); + TEMPLATE_KIND(UsingTemplate); #undef TEMPLATE_KIND } llvm_unreachable("Unhandled NameKind enum"); diff --git a/clang-tools-extra/clangd/SemanticHighlighting.cpp b/clang-tools-extra/clangd/SemanticHighlighting.cpp index fce83d1..489bb93 100644 --- a/clang-tools-extra/clangd/SemanticHighlighting.cpp +++ b/clang-tools-extra/clangd/SemanticHighlighting.cpp @@ -762,6 +762,7 @@ public: case TemplateName::QualifiedTemplate: case TemplateName::SubstTemplateTemplateParm: case TemplateName::SubstTemplateTemplateParmPack: + case TemplateName::UsingTemplate: // Names that could be resolved to a TemplateDecl are handled elsewhere. break; } diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td index 3da7fd9..0ab18b6 100644 --- a/clang/include/clang/AST/PropertiesBase.td +++ b/clang/include/clang/AST/PropertiesBase.td @@ -620,6 +620,16 @@ let Class = PropertyTypeCase in { return TemplateName(declaration); }]>; } + +let Class = PropertyTypeCase in { + def : Property<"foundDecl", UsingShadowDeclRef> { + let Read = [{ node.getAsUsingShadowDecl() }]; + } + def : Creator<[{ + return TemplateName(foundDecl); + }]>; +} + let Class = PropertyTypeCase in { def : Property<"overloads", Array> { let Read = [{ node.getAsOverloadedTemplate()->decls() }]; diff --git a/clang/include/clang/AST/TemplateName.h b/clang/include/clang/AST/TemplateName.h index 26c64d0..1bd86b0 100644 --- a/clang/include/clang/AST/TemplateName.h +++ b/clang/include/clang/AST/TemplateName.h @@ -25,6 +25,7 @@ namespace clang { class ASTContext; +class Decl; class DependentTemplateName; class IdentifierInfo; class NamedDecl; @@ -39,6 +40,7 @@ class SubstTemplateTemplateParmStorage; class TemplateArgument; class TemplateDecl; class TemplateTemplateParmDecl; +class UsingShadowDecl; /// Implementation class used to describe either a set of overloaded /// template names or an already-substituted template template parameter pack. @@ -188,8 +190,12 @@ public: /// specifier in the typedef. "apply" is a nested template, and can /// only be understood in the context of class TemplateName { + // NameDecl is either a TemplateDecl or a UsingShadowDecl depending on the + // NameKind. + // !! There is no free low bits in 32-bit builds to discriminate more than 4 + // pointer types in PointerUnion. using StorageType = - llvm::PointerUnion; StorageType Storage; @@ -224,7 +230,11 @@ public: /// A template template parameter pack that has been substituted for /// a template template argument pack, but has not yet been expanded into /// individual arguments. - SubstTemplateTemplateParmPack + SubstTemplateTemplateParmPack, + + /// A template name that refers to a template declaration found through a + /// specific using shadow declaration. + UsingTemplate, }; TemplateName() = default; @@ -235,6 +245,7 @@ public: explicit TemplateName(SubstTemplateTemplateParmPackStorage *Storage); explicit TemplateName(QualifiedTemplateName *Qual); explicit TemplateName(DependentTemplateName *Dep); + explicit TemplateName(UsingShadowDecl *Using); /// Determine whether this template name is NULL. bool isNull() const; @@ -287,6 +298,10 @@ public: /// structure, if any. DependentTemplateName *getAsDependentTemplateName() const; + /// Retrieve the using shadow declaration through which the underlying + /// template declaration is introduced, if any. + UsingShadowDecl *getAsUsingShadowDecl() const; + TemplateName getUnderlying() const; /// Get the template name to substitute when this template name is used as a diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h index 41bbf2e..0ecb8a2 100644 --- a/clang/include/clang/AST/TextNodeDumper.h +++ b/clang/include/clang/AST/TextNodeDumper.h @@ -317,6 +317,8 @@ public: void VisitTagType(const TagType *T); void VisitTemplateTypeParmType(const TemplateTypeParmType *T); void VisitAutoType(const AutoType *T); + void VisitDeducedTemplateSpecializationType( + const DeducedTemplateSpecializationType *T); void VisitTemplateSpecializationType(const TemplateSpecializationType *T); void VisitInjectedClassNameType(const InjectedClassNameType *T); void VisitObjCInterfaceType(const ObjCInterfaceType *T); diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 036f970..f6bb757 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -6125,6 +6125,9 @@ ASTContext::getNameForTemplate(TemplateName Name, return DeclarationNameInfo(subst->getParameterPack()->getDeclName(), NameLoc); } + case TemplateName::UsingTemplate: + return DeclarationNameInfo(Name.getAsUsingShadowDecl()->getDeclName(), + NameLoc); } llvm_unreachable("bad template name kind!"); @@ -6133,6 +6136,7 @@ ASTContext::getNameForTemplate(TemplateName Name, TemplateName ASTContext::getCanonicalTemplateName(const TemplateName &Name) const { switch (Name.getKind()) { + case TemplateName::UsingTemplate: case TemplateName::QualifiedTemplate: case TemplateName::Template: { TemplateDecl *Template = Name.getAsTemplateDecl(); diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index d0041b8..319f39b 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -9240,6 +9240,12 @@ Expected ASTImporter::Import(TemplateName From) { return ToContext.getSubstTemplateTemplateParmPack( cast(*ParamOrErr), *ArgPackOrErr); } + case TemplateName::UsingTemplate: { + auto UsingOrError = Import(From.getAsUsingShadowDecl()); + if (!UsingOrError) + return UsingOrError.takeError(); + return TemplateName(cast(*UsingOrError)); + } } llvm_unreachable("Invalid template name kind"); diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp index d4d20dd..05f3470 100644 --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -517,6 +517,7 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, case TemplateName::Template: case TemplateName::QualifiedTemplate: case TemplateName::SubstTemplateTemplateParm: + case TemplateName::UsingTemplate: // It is sufficient to check value of getAsTemplateDecl. break; diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 50e110e..adab764 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -2207,6 +2207,7 @@ void CXXNameMangler::mangleType(TemplateName TN) { TD = TN.getAsQualifiedTemplateName()->getTemplateDecl(); goto HaveDecl; + case TemplateName::UsingTemplate: case TemplateName::Template: TD = TN.getAsTemplateDecl(); goto HaveDecl; @@ -2383,6 +2384,12 @@ bool CXXNameMangler::mangleUnresolvedTypeOrSimpleId(QualType Ty, Out << "_SUBSTPACK_"; break; } + case TemplateName::UsingTemplate: { + TemplateDecl *TD = TN.getAsTemplateDecl(); + assert(TD && !isa(TD)); + mangleSourceNameWithAbiTags(TD); + break; + } } // Note: we don't pass in the template name here. We are mangling the diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp index 735bcff..04cbb09 100644 --- a/clang/lib/AST/ODRHash.cpp +++ b/clang/lib/AST/ODRHash.cpp @@ -150,6 +150,7 @@ void ODRHash::AddTemplateName(TemplateName Name) { case TemplateName::DependentTemplate: case TemplateName::SubstTemplateTemplateParm: case TemplateName::SubstTemplateTemplateParmPack: + case TemplateName::UsingTemplate: break; } } diff --git a/clang/lib/AST/TemplateName.cpp b/clang/lib/AST/TemplateName.cpp index a6d8a7f..8e32c9c 100644 --- a/clang/lib/AST/TemplateName.cpp +++ b/clang/lib/AST/TemplateName.cpp @@ -13,6 +13,7 @@ #include "clang/AST/TemplateName.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/DependenceFlags.h" #include "clang/AST/NestedNameSpecifier.h" @@ -76,12 +77,18 @@ TemplateName::TemplateName(SubstTemplateTemplateParmPackStorage *Storage) : Storage(Storage) {} TemplateName::TemplateName(QualifiedTemplateName *Qual) : Storage(Qual) {} TemplateName::TemplateName(DependentTemplateName *Dep) : Storage(Dep) {} +TemplateName::TemplateName(UsingShadowDecl *Using) : Storage(Using) {} bool TemplateName::isNull() const { return Storage.isNull(); } TemplateName::NameKind TemplateName::getKind() const { - if (Storage.is()) + if (auto *ND = Storage.dyn_cast()) { + if (isa(ND)) + return UsingTemplate; + assert(isa(ND)); return Template; + } + if (Storage.is()) return DependentTemplate; if (Storage.is()) @@ -99,8 +106,13 @@ TemplateName::NameKind TemplateName::getKind() const { } TemplateDecl *TemplateName::getAsTemplateDecl() const { - if (TemplateDecl *Template = Storage.dyn_cast()) - return Template; + if (Decl *TemplateOrUsing = Storage.dyn_cast()) { + if (UsingShadowDecl *USD = dyn_cast(TemplateOrUsing)) + return cast(USD->getTargetDecl()); + + assert(isa(TemplateOrUsing)); + return cast(TemplateOrUsing); + } if (QualifiedTemplateName *QTN = getAsQualifiedTemplateName()) return QTN->getTemplateDecl(); @@ -108,6 +120,9 @@ TemplateDecl *TemplateName::getAsTemplateDecl() const { if (SubstTemplateTemplateParmStorage *sub = getAsSubstTemplateTemplateParm()) return sub->getReplacement().getAsTemplateDecl(); + if (UsingShadowDecl *USD = getAsUsingShadowDecl()) + return cast(USD->getTargetDecl()); + return nullptr; } @@ -153,6 +168,13 @@ DependentTemplateName *TemplateName::getAsDependentTemplateName() const { return Storage.dyn_cast(); } +UsingShadowDecl *TemplateName::getAsUsingShadowDecl() const { + if (Decl *D = Storage.dyn_cast()) + if (UsingShadowDecl *USD = dyn_cast(D)) + return USD; + return nullptr; +} + TemplateName TemplateName::getNameToSubstitute() const { TemplateDecl *Decl = getAsTemplateDecl(); @@ -222,7 +244,22 @@ bool TemplateName::containsUnexpandedParameterPack() const { void TemplateName::print(raw_ostream &OS, const PrintingPolicy &Policy, Qualified Qual) const { - if (TemplateDecl *Template = Storage.dyn_cast()) + auto Kind = getKind(); + TemplateDecl *Template = nullptr; + if (Kind == TemplateName::Template || Kind == TemplateName::UsingTemplate) { + // After `namespace ns { using std::vector }`, what is the fully-qualified + // name of the UsingTemplateName `vector` within ns? + // + // - ns::vector (the qualified name of the using-shadow decl) + // - std::vector (the qualified name of the underlying template decl) + // + // Similar to the UsingType behavior, using declarations are used to import + // names more often than to export them, thus using the original name is + // most useful in this case. + Template = getAsTemplateDecl(); + } + + if (Template) if (Policy.CleanUglifiedParameters && isa(Template) && Template->getIdentifier()) OS << Template->getIdentifier()->deuglifiedName(); @@ -262,6 +299,7 @@ void TemplateName::print(raw_ostream &OS, const PrintingPolicy &Policy, else if (AssumedTemplateStorage *Assumed = getAsAssumedTemplateName()) { Assumed->getDeclName().print(OS, Policy); } else { + assert(getKind() == TemplateName::OverloadedTemplate); OverloadedTemplateStorage *OTS = getAsOverloadedTemplate(); (*OTS->begin())->printName(OS); } diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index ba8b7b6..9131dfb 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -900,12 +900,17 @@ void TextNodeDumper::VisitIntegralTemplateArgument(const TemplateArgument &TA) { } void TextNodeDumper::VisitTemplateTemplateArgument(const TemplateArgument &TA) { + if (TA.getAsTemplate().getKind() == TemplateName::UsingTemplate) + OS << " using"; OS << " template "; TA.getAsTemplate().dump(OS); } void TextNodeDumper::VisitTemplateExpansionTemplateArgument( const TemplateArgument &TA) { + if (TA.getAsTemplateOrTemplatePattern().getKind() == + TemplateName::UsingTemplate) + OS << " using"; OS << " template expansion "; TA.getAsTemplateOrTemplatePattern().dump(OS); } @@ -1575,10 +1580,18 @@ void TextNodeDumper::VisitAutoType(const AutoType *T) { } } +void TextNodeDumper::VisitDeducedTemplateSpecializationType( + const DeducedTemplateSpecializationType *T) { + if (T->getTemplateName().getKind() == TemplateName::UsingTemplate) + OS << " using"; +} + void TextNodeDumper::VisitTemplateSpecializationType( const TemplateSpecializationType *T) { if (T->isTypeAlias()) OS << " alias"; + if (T->getTemplateName().getKind() == TemplateName::UsingTemplate) + OS << " using"; OS << " "; T->getTemplateName().dump(OS); } diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 936550d..bb9900e 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -3686,7 +3686,8 @@ TemplateSpecializationType::TemplateSpecializationType( "Use DependentTemplateSpecializationType for dependent template-name"); assert((T.getKind() == TemplateName::Template || T.getKind() == TemplateName::SubstTemplateTemplateParm || - T.getKind() == TemplateName::SubstTemplateTemplateParmPack) && + T.getKind() == TemplateName::SubstTemplateTemplateParmPack || + T.getKind() == TemplateName::UsingTemplate) && "Unexpected template name for TemplateSpecializationType"); auto *TemplateArgs = reinterpret_cast(this + 1); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 706b3da..0708e68 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -504,9 +504,11 @@ ParsedType Sema::getTypeName(const IdentifierInfo &II, SourceLocation NameLoc, FoundUsingShadow = nullptr; } else if (AllowDeducedTemplate) { if (auto *TD = getAsTypeTemplateDecl(IIDecl)) { - // FIXME: TemplateName should include FoundUsingShadow sugar. - T = Context.getDeducedTemplateSpecializationType(TemplateName(TD), - QualType(), false); + assert(!FoundUsingShadow || FoundUsingShadow->getTargetDecl() == TD); + TemplateName Template = + FoundUsingShadow ? TemplateName(FoundUsingShadow) : TemplateName(TD); + T = Context.getDeducedTemplateSpecializationType(Template, QualType(), + false); // Don't wrap in a further UsingType. FoundUsingShadow = nullptr; } @@ -1107,12 +1109,20 @@ Corrected: IsFunctionTemplate = isa(TD); IsVarTemplate = isa(TD); - if (SS.isNotEmpty()) + UsingShadowDecl *FoundUsingShadow = + dyn_cast(*Result.begin()); + + if (SS.isNotEmpty()) { + // FIXME: support using shadow-declaration in qualified template name. Template = Context.getQualifiedTemplateName(SS.getScopeRep(), /*TemplateKeyword=*/false, TD); - else - Template = TemplateName(TD); + } else { + assert(!FoundUsingShadow || + TD == cast(FoundUsingShadow->getTargetDecl())); + Template = FoundUsingShadow ? TemplateName(FoundUsingShadow) + : TemplateName(TD); + } } else { // All results were non-template functions. This is a function template // name. diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 0ce9ddd3..4467f23 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -11023,6 +11023,8 @@ void Sema::CheckDeductionGuideDeclarator(Declarator &D, QualType &R, TemplateName SpecifiedName = RetTST.getTypePtr()->getTemplateName(); bool TemplateMatches = Context.hasSameTemplateName(SpecifiedName, GuidedTemplate); + // FIXME: We should consider other template kinds (using, qualified), + // otherwise we will emit bogus diagnostics. if (SpecifiedName.getKind() == TemplateName::Template && TemplateMatches) AcceptableReturnType = true; else { diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index bf65f11..e7195ed 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -11,11 +11,13 @@ #include "TreeTransform.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" #include "clang/AST/DeclFriend.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/TemplateName.h" #include "clang/AST/TypeVisitor.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/LangOptions.h" @@ -223,6 +225,7 @@ TemplateNameKind Sema::isTemplateName(Scope *S, return TNK_Non_template; NamedDecl *D = nullptr; + UsingShadowDecl *FoundUsingShadow = dyn_cast(*R.begin()); if (R.isAmbiguous()) { // If we got an ambiguity involving a non-function template, treat this // as a template name, and pick an arbitrary template for error recovery. @@ -233,6 +236,7 @@ TemplateNameKind Sema::isTemplateName(Scope *S, AnyFunctionTemplates = true; else { D = FoundTemplate; + FoundUsingShadow = dyn_cast(FoundD); break; } } @@ -283,10 +287,14 @@ TemplateNameKind Sema::isTemplateName(Scope *S, if (SS.isSet() && !SS.isInvalid()) { NestedNameSpecifier *Qualifier = SS.getScopeRep(); - Template = Context.getQualifiedTemplateName(Qualifier, - hasTemplateKeyword, TD); + // FIXME: store the using TemplateName in QualifiedTemplateName if + // the TD is referred via a using-declaration. + Template = + Context.getQualifiedTemplateName(Qualifier, hasTemplateKeyword, TD); } else { - Template = TemplateName(TD); + Template = + FoundUsingShadow ? TemplateName(FoundUsingShadow) : TemplateName(TD); + assert(!FoundUsingShadow || FoundUsingShadow->getTargetDecl() == TD); } if (isa(TD)) { diff --git a/clang/test/AST/ast-dump-using-template.cpp b/clang/test/AST/ast-dump-using-template.cpp new file mode 100644 index 0000000..fbce09d --- /dev/null +++ b/clang/test/AST/ast-dump-using-template.cpp @@ -0,0 +1,35 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c++17 -ast-dump %s | FileCheck -strict-whitespace %s + +// Tests to verify we construct correct using template names. +// TemplateNames are not dumped, so the sugar here isn't obvious. However +// the "using" on the TemplateSpecializationTypes shows that the +// UsingTemplateName is present. +namespace ns { +template class S { + public: + S(T); +}; +} +using ns::S; + +// TemplateName in TemplateSpecializationType. +template +using A = S; +// CHECK: TypeAliasDecl +// CHECK-NEXT: `-TemplateSpecializationType {{.*}} 'S' dependent using S + +// TemplateName in TemplateArgument. +template