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.

llvm-svn: 149697
This commit is contained in:
Douglas Gregor 2012-02-03 17:16:23 +00:00
parent e340deedbf
commit 1f79ca839c
7 changed files with 79 additions and 32 deletions

View File

@ -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<InjectedClassNameType>(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<typename T, typename U, typename V> struct S;
/// template<typename T, typename U> using A = S<T, int, U>;
/// template<typename... Ts> struct X {
/// typedef A<Ts...> 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 {

View File

@ -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<TemplateArgument> &Converted);
SmallVectorImpl<TemplateArgument> &Converted,
bool *ExpansionIntoFixedList = 0);
bool CheckTemplateTypeArgument(TemplateTypeParmDecl *Param,
const TemplateArgumentLoc &Arg,

View File

@ -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<TypeAliasTemplateDecl>(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<TypeAliasTemplateDecl>(Template.getAsTemplateDecl())) &&
"Underlying type for template alias must be computed by caller");
// Look through qualified template names.
if (QualifiedTemplateName *QTN = Template.getAsQualifiedTemplateName())

View File

@ -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<TemplateArgument *>(this + 1);
*reinterpret_cast<QualType*>(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<TypeAliasTemplateDecl>(D);
}
QualType
QualifierCollector::apply(const ASTContext &Context, QualType QT) const {
if (!hasNonFastQualifiers())

View File

@ -1918,15 +1918,17 @@ QualType Sema::CheckTemplateIdType(TemplateName Name,
// Check that the template argument list is well-formed for this
// template.
SmallVector<TemplateArgument, 4> 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<TypeAliasTemplateDecl>(Template)) {
TypeAliasTemplateDecl *AliasTemplate = 0;
if (!ExpansionIntoFixedList &&
(AliasTemplate = dyn_cast<TypeAliasTemplateDecl>(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<TemplateArgument> &Converted) {
SmallVectorImpl<TemplateArgument> &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;

View File

@ -118,3 +118,10 @@ namespace PartialSpecialization {
X0<int, float> x0if;
X0<int, float, double> x0ifd;
}
namespace FixedAliasTemplate {
template<typename,typename,typename> struct S {};
template<typename T, typename U> using U = S<T, int, U>;
template<typename...Ts> U<Ts...> &f(U<Ts...>, Ts...);
S<int, int, double> &s1 = f({}, 0, 0.0);
}

View File

@ -11,6 +11,10 @@ template<typename T> void g(T);
template<template<typename> class F> void h(F<int>);
template<typename,typename,typename> struct S {};
template<typename T, typename U> using U = S<T, int, U>;
template<typename...Ts> void h(U<Ts...>, Ts...);
// CHECK: define void @_Z1zv(
void z() {
vector<int> VI;
@ -38,4 +42,7 @@ void z() {
Vec<Vec<int>> VVI;
g(VVI);
// CHECK: call void @_Z1gI6vectorIS0_Ii5allocIiEES1_IS3_EEEvT_(
// CHECK: call void @_Z1hIJidEEv1UIDpT_ES2_
h({}, 0, 0.0);
}