hokein updated this revision to Diff 421856. hokein added a comment. add a unittest for template name.
Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D123127/new/ https://reviews.llvm.org/D123127 Files: 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/AST/ast-dump-using-template.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 clang/unittests/AST/TemplateNameTest.cpp
Index: clang/unittests/AST/TemplateNameTest.cpp =================================================================== --- /dev/null +++ clang/unittests/AST/TemplateNameTest.cpp @@ -0,0 +1,63 @@ +//===- 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 X in X<vector>. + auto Matcher = templateArgumentLoc().bind("id"); + + const auto *Template = + selectFirst<TemplateArgumentLoc>("id", match(Matcher, Ctx)); + assert(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 Index: clang/unittests/AST/CMakeLists.txt =================================================================== --- clang/unittests/AST/CMakeLists.txt +++ clang/unittests/AST/CMakeLists.txt @@ -30,6 +30,7 @@ SourceLocationTest.cpp StmtPrinterTest.cpp StructuralEquivalenceTest.cpp + TemplateNameTest.cpp TypePrinterTest.cpp ) Index: clang/unittests/AST/ASTImporterTest.cpp =================================================================== --- clang/unittests/AST/ASTImporterTest.cpp +++ clang/unittests/AST/ASTImporterTest.cpp @@ -890,6 +890,18 @@ 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 }; }" Index: clang/tools/libclang/CIndex.cpp =================================================================== --- clang/tools/libclang/CIndex.cpp +++ clang/tools/libclang/CIndex.cpp @@ -1442,6 +1442,7 @@ 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: Index: clang/test/CXX/temp/temp.deduct.guide/p3.cpp =================================================================== --- clang/test/CXX/temp/temp.deduct.guide/p3.cpp +++ clang/test/CXX/temp/temp.deduct.guide/p3.cpp @@ -55,6 +55,9 @@ } 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@-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 { Index: clang/test/AST/ast-dump-using-template.cpp =================================================================== --- /dev/null +++ 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 Index: clang/lib/Sema/SemaTemplate.cpp =================================================================== --- clang/lib/Sema/SemaTemplate.cpp +++ 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 @@ 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 @@ AnyFunctionTemplates = true; else { D = FoundTemplate; + FoundUsingShadow = dyn_cast<UsingShadowDecl>(FoundD); break; } } @@ -283,10 +287,14 @@ 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)) { Index: clang/lib/Sema/SemaDeclCXX.cpp =================================================================== --- clang/lib/Sema/SemaDeclCXX.cpp +++ clang/lib/Sema/SemaDeclCXX.cpp @@ -11023,6 +11023,8 @@ 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 { Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -503,9 +503,11 @@ 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; } @@ -1106,12 +1108,19 @@ IsFunctionTemplate = isa<FunctionTemplateDecl>(TD); IsVarTemplate = isa<VarTemplateDecl>(TD); + UsingShadowDecl *FoundUsingShadow = + dyn_cast<UsingShadowDecl>(*Result.begin()); + if (SS.isNotEmpty()) 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. Index: clang/lib/AST/Type.cpp =================================================================== --- clang/lib/AST/Type.cpp +++ clang/lib/AST/Type.cpp @@ -3686,7 +3686,8 @@ "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); Index: clang/lib/AST/TextNodeDumper.cpp =================================================================== --- clang/lib/AST/TextNodeDumper.cpp +++ clang/lib/AST/TextNodeDumper.cpp @@ -900,12 +900,17 @@ } 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::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); } Index: clang/lib/AST/TemplateName.cpp =================================================================== --- clang/lib/AST/TemplateName.cpp +++ 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,6 +77,7 @@ : 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(); } @@ -86,6 +88,8 @@ return DependentTemplate; if (Storage.is<QualifiedTemplateName *>()) return QualifiedTemplate; + if (Storage.is<UsingShadowDecl *>()) + return UsingTemplate; UncommonTemplateNameStorage *uncommon = Storage.get<UncommonTemplateNameStorage*>(); @@ -108,6 +112,9 @@ if (SubstTemplateTemplateParmStorage *sub = getAsSubstTemplateTemplateParm()) return sub->getReplacement().getAsTemplateDecl(); + if (auto *USD = Storage.dyn_cast<UsingShadowDecl *>()) + return cast<TemplateDecl>(USD->getTargetDecl()); + return nullptr; } @@ -153,6 +160,10 @@ return Storage.dyn_cast<DependentTemplateName *>(); } +UsingShadowDecl *TemplateName::getAsUsingShadowDecl() const { + return Storage.dyn_cast<UsingShadowDecl *>(); +} + TemplateName TemplateName::getNameToSubstitute() const { TemplateDecl *Decl = getAsTemplateDecl(); @@ -222,7 +233,10 @@ void TemplateName::print(raw_ostream &OS, const PrintingPolicy &Policy, Qualified Qual) const { - if (TemplateDecl *Template = Storage.dyn_cast<TemplateDecl *>()) + TemplateDecl *Template = Storage.dyn_cast<TemplateDecl *>(); + if (getKind() == TemplateName::UsingTemplate) + Template = getAsTemplateDecl(); + if (Template) if (Policy.CleanUglifiedParameters && isa<TemplateTemplateParmDecl>(Template) && Template->getIdentifier()) OS << Template->getIdentifier()->deuglifiedName(); @@ -262,6 +276,7 @@ else if (AssumedTemplateStorage *Assumed = getAsAssumedTemplateName()) { Assumed->getDeclName().print(OS, Policy); } else { + assert(getKind() == TemplateName::OverloadedTemplate); OverloadedTemplateStorage *OTS = getAsOverloadedTemplate(); (*OTS->begin())->printName(OS); } Index: clang/lib/AST/ODRHash.cpp =================================================================== --- clang/lib/AST/ODRHash.cpp +++ clang/lib/AST/ODRHash.cpp @@ -150,6 +150,7 @@ case TemplateName::DependentTemplate: case TemplateName::SubstTemplateTemplateParm: case TemplateName::SubstTemplateTemplateParmPack: + case TemplateName::UsingTemplate: break; } } Index: clang/lib/AST/ItaniumMangle.cpp =================================================================== --- clang/lib/AST/ItaniumMangle.cpp +++ clang/lib/AST/ItaniumMangle.cpp @@ -2207,6 +2207,7 @@ TD = TN.getAsQualifiedTemplateName()->getTemplateDecl(); goto HaveDecl; + case TemplateName::UsingTemplate: case TemplateName::Template: TD = TN.getAsTemplateDecl(); goto HaveDecl; @@ -2383,6 +2384,12 @@ 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 Index: clang/lib/AST/ASTStructuralEquivalence.cpp =================================================================== --- clang/lib/AST/ASTStructuralEquivalence.cpp +++ clang/lib/AST/ASTStructuralEquivalence.cpp @@ -517,6 +517,7 @@ case TemplateName::Template: case TemplateName::QualifiedTemplate: case TemplateName::SubstTemplateTemplateParm: + case TemplateName::UsingTemplate: // It is sufficient to check value of getAsTemplateDecl. break; Index: clang/lib/AST/ASTImporter.cpp =================================================================== --- clang/lib/AST/ASTImporter.cpp +++ clang/lib/AST/ASTImporter.cpp @@ -9186,6 +9186,12 @@ 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"); Index: clang/lib/AST/ASTContext.cpp =================================================================== --- clang/lib/AST/ASTContext.cpp +++ clang/lib/AST/ASTContext.cpp @@ -6125,6 +6125,9 @@ 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 @@ TemplateName ASTContext::getCanonicalTemplateName(const TemplateName &Name) const { switch (Name.getKind()) { + case TemplateName::UsingTemplate: case TemplateName::QualifiedTemplate: case TemplateName::Template: { TemplateDecl *Template = Name.getAsTemplateDecl(); Index: clang/include/clang/AST/TextNodeDumper.h =================================================================== --- clang/include/clang/AST/TextNodeDumper.h +++ clang/include/clang/AST/TextNodeDumper.h @@ -317,6 +317,8 @@ 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); Index: clang/include/clang/AST/TemplateName.h =================================================================== --- clang/include/clang/AST/TemplateName.h +++ clang/include/clang/AST/TemplateName.h @@ -39,6 +39,7 @@ 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. @@ -190,7 +191,8 @@ class TemplateName { using StorageType = llvm::PointerUnion<TemplateDecl *, UncommonTemplateNameStorage *, - QualifiedTemplateName *, DependentTemplateName *>; + QualifiedTemplateName *, DependentTemplateName *, + UsingShadowDecl *>; StorageType Storage; @@ -224,7 +226,11 @@ /// 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 +241,7 @@ 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 +294,13 @@ /// structure, if any. DependentTemplateName *getAsDependentTemplateName() const; + /// Retrieve the using shadow declaration through which the underlying + /// template declaration is introduced. + /// + /// The underlying template declaration is not stored in the template name, it + /// can be retrieved via the using shadow declaration. + UsingShadowDecl *getAsUsingShadowDecl() const; + TemplateName getUnderlying() const; /// Get the template name to substitute when this template name is used as a Index: clang/include/clang/AST/PropertiesBase.td =================================================================== --- clang/include/clang/AST/PropertiesBase.td +++ clang/include/clang/AST/PropertiesBase.td @@ -620,6 +620,16 @@ 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() }]; Index: clang-tools-extra/clangd/SemanticHighlighting.cpp =================================================================== --- clang-tools-extra/clangd/SemanticHighlighting.cpp +++ clang-tools-extra/clangd/SemanticHighlighting.cpp @@ -762,6 +762,7 @@ case TemplateName::QualifiedTemplate: case TemplateName::SubstTemplateTemplateParm: case TemplateName::SubstTemplateTemplateParmPack: + case TemplateName::UsingTemplate: // Names that could be resolved to a TemplateDecl are handled elsewhere. break; } Index: clang-tools-extra/clangd/DumpAST.cpp =================================================================== --- clang-tools-extra/clangd/DumpAST.cpp +++ clang-tools-extra/clangd/DumpAST.cpp @@ -184,6 +184,7 @@ TEMPLATE_KIND(DependentTemplate); TEMPLATE_KIND(SubstTemplateTemplateParm); TEMPLATE_KIND(SubstTemplateTemplateParmPack); + TEMPLATE_KIND(UsingTemplate); #undef TEMPLATE_KIND } llvm_unreachable("Unhandled NameKind enum");
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits