diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index f0147c42e27b..614351f76b38 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -4350,6 +4350,9 @@ public: const TemplateSpecializationType *getInjectedTST() const { return cast(InjectedType.getTypePtr()); } + TemplateName getTemplateName() const { + return getInjectedTST()->getTemplateName(); + } CXXRecordDecl *getDecl() const; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 144ef0ba9d7b..e20619bb37d3 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -6114,12 +6114,17 @@ public: /// \param Converted Will receive the converted, canonicalized template /// arguments. /// + /// \param UpdateArgsWithConversions If \c true, update \p TemplateArgs to + /// contain the converted forms of the template arguments as written. + /// Otherwise, \p TemplateArgs will not be modified. + /// /// \returns true if an error occurred, false otherwise. bool CheckTemplateArgumentList(TemplateDecl *Template, SourceLocation TemplateLoc, TemplateArgumentListInfo &TemplateArgs, bool PartialTemplateArgs, - SmallVectorImpl &Converted); + SmallVectorImpl &Converted, + bool UpdateArgsWithConversions = true); bool CheckTemplateTypeArgument(TemplateTypeParmDecl *Param, TemplateArgumentLoc &Arg, diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 2920ede3b5ee..90de88ac3327 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -3657,6 +3657,39 @@ Sema::SubstDefaultTemplateArgumentIfAvailable(TemplateDecl *Template, TempTempParm->getDefaultArgument().getTemplateNameLoc()); } +/// Convert a template-argument that we parsed as a type into a template, if +/// possible. C++ permits injected-class-names to perform dual service as +/// template template arguments and as template type arguments. +static TemplateArgumentLoc convertTypeTemplateArgumentToTemplate(TypeLoc TLoc) { + // Extract and step over any surrounding nested-name-specifier. + NestedNameSpecifierLoc QualLoc; + if (auto ETLoc = TLoc.getAs()) { + if (ETLoc.getTypePtr()->getKeyword() != ETK_None) + return TemplateArgumentLoc(); + + QualLoc = ETLoc.getQualifierLoc(); + TLoc = ETLoc.getNamedTypeLoc(); + } + + // If this type was written as an injected-class-name, it can be used as a + // template template argument. + if (auto InjLoc = TLoc.getAs()) + return TemplateArgumentLoc(InjLoc.getTypePtr()->getTemplateName(), + QualLoc, InjLoc.getNameLoc()); + + // If this type was written as an injected-class-name, it may have been + // converted to a RecordType during instantiation. If the RecordType is + // *not* wrapped in a TemplateSpecializationType and denotes a class + // template specialization, it must have come from an injected-class-name. + if (auto RecLoc = TLoc.getAs()) + if (auto *CTSD = + dyn_cast(RecLoc.getDecl())) + return TemplateArgumentLoc(TemplateName(CTSD->getSpecializedTemplate()), + QualLoc, RecLoc.getNameLoc()); + + return TemplateArgumentLoc(); +} + /// \brief Check that the given template argument corresponds to the given /// template parameter. /// @@ -3863,6 +3896,17 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param, return true; } + // C++1z [temp.local]p1: (DR1004) + // When [the injected-class-name] is used [...] as a template-argument for + // a template template-parameter [...] it refers to the class template + // itself. + if (Arg.getArgument().getKind() == TemplateArgument::Type) { + TemplateArgumentLoc ConvertedArg = convertTypeTemplateArgumentToTemplate( + Arg.getTypeSourceInfo()->getTypeLoc()); + if (!ConvertedArg.getArgument().isNull()) + Arg = ConvertedArg; + } + switch (Arg.getArgument().getKind()) { case TemplateArgument::Null: llvm_unreachable("Should never see a NULL template argument here"); @@ -3976,11 +4020,11 @@ static bool diagnoseMissingArgument(Sema &S, SourceLocation Loc, /// \brief Check that the given template argument list is well-formed /// for specializing the given template. -bool Sema::CheckTemplateArgumentList(TemplateDecl *Template, - SourceLocation TemplateLoc, - TemplateArgumentListInfo &TemplateArgs, - bool PartialTemplateArgs, - SmallVectorImpl &Converted) { +bool Sema::CheckTemplateArgumentList( + TemplateDecl *Template, SourceLocation TemplateLoc, + TemplateArgumentListInfo &TemplateArgs, bool PartialTemplateArgs, + SmallVectorImpl &Converted, + bool UpdateArgsWithConversions) { // Make a copy of the template arguments for processing. Only make the // changes at the end when successful in matching the arguments to the // template. @@ -4218,7 +4262,8 @@ bool Sema::CheckTemplateArgumentList(TemplateDecl *Template, // No problems found with the new argument list, propagate changes back // to caller. - TemplateArgs = std::move(NewArgs); + if (UpdateArgsWithConversions) + TemplateArgs = std::move(NewArgs); return false; } diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 93e796ee9668..3197647d8d8b 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -2640,11 +2640,9 @@ Sema::SubstituteExplicitTemplateArguments( if (Inst.isInvalid()) return TDK_InstantiationDepth; - if (CheckTemplateArgumentList(FunctionTemplate, - SourceLocation(), - ExplicitTemplateArgs, - true, - Builder) || Trap.hasErrorOccurred()) { + if (CheckTemplateArgumentList(FunctionTemplate, SourceLocation(), + ExplicitTemplateArgs, true, Builder, false) || + Trap.hasErrorOccurred()) { unsigned Index = Builder.size(); if (Index >= TemplateParams->size()) Index = TemplateParams->size() - 1; diff --git a/clang/test/CXX/drs/dr10xx.cpp b/clang/test/CXX/drs/dr10xx.cpp index e11e796165e2..0ae55f5e07c0 100644 --- a/clang/test/CXX/drs/dr10xx.cpp +++ b/clang/test/CXX/drs/dr10xx.cpp @@ -12,6 +12,29 @@ namespace std { }; } +namespace dr1004 { // dr1004: 5 + template struct A {}; + template struct B1 {}; + template class> struct B2 {}; + template void f(); // expected-note {{[with X = dr1004::A]}} + template class X> void f(); // expected-note {{[with X = A]}} + template class X> void g(); // expected-note {{[with X = A]}} + template void g(); // expected-note {{[with X = dr1004::A]}} + struct C : A { + B1 b1a; + B2 b2a; + void h() { + f(); // expected-error {{ambiguous}} + g(); // expected-error {{ambiguous}} + } + }; + + // FIXME: Is this example (from the standard) really OK, or does name lookup + // of "T::template A" name the constructor? + template class U = T::template A> struct Third { }; + Third > t; +} + namespace dr1048 { // dr1048: 3.6 struct A {}; const A f(); diff --git a/clang/test/CXX/temp/temp.res/temp.local/p1.cpp b/clang/test/CXX/temp/temp.res/temp.local/p1.cpp index f6ef636daa56..faa85cb5fce3 100644 --- a/clang/test/CXX/temp/temp.res/temp.local/p1.cpp +++ b/clang/test/CXX/temp/temp.res/temp.local/p1.cpp @@ -1,12 +1,55 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s -// expected-no-diagnostics +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1z %s -// C++0x [temp.local]p1: +// C++1z [temp.local]p1: // Like normal (non-template) classes, class templates have an -// injected-class-name (Clause 9). The injected-class-name can be used with -// or without a template-argument-list. When it is used without -// a template-argument-list, it is equivalent to the injected-class-name -// followed by the template-parameters of the class template enclosed in <>. +// injected-class-name (Clause 9). The injected-class-name can +// be used as a template-name or a type-name. + +template char id; + +template struct TempType {}; +template class> struct TempTemp {}; + +template void use(int&); // expected-note {{invalid explicitly-specified argument}} expected-note {{no known conversion}} +template class> void use(float&); // expected-note 2{{no known conversion}} +template void use(char&); // expected-note 2{{invalid explicitly-specified argument}} + +template struct A { + template struct C {}; + struct B : C { + // When it is used with a template-argument-list, + A *aint; + typename B::template C *cint; + + // as a template-argument for a template template-parameter, + TempTemp a_as_temp; + TempTemp c_as_temp; + + // or as the final identifier in the elaborated-type-specifier of a friend + // class template declaration, + template friend struct A; + // it refers to the class template itself. + + // Otherwise, it is equivalent to the template-name followed by the + // template-parameters of the class template enclosed in <>. + A *aT; + typename B::C *cT; + TempType a_as_type; + TempType c_as_type; + friend struct A; + friend struct B::C; + + void f(T &t) { + use(t); // expected-error {{no matching function}} + if constexpr (&id != &id) + use(t); // expected-error {{no matching function}} + } + }; +}; + +template struct A; +template struct A; +template struct A; // expected-note {{instantiation of}} template struct X0 { X0(); diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html index ffc076512446..b4a4d7710f5e 100644 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -1937,7 +1937,7 @@ of class templates 316 NAD Injected-class-name of template used as template template parameter - Superseded by 1004 + Superseded by 1004 317 @@ -5839,7 +5839,7 @@ and POD class 1004 C++11 Injected-class-names as arguments for template template parameters - Unknown + SVN 1005