diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index f0147c42e27bfc1866685d35e56c6c8cc1c88910..614351f76b38fbbeeb3f9e9b9d8c0f116034675c 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -4350,6 +4350,9 @@ public: const TemplateSpecializationType *getInjectedTST() const { return cast<TemplateSpecializationType>(InjectedType.getTypePtr()); } + TemplateName getTemplateName() const { + return getInjectedTST()->getTemplateName(); + } CXXRecordDecl *getDecl() const; diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 144ef0ba9d7b10ee21736c9f2db3d0edb4465c9c..e20619bb37d3d5fe419d83125729f042c507e144 100644 --- a/include/clang/Sema/Sema.h +++ b/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<TemplateArgument> &Converted); + SmallVectorImpl<TemplateArgument> &Converted, + bool UpdateArgsWithConversions = true); bool CheckTemplateTypeArgument(TemplateTypeParmDecl *Param, TemplateArgumentLoc &Arg, diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 2920ede3b5eea3de477e0ab2e6a8e2fa993edfac..90de88ac3327a33a8e23678e34010eb1b67237db 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/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<ElaboratedTypeLoc>()) { + 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<InjectedClassNameTypeLoc>()) + 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<RecordTypeLoc>()) + if (auto *CTSD = + dyn_cast<ClassTemplateSpecializationDecl>(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<TemplateArgument> &Converted) { +bool Sema::CheckTemplateArgumentList( + TemplateDecl *Template, SourceLocation TemplateLoc, + TemplateArgumentListInfo &TemplateArgs, bool PartialTemplateArgs, + SmallVectorImpl<TemplateArgument> &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/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index 93e796ee9668ecb6d2abdafbd30159d63a1ed68e..3197647d8d8beb3f1326e20a88df161eef5e143f 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/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/test/CXX/drs/dr10xx.cpp b/test/CXX/drs/dr10xx.cpp index e11e796165e22ab36e2ec3b0be0c51c90e2240ac..0ae55f5e07c073a1f0abbf8dded11d3bb17eb1a5 100644 --- a/test/CXX/drs/dr10xx.cpp +++ b/test/CXX/drs/dr10xx.cpp @@ -12,6 +12,29 @@ namespace std { }; } +namespace dr1004 { // dr1004: 5 + template<typename> struct A {}; + template<typename> struct B1 {}; + template<template<typename> class> struct B2 {}; + template<typename X> void f(); // expected-note {{[with X = dr1004::A<int>]}} + template<template<typename> class X> void f(); // expected-note {{[with X = A]}} + template<template<typename> class X> void g(); // expected-note {{[with X = A]}} + template<typename X> void g(); // expected-note {{[with X = dr1004::A<int>]}} + struct C : A<int> { + B1<A> b1a; + B2<A> b2a; + void h() { + f<A>(); // expected-error {{ambiguous}} + g<A>(); // 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 T, template<class> class U = T::template A> struct Third { }; + Third<A<int> > t; +} + namespace dr1048 { // dr1048: 3.6 struct A {}; const A f(); diff --git a/test/CXX/temp/temp.res/temp.local/p1.cpp b/test/CXX/temp/temp.res/temp.local/p1.cpp index f6ef636daa56b729c6e307d6bc033a7ab7d773ca..faa85cb5fce3000adbb8f0170a5d435e10ed2771 100644 --- a/test/CXX/temp/temp.res/temp.local/p1.cpp +++ b/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<typename> char id; + +template<typename> struct TempType {}; +template<template<typename> class> struct TempTemp {}; + +template<typename> void use(int&); // expected-note {{invalid explicitly-specified argument}} expected-note {{no known conversion}} +template<template<typename> class> void use(float&); // expected-note 2{{no known conversion}} +template<int> void use(char&); // expected-note 2{{invalid explicitly-specified argument}} + +template<typename T> struct A { + template<typename> struct C {}; + struct B : C<T> { + // When it is used with a template-argument-list, + A<int> *aint; + typename B::template C<int> *cint; + + // as a template-argument for a template template-parameter, + TempTemp<A> a_as_temp; + TempTemp<B::template C> c_as_temp; + + // or as the final identifier in the elaborated-type-specifier of a friend + // class template declaration, + template<typename U> 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> a_as_type; + TempType<typename B::C> c_as_type; + friend struct A; + friend struct B::C; + + void f(T &t) { + use<A>(t); // expected-error {{no matching function}} + if constexpr (&id<T> != &id<int>) + use<B::template C>(t); // expected-error {{no matching function}} + } + }; +}; + +template struct A<int>; +template struct A<float>; +template struct A<char>; // expected-note {{instantiation of}} template <typename T> struct X0 { X0(); diff --git a/www/cxx_dr_status.html b/www/cxx_dr_status.html index ffc076512446088cdb96a21ab7af13b7925a2248..b4a4d7710f5e9118a543ae42680138ef47d99028 100644 --- a/www/cxx_dr_status.html +++ b/www/cxx_dr_status.html @@ -1937,7 +1937,7 @@ of class templates</td> <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#316">316</a></td> <td>NAD</td> <td>Injected-class-name of template used as template template parameter</td> - <td class="none" align="center">Superseded by <a href="#1004">1004</a></td> + <td class="svn" align="center">Superseded by <a href="#1004">1004</a></td> </tr> <tr id="317"> <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#317">317</a></td> @@ -5839,7 +5839,7 @@ and <I>POD class</I></td> <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1004">1004</a></td> <td>C++11</td> <td>Injected-class-names as arguments for template template parameters</td> - <td class="none" align="center">Unknown</td> + <td class="svn" align="center">SVN</td> </tr> <tr id="1005"> <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#1005">1005</a></td>