From b70126a328f89937f46db42f9e3cba1592887c91 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Fri, 3 Feb 2012 17:16:23 +0000 Subject: [PATCH] When a pack expansion occurs in the template argument list of an alias template without a corresponding parameter pack, don't immediately substitute the alias template. This is under discussion in the C++ committee, and may become ill-formed, but for now we match GCC. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@149697 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Type.h | 26 ++++++++++++--- include/clang/Sema/Sema.h | 8 ++++- lib/AST/ASTContext.cpp | 32 ++++++++++++------- lib/AST/Type.cpp | 12 ++----- lib/Sema/SemaTemplate.cpp | 19 ++++++++--- .../temp.variadic/fixed-expansion.cpp | 7 ++++ test/CodeGenCXX/mangle-alias-template.cpp | 7 ++++ 7 files changed, 79 insertions(+), 32 deletions(-) diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index cb183b8d7a..0fd9e51b3a 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -3484,8 +3484,12 @@ class TemplateSpecializationType /// \brief - The number of template arguments named in this class /// template specialization. - unsigned NumArgs; + unsigned NumArgs : 31; + /// \brief Whether this template specialization type is a substituted + /// type alias. + bool TypeAlias : 1; + TemplateSpecializationType(TemplateName T, const TemplateArgument *Args, unsigned NumArgs, QualType Canon, @@ -3527,9 +3531,23 @@ public: return isa(getCanonicalTypeInternal()); } - /// True if this template specialization type is for a type alias - /// template. - bool isTypeAlias() const; + /// \brief Determine if this template specialization type is for a type alias + /// template that has been substituted. + /// + /// Nearly every template specialization type whose template is an alias + /// template will be substituted. However, this is not the case when + /// the specialization contains a pack expansion but the template alias + /// does not have a corresponding parameter pack, e.g., + /// + /// \code + /// template struct S; + /// template using A = S; + /// template struct X { + /// typedef A type; // not a type alias + /// }; + /// \endcode + bool isTypeAlias() const { return TypeAlias; } + /// Get the aliased type, if this is a specialization of a type alias /// template. QualType getAliasedType() const { diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 4d27ba0292..86a983c1d9 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -4133,12 +4133,18 @@ public: /// \param Converted Will receive the converted, canonicalized template /// arguments. /// + /// + /// \param ExpansionIntoFixedList If non-NULL, will be set true to indicate + /// when the template arguments contain a pack expansion that is being + /// expanded into a fixed parameter list. + /// /// \returns True if an error occurred, false otherwise. bool CheckTemplateArgumentList(TemplateDecl *Template, SourceLocation TemplateLoc, TemplateArgumentListInfo &TemplateArgs, bool PartialTemplateArgs, - SmallVectorImpl &Converted); + SmallVectorImpl &Converted, + bool *ExpansionIntoFixedList = 0); bool CheckTemplateTypeArgument(TemplateTypeParmDecl *Param, const TemplateArgumentLoc &Arg, diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 3b669320e9..c9af67c8b6 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -2450,6 +2450,17 @@ ASTContext::getTemplateSpecializationType(TemplateName Template, Underlying); } +#ifndef NDEBUG +static bool hasAnyPackExpansions(const TemplateArgument *Args, + unsigned NumArgs) { + for (unsigned I = 0; I != NumArgs; ++I) + if (Args[I].isPackExpansion()) + return true; + + return true; +} +#endif + QualType ASTContext::getTemplateSpecializationType(TemplateName Template, const TemplateArgument *Args, @@ -2461,16 +2472,18 @@ ASTContext::getTemplateSpecializationType(TemplateName Template, if (QualifiedTemplateName *QTN = Template.getAsQualifiedTemplateName()) Template = TemplateName(QTN->getTemplateDecl()); - bool isTypeAlias = + bool IsTypeAlias = Template.getAsTemplateDecl() && isa(Template.getAsTemplateDecl()); - QualType CanonType; if (!Underlying.isNull()) CanonType = getCanonicalType(Underlying); else { - assert(!isTypeAlias && - "Underlying type for template alias must be computed by caller"); + // We can get here with an alias template when the specialization contains + // a pack expansion that does not match up with a parameter pack. + assert((!IsTypeAlias || hasAnyPackExpansions(Args, NumArgs)) && + "Caller must compute aliased type"); + IsTypeAlias = false; CanonType = getCanonicalTemplateSpecializationType(Template, Args, NumArgs); } @@ -2480,13 +2493,11 @@ ASTContext::getTemplateSpecializationType(TemplateName Template, // we don't unique and don't want to lose. void *Mem = Allocate(sizeof(TemplateSpecializationType) + sizeof(TemplateArgument) * NumArgs + - (isTypeAlias ? sizeof(QualType) : 0), + (IsTypeAlias? sizeof(QualType) : 0), TypeAlignment); TemplateSpecializationType *Spec - = new (Mem) TemplateSpecializationType(Template, - Args, NumArgs, - CanonType, - isTypeAlias ? Underlying : QualType()); + = new (Mem) TemplateSpecializationType(Template, Args, NumArgs, CanonType, + IsTypeAlias ? Underlying : QualType()); Types.push_back(Spec); return QualType(Spec, 0); @@ -2498,9 +2509,6 @@ ASTContext::getCanonicalTemplateSpecializationType(TemplateName Template, unsigned NumArgs) const { assert(!Template.getAsDependentTemplateName() && "No dependent template names here!"); - assert((!Template.getAsTemplateDecl() || - !isa(Template.getAsTemplateDecl())) && - "Underlying type for template alias must be computed by caller"); // Look through qualified template names. if (QualifiedTemplateName *QTN = Template.getAsQualifiedTemplateName()) diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 698474b838..ebf5706ed0 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -1884,7 +1884,7 @@ TemplateSpecializationType(TemplateName T, false, Canon.isNull()? T.containsUnexpandedParameterPack() : Canon->containsUnexpandedParameterPack()), - Template(T), NumArgs(NumArgs) { + Template(T), NumArgs(NumArgs), TypeAlias(!AliasedType.isNull()) { assert(!T.getAsDependentTemplateName() && "Use DependentTemplateSpecializationType for dependent template-name"); assert((T.getKind() == TemplateName::Template || @@ -1923,10 +1923,7 @@ TemplateSpecializationType(TemplateName T, } // Store the aliased type if this is a type alias template specialization. - bool IsTypeAlias = !AliasedType.isNull(); - assert(IsTypeAlias == isTypeAlias() && - "allocated wrong size for type alias"); - if (IsTypeAlias) { + if (TypeAlias) { TemplateArgument *Begin = reinterpret_cast(this + 1); *reinterpret_cast(Begin + getNumArgs()) = AliasedType; } @@ -1943,11 +1940,6 @@ TemplateSpecializationType::Profile(llvm::FoldingSetNodeID &ID, Args[Idx].Profile(ID, Context); } -bool TemplateSpecializationType::isTypeAlias() const { - TemplateDecl *D = Template.getAsTemplateDecl(); - return D && isa(D); -} - QualType QualifierCollector::apply(const ASTContext &Context, QualType QT) const { if (!hasNonFastQualifiers()) diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 7cd604d585..eca491f733 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -1918,15 +1918,17 @@ QualType Sema::CheckTemplateIdType(TemplateName Name, // Check that the template argument list is well-formed for this // template. SmallVector Converted; + bool ExpansionIntoFixedList = false; if (CheckTemplateArgumentList(Template, TemplateLoc, TemplateArgs, - false, Converted)) + false, Converted, &ExpansionIntoFixedList)) return QualType(); QualType CanonType; bool InstantiationDependent = false; - if (TypeAliasTemplateDecl *AliasTemplate - = dyn_cast(Template)) { + TypeAliasTemplateDecl *AliasTemplate = 0; + if (!ExpansionIntoFixedList && + (AliasTemplate = dyn_cast(Template))) { // Find the canonical type for this type alias template specialization. TypeAliasDecl *Pattern = AliasTemplate->getTemplatedDecl(); if (Pattern->isInvalidDecl()) @@ -2891,7 +2893,11 @@ bool Sema::CheckTemplateArgumentList(TemplateDecl *Template, SourceLocation TemplateLoc, TemplateArgumentListInfo &TemplateArgs, bool PartialTemplateArgs, - SmallVectorImpl &Converted) { + SmallVectorImpl &Converted, + bool *ExpansionIntoFixedList) { + if (ExpansionIntoFixedList) + *ExpansionIntoFixedList = false; + TemplateParameterList *Params = Template->getTemplateParameters(); unsigned NumParams = Params->size(); unsigned NumArgs = TemplateArgs.size(); @@ -2901,7 +2907,7 @@ bool Sema::CheckTemplateArgumentList(TemplateDecl *Template, bool HasParameterPack = NumParams > 0 && Params->getParam(NumParams - 1)->isTemplateParameterPack(); - + // C++ [temp.arg]p1: // [...] The type and form of each template-argument specified in // a template-id shall match the type and form specified for the @@ -3088,6 +3094,9 @@ bool Sema::CheckTemplateArgumentList(TemplateDecl *Template, ArgumentPack.size())); ArgumentPack.clear(); } + } else if (ExpansionIntoFixedList) { + // We have expanded a pack into a fixed list. + *ExpansionIntoFixedList = true; } return Invalid; diff --git a/test/CXX/temp/temp.decls/temp.variadic/fixed-expansion.cpp b/test/CXX/temp/temp.decls/temp.variadic/fixed-expansion.cpp index 5db21570aa..fb727543ef 100644 --- a/test/CXX/temp/temp.decls/temp.variadic/fixed-expansion.cpp +++ b/test/CXX/temp/temp.decls/temp.variadic/fixed-expansion.cpp @@ -118,3 +118,10 @@ namespace PartialSpecialization { X0 x0if; X0 x0ifd; } + +namespace FixedAliasTemplate { + template struct S {}; + template using U = S; + template U &f(U, Ts...); + S &s1 = f({}, 0, 0.0); +} diff --git a/test/CodeGenCXX/mangle-alias-template.cpp b/test/CodeGenCXX/mangle-alias-template.cpp index 1143ea114a..5ace0b01cc 100644 --- a/test/CodeGenCXX/mangle-alias-template.cpp +++ b/test/CodeGenCXX/mangle-alias-template.cpp @@ -11,6 +11,10 @@ template void g(T); template class F> void h(F); +template struct S {}; +template using U = S; +template void h(U, Ts...); + // CHECK: define void @_Z1zv( void z() { vector VI; @@ -38,4 +42,7 @@ void z() { Vec> VVI; g(VVI); // CHECK: call void @_Z1gI6vectorIS0_Ii5allocIiEES1_IS3_EEEvT_( + + // CHECK: call void @_Z1hIJidEEv1UIDpT_ES2_ + h({}, 0, 0.0); } -- 2.40.0