[clang] 6ba1b90 - Reland "[AST] Add a new TemplateKind for template decls found via a using decl.""

Haojian Wu via cfe-commits cfe-commits at lists.llvm.org
Thu Apr 14 02:05:06 PDT 2022


Author: Haojian Wu
Date: 2022-04-14T11:04:55+02:00
New Revision: 6ba1b9075dc1fef6c32eafa71495bfec803321e4

URL: https://github.com/llvm/llvm-project/commit/6ba1b9075dc1fef6c32eafa71495bfec803321e4
DIFF: https://github.com/llvm/llvm-project/commit/6ba1b9075dc1fef6c32eafa71495bfec803321e4.diff

LOG: 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

Added: 
    clang/test/AST/ast-dump-using-template.cpp
    clang/unittests/AST/TemplateNameTest.cpp

Modified: 
    clang-tools-extra/clangd/DumpAST.cpp
    clang-tools-extra/clangd/SemanticHighlighting.cpp
    clang/include/clang/AST/PropertiesBase.td
    clang/include/clang/AST/TemplateName.h
    clang/include/clang/AST/TextNodeDumper.h
    clang/lib/AST/ASTContext.cpp
    clang/lib/AST/ASTImporter.cpp
    clang/lib/AST/ASTStructuralEquivalence.cpp
    clang/lib/AST/ItaniumMangle.cpp
    clang/lib/AST/ODRHash.cpp
    clang/lib/AST/TemplateName.cpp
    clang/lib/AST/TextNodeDumper.cpp
    clang/lib/AST/Type.cpp
    clang/lib/Sema/SemaDecl.cpp
    clang/lib/Sema/SemaDeclCXX.cpp
    clang/lib/Sema/SemaTemplate.cpp
    clang/test/CXX/temp/temp.deduct.guide/p3.cpp
    clang/tools/libclang/CIndex.cpp
    clang/unittests/AST/ASTImporterTest.cpp
    clang/unittests/AST/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clangd/DumpAST.cpp b/clang-tools-extra/clangd/DumpAST.cpp
index 4c36de88aea1f..8f640ad821f8c 100644
--- a/clang-tools-extra/clangd/DumpAST.cpp
+++ b/clang-tools-extra/clangd/DumpAST.cpp
@@ -184,6 +184,7 @@ class DumpVisitor : public RecursiveASTVisitor<DumpVisitor> {
       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 fce83d1834a50..489bb93856a04 100644
--- a/clang-tools-extra/clangd/SemanticHighlighting.cpp
+++ b/clang-tools-extra/clangd/SemanticHighlighting.cpp
@@ -762,6 +762,7 @@ class CollectExtraHighlightings
     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 3da7fd9030aac..0ab18b6d59c3d 100644
--- a/clang/include/clang/AST/PropertiesBase.td
+++ b/clang/include/clang/AST/PropertiesBase.td
@@ -620,6 +620,16 @@ let Class = PropertyTypeCase<TemplateName, "Template"> in {
     return TemplateName(declaration);
   }]>;
 }
+
+let Class = PropertyTypeCase<TemplateName, "UsingTemplate"> in {
+  def : Property<"foundDecl", UsingShadowDeclRef> {
+    let Read = [{ node.getAsUsingShadowDecl() }];
+  }
+  def : Creator<[{
+    return TemplateName(foundDecl);
+  }]>;
+}
+
 let Class = PropertyTypeCase<TemplateName, "OverloadedTemplate"> in {
   def : Property<"overloads", Array<NamedDeclRef>> {
     let Read = [{ node.getAsOverloadedTemplate()->decls() }];

diff  --git a/clang/include/clang/AST/TemplateName.h b/clang/include/clang/AST/TemplateName.h
index 26c64d0e277ff..1bd86b0bdcf11 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 @@ class SubstTemplateTemplateParmPackStorage
 /// 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<TemplateDecl *, UncommonTemplateNameStorage *,
+      llvm::PointerUnion<Decl *, UncommonTemplateNameStorage *,
                          QualifiedTemplateName *, DependentTemplateName *>;
 
   StorageType Storage;
@@ -224,7 +230,11 @@ class TemplateName {
     /// 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 @@ class TemplateName {
   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 @@ class TemplateName {
   /// 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 41bbf2ec593aa..0ecb8a23dfb36 100644
--- a/clang/include/clang/AST/TextNodeDumper.h
+++ b/clang/include/clang/AST/TextNodeDumper.h
@@ -317,6 +317,8 @@ class TextNodeDumper
   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 036f970897180..f6bb7574bf71f 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 d0041b83f7afb..319f39b43264c 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -9240,6 +9240,12 @@ Expected<TemplateName> ASTImporter::Import(TemplateName From) {
     return ToContext.getSubstTemplateTemplateParmPack(
         cast<TemplateTemplateParmDecl>(*ParamOrErr), *ArgPackOrErr);
   }
+  case TemplateName::UsingTemplate: {
+    auto UsingOrError = Import(From.getAsUsingShadowDecl());
+    if (!UsingOrError)
+      return UsingOrError.takeError();
+    return TemplateName(cast<UsingShadowDecl>(*UsingOrError));
+  }
   }
 
   llvm_unreachable("Invalid template name kind");

diff  --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp
index d4d20dde75e33..05f3470a179d2 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 50e110ec1f57e..adab764487c7b 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<TemplateTemplateParmDecl>(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 735bcff8f1137..04cbb09356d7b 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 a6d8a7fe7b3dd..8e32c9c6a6ad7 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<TemplateDecl *>())
+  if (auto *ND = Storage.dyn_cast<Decl *>()) {
+    if (isa<UsingShadowDecl>(ND))
+      return UsingTemplate;
+    assert(isa<TemplateDecl>(ND));
     return Template;
+  }
+
   if (Storage.is<DependentTemplateName *>())
     return DependentTemplate;
   if (Storage.is<QualifiedTemplateName *>())
@@ -99,8 +106,13 @@ TemplateName::NameKind TemplateName::getKind() const {
 }
 
 TemplateDecl *TemplateName::getAsTemplateDecl() const {
-  if (TemplateDecl *Template = Storage.dyn_cast<TemplateDecl *>())
-    return Template;
+  if (Decl *TemplateOrUsing = Storage.dyn_cast<Decl *>()) {
+    if (UsingShadowDecl *USD = dyn_cast<UsingShadowDecl>(TemplateOrUsing))
+      return cast<TemplateDecl>(USD->getTargetDecl());
+
+    assert(isa<TemplateDecl>(TemplateOrUsing));
+    return cast<TemplateDecl>(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<TemplateDecl>(USD->getTargetDecl());
+
   return nullptr;
 }
 
@@ -153,6 +168,13 @@ DependentTemplateName *TemplateName::getAsDependentTemplateName() const {
   return Storage.dyn_cast<DependentTemplateName *>();
 }
 
+UsingShadowDecl *TemplateName::getAsUsingShadowDecl() const {
+  if (Decl *D = Storage.dyn_cast<Decl *>())
+    if (UsingShadowDecl *USD = dyn_cast<UsingShadowDecl>(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<TemplateDecl *>())
+  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<TemplateTemplateParmDecl>(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 ba8b7b692d588..9131dfbdca778 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 936550ddc54ce..bb9900ea6eea4 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<TemplateArgument *>(this + 1);

diff  --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 706b3daf918dc..0708e687c5752 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 @@ Sema::NameClassification Sema::ClassifyName(Scope *S, CXXScopeSpec &SS,
       IsFunctionTemplate = isa<FunctionTemplateDecl>(TD);
       IsVarTemplate = isa<VarTemplateDecl>(TD);
 
-      if (SS.isNotEmpty())
+      UsingShadowDecl *FoundUsingShadow =
+          dyn_cast<UsingShadowDecl>(*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<TemplateDecl>(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 0ce9ddd36d68c..4467f2357189e 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 bf65f115116f1..e7195edc9a268 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<UsingShadowDecl>(*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<UsingShadowDecl>(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<FunctionTemplateDecl>(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 0000000000000..fbce09d116ed0
--- /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<typename T> class S {
+ public:
+   S(T);
+};
+}
+using ns::S;
+
+// TemplateName in TemplateSpecializationType.
+template<typename T>
+using A = S<T>;
+// CHECK:      TypeAliasDecl
+// CHECK-NEXT: `-TemplateSpecializationType {{.*}} 'S<T>' dependent using S
+
+// TemplateName in TemplateArgument.
+template <template <typename> class T> class X {};
+using B = X<S>;
+// CHECK:      TypeAliasDecl
+// CHECK-NEXT: `-TemplateSpecializationType {{.*}} 'X<ns::S>' sugar X
+// CHECK-NEXT:   |-TemplateArgument using template S
+// CHECK-NEXT:     `-RecordType {{.*}} 'X<ns::S>'
+// CHECK-NEXT:       `-ClassTemplateSpecialization {{.*}} 'X'
+
+// TemplateName in DeducedTemplateSpecializationType.
+S DeducedTemplateSpecializationT(123);
+using C = decltype(DeducedTemplateSpecializationT);
+// CHECK:      DecltypeType {{.*}}
+// CHECK-NEXT:  |-DeclRefExpr {{.*}}
+// CHECK-NEXT:  `-DeducedTemplateSpecializationType {{.*}} 'ns::S<int>' sugar using

diff  --git a/clang/test/CXX/temp/temp.deduct.guide/p3.cpp b/clang/test/CXX/temp/temp.deduct.guide/p3.cpp
index ec39c0c5ac995..4ba6e8ef03732 100644
--- a/clang/test/CXX/temp/temp.deduct.guide/p3.cpp
+++ b/clang/test/CXX/temp/temp.deduct.guide/p3.cpp
@@ -55,6 +55,9 @@ namespace WrongScope {
   }
   using N::NamedNS1;
   NamedNS1(int) -> NamedNS1<int>; // expected-error {{deduction guide must be declared in the same scope as template}}
+  // FIXME: remove the following bogus diagnostic
+  // expected-error at -2{{deduction guide is not written as a specialization of template 'NamedNS1'}}
+
   using namespace N;
   NamedNS2(int) -> NamedNS2<int>; // expected-error {{deduction guide must be declared in the same scope as template}}
   struct ClassMemberA {

diff  --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index 9e15ca714ceac..fc6177036b08a 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -1442,6 +1442,7 @@ bool CursorVisitor::VisitTemplateParameters(
 bool CursorVisitor::VisitTemplateName(TemplateName Name, SourceLocation Loc) {
   switch (Name.getKind()) {
   case TemplateName::Template:
+  case TemplateName::UsingTemplate:
     return Visit(MakeCursorTemplateRef(Name.getAsTemplateDecl(), Loc, TU));
 
   case TemplateName::OverloadedTemplate:

diff  --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp
index d9b6f4969abf8..856010cd4d036 100644
--- a/clang/unittests/AST/ASTImporterTest.cpp
+++ b/clang/unittests/AST/ASTImporterTest.cpp
@@ -890,6 +890,18 @@ TEST_P(ImportDecl, ImportUsingDecl) {
              functionDecl(hasDescendant(usingDecl(hasName("bar")))));
 }
 
+TEST_P(ImportDecl, ImportUsingTemplate) {
+  MatchVerifier<Decl> Verifier;
+  testImport("namespace ns { template <typename T> struct S {}; }"
+             "template <template <typename> class T> class X {};"
+             "void declToImport() {"
+             "using ns::S;  X<S> xi; }",
+             Lang_CXX11, "", Lang_CXX11, Verifier,
+             functionDecl(
+                 hasDescendant(varDecl(hasTypeLoc(templateSpecializationTypeLoc(
+                     hasAnyTemplateArgumentLoc(templateArgumentLoc())))))));
+}
+
 TEST_P(ImportDecl, ImportUsingEnumDecl) {
   MatchVerifier<Decl> Verifier;
   testImport("namespace foo { enum bar { baz, toto, quux }; }"

diff  --git a/clang/unittests/AST/CMakeLists.txt b/clang/unittests/AST/CMakeLists.txt
index 2dcef2d2fca0e..48a610c98138a 100644
--- a/clang/unittests/AST/CMakeLists.txt
+++ b/clang/unittests/AST/CMakeLists.txt
@@ -31,6 +31,7 @@ add_clang_unittest(ASTTests
   SourceLocationTest.cpp
   StmtPrinterTest.cpp
   StructuralEquivalenceTest.cpp
+  TemplateNameTest.cpp
   TypePrinterTest.cpp
   )
 

diff  --git a/clang/unittests/AST/TemplateNameTest.cpp b/clang/unittests/AST/TemplateNameTest.cpp
new file mode 100644
index 0000000000000..30986920ce28e
--- /dev/null
+++ b/clang/unittests/AST/TemplateNameTest.cpp
@@ -0,0 +1,62 @@
+//===- unittests/AST/TemplateNameTest.cpp --- Tests for TemplateName ------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASTPrint.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "llvm/Support/raw_ostream.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace {
+using namespace ast_matchers;
+
+std::string printTemplateName(TemplateName TN, const PrintingPolicy &Policy,
+                              TemplateName::Qualified Qual) {
+  std::string Result;
+  llvm::raw_string_ostream Out(Result);
+  TN.print(Out, Policy, Qual);
+  return Out.str();
+}
+
+TEST(TemplateName, PrintUsingTemplate) {
+  std::string Code = R"cpp(
+    namespace std {
+      template <typename> struct vector {};
+    }
+    namespace absl { using std::vector; }
+
+    template<template <typename> class T> class X;
+
+    using absl::vector;
+    using A = X<vector>;
+  )cpp";
+  auto AST = tooling::buildASTFromCode(Code);
+  ASTContext &Ctx = AST->getASTContext();
+  // Match the template argument vector in X<vector>.
+  auto MatchResults = match(templateArgumentLoc().bind("id"), Ctx);
+  const auto *Template = selectFirst<TemplateArgumentLoc>("id", MatchResults);
+  ASSERT_TRUE(Template);
+
+  TemplateName TN = Template->getArgument().getAsTemplate();
+  EXPECT_EQ(TN.getKind(), TemplateName::UsingTemplate);
+  EXPECT_EQ(TN.getAsUsingShadowDecl()->getTargetDecl(), TN.getAsTemplateDecl());
+
+  EXPECT_EQ(printTemplateName(TN, Ctx.getPrintingPolicy(),
+                              TemplateName::Qualified::Fully),
+            "std::vector");
+  EXPECT_EQ(printTemplateName(TN, Ctx.getPrintingPolicy(),
+                              TemplateName::Qualified::AsWritten),
+            "vector");
+  EXPECT_EQ(printTemplateName(TN, Ctx.getPrintingPolicy(),
+                              TemplateName::Qualified::None),
+            "vector");
+}
+
+} // namespace
+} // namespace clang


        


More information about the cfe-commits mailing list