aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/Sema/Sema.h2
-rw-r--r--lib/Sema/SemaTemplate.cpp77
-rw-r--r--lib/Sema/SemaTemplateDeduction.cpp180
-rw-r--r--lib/Sema/SemaTemplateInstantiate.cpp12
-rw-r--r--test/SemaTemplate/temp_class_spec.cpp55
5 files changed, 240 insertions, 86 deletions
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index 866f898391..2df1ae2950 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -1995,7 +1995,7 @@ public:
bool CheckTemplateArgumentPointerToMember(Expr *Arg, NamedDecl *&Member);
bool CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
QualType InstantiatedParamType, Expr *&Arg,
- TemplateArgumentListBuilder *Converted = 0);
+ TemplateArgument &Converted);
bool CheckTemplateArgument(TemplateTemplateParmDecl *Param, DeclRefExpr *Arg);
bool TemplateParameterListsAreEqual(TemplateParameterList *New,
TemplateParameterList *Old,
diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp
index f03c1acb8e..7c1c7cf0e1 100644
--- a/lib/Sema/SemaTemplate.cpp
+++ b/lib/Sema/SemaTemplate.cpp
@@ -297,7 +297,9 @@ void Sema::ActOnNonTypeTemplateParameterDefault(DeclPtrTy TemplateParamD,
// FIXME: Implement this check! Needs a recursive walk over the types.
// Check the well-formedness of the default template argument.
- if (CheckTemplateArgument(TemplateParm, TemplateParm->getType(), Default)) {
+ TemplateArgument Converted;
+ if (CheckTemplateArgument(TemplateParm, TemplateParm->getType(), Default,
+ Converted)) {
TemplateParm->setInvalidDecl();
return;
}
@@ -1116,8 +1118,11 @@ bool Sema::CheckTemplateArgumentList(TemplateDecl *Template,
case TemplateArgument::Expression: {
Expr *E = Arg.getAsExpr();
- if (CheckTemplateArgument(NTTP, NTTPType, E, &Converted))
+ TemplateArgument Result;
+ if (CheckTemplateArgument(NTTP, NTTPType, E, Result))
Invalid = true;
+ else
+ Converted.push_back(Result);
break;
}
@@ -1401,11 +1406,10 @@ Sema::CheckTemplateArgumentPointerToMember(Expr *Arg, NamedDecl *&Member) {
/// InstantiatedParamType is the type of the non-type template
/// parameter after it has been instantiated.
///
-/// If Converted is non-NULL and no errors occur, the value
-/// of this argument will be added to the end of the Converted vector.
+/// If no error was detected, Converted receives the converted template argument.
bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
QualType InstantiatedParamType, Expr *&Arg,
- TemplateArgumentListBuilder *Converted) {
+ TemplateArgument &Converted) {
SourceLocation StartLoc = Arg->getSourceRange().getBegin();
// If either the parameter has a dependent type or the argument is
@@ -1413,8 +1417,7 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
// FIXME: Add template argument to Converted!
if (InstantiatedParamType->isDependentType() || Arg->isTypeDependent()) {
// FIXME: Produce a cloned, canonical expression?
- if (Converted)
- Converted->push_back(TemplateArgument(Arg));
+ Converted = TemplateArgument(Arg);
return false;
}
@@ -1479,7 +1482,7 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
QualType IntegerType = Context.getCanonicalType(ParamType);
if (const EnumType *Enum = IntegerType->getAsEnumType())
- IntegerType = Enum->getDecl()->getIntegerType();
+ IntegerType = Context.getCanonicalType(Enum->getDecl()->getIntegerType());
if (!Arg->isValueDependent()) {
// Check that an unsigned parameter does not receive a negative
@@ -1509,21 +1512,19 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
Value.setIsSigned(IntegerType->isSignedIntegerType());
}
- if (Converted) {
- // Add the value of this argument to the list of converted
- // arguments. We use the bitwidth and signedness of the template
- // parameter.
- if (Arg->isValueDependent()) {
- // The argument is value-dependent. Create a new
- // TemplateArgument with the converted expression.
- Converted->push_back(TemplateArgument(Arg));
- return false;
- }
-
- Converted->push_back(TemplateArgument(StartLoc, Value,
- ParamType->isEnumeralType() ? ParamType : IntegerType));
+ // Add the value of this argument to the list of converted
+ // arguments. We use the bitwidth and signedness of the template
+ // parameter.
+ if (Arg->isValueDependent()) {
+ // The argument is value-dependent. Create a new
+ // TemplateArgument with the converted expression.
+ Converted = TemplateArgument(Arg);
+ return false;
}
+ Converted = TemplateArgument(StartLoc, Value,
+ ParamType->isEnumeralType() ? ParamType
+ : IntegerType);
return false;
}
@@ -1590,11 +1591,8 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
if (CheckTemplateArgumentPointerToMember(Arg, Member))
return true;
- if (Converted) {
- Member = cast_or_null<NamedDecl>(Context.getCanonicalDecl(Member));
- Converted->push_back(TemplateArgument(StartLoc, Member));
- }
-
+ Member = cast_or_null<NamedDecl>(Context.getCanonicalDecl(Member));
+ Converted = TemplateArgument(StartLoc, Member);
return false;
}
@@ -1602,10 +1600,8 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
if (CheckTemplateArgumentAddressOfObjectOrFunction(Arg, Entity))
return true;
- if (Converted) {
- Entity = cast_or_null<NamedDecl>(Context.getCanonicalDecl(Entity));
- Converted->push_back(TemplateArgument(StartLoc, Entity));
- }
+ Entity = cast_or_null<NamedDecl>(Context.getCanonicalDecl(Entity));
+ Converted = TemplateArgument(StartLoc, Entity);
return false;
}
@@ -1643,11 +1639,8 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
if (CheckTemplateArgumentAddressOfObjectOrFunction(Arg, Entity))
return true;
- if (Converted) {
- Entity = cast_or_null<NamedDecl>(Context.getCanonicalDecl(Entity));
- Converted->push_back(TemplateArgument(StartLoc, Entity));
- }
-
+ Entity = cast_or_null<NamedDecl>(Context.getCanonicalDecl(Entity));
+ Converted = TemplateArgument(StartLoc, Entity);
return false;
}
@@ -1687,11 +1680,8 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
if (CheckTemplateArgumentAddressOfObjectOrFunction(Arg, Entity))
return true;
- if (Converted) {
- Entity = cast<NamedDecl>(Context.getCanonicalDecl(Entity));
- Converted->push_back(TemplateArgument(StartLoc, Entity));
- }
-
+ Entity = cast<NamedDecl>(Context.getCanonicalDecl(Entity));
+ Converted = TemplateArgument(StartLoc, Entity);
return false;
}
@@ -1719,11 +1709,8 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
if (CheckTemplateArgumentPointerToMember(Arg, Member))
return true;
- if (Converted) {
- Member = cast_or_null<NamedDecl>(Context.getCanonicalDecl(Member));
- Converted->push_back(TemplateArgument(StartLoc, Member));
- }
-
+ Member = cast_or_null<NamedDecl>(Context.getCanonicalDecl(Member));
+ Converted = TemplateArgument(StartLoc, Member);
return false;
}
diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp
index 6e7a47aeb2..fe64946e14 100644
--- a/lib/Sema/SemaTemplateDeduction.cpp
+++ b/lib/Sema/SemaTemplateDeduction.cpp
@@ -531,30 +531,36 @@ Sema::DeduceTemplateArguments(ClassTemplatePartialSpecializationDecl *Partial,
Deduced.data(), Deduced.size());
if (Inst)
return 0;
-
- // FIXME: Substitute the deduced template arguments into the template
- // arguments of the class template partial specialization; the resulting
- // template arguments should match TemplateArgs exactly.
-
- for (unsigned I = 0, N = Deduced.size(); I != N; ++I) {
- TemplateArgument &Arg = Deduced[I];
- // FIXME: If this template argument was not deduced, but the corresponding
- // template parameter has a default argument, instantiate the default
- // argument.
- if (Arg.isNull()) // FIXME: Result->Destroy(Context);
+ // C++ [temp.deduct.type]p2:
+ // [...] or if any template argument remains neither deduced nor
+ // explicitly specified, template argument deduction fails.
+ TemplateArgumentListBuilder Builder(Context);
+ for (unsigned I = 0, N = Deduced.size(); I != N; ++I) {
+ if (Deduced[I].isNull())
return 0;
-
+
+ Builder.push_back(Deduced[I]);
+ }
+
+ // Form the template argument list from the deduced template arguments.
+ TemplateArgumentList *DeducedArgumentList
+ = new (Context) TemplateArgumentList(Context, Builder, /*CopyArgs=*/true,
+ /*FlattenArgs=*/true);
+
+ // Now that we have all of the deduced template arguments, take
+ // another pass through them to convert any integral template
+ // arguments to the appropriate type.
+ for (unsigned I = 0, N = Deduced.size(); I != N; ++I) {
+ TemplateArgument &Arg = Deduced[I];
if (Arg.getKind() == TemplateArgument::Integral) {
- // FIXME: Instantiate the type, but we need some context!
const NonTypeTemplateParmDecl *Parm
= cast<NonTypeTemplateParmDecl>(Partial->getTemplateParameters()
->getParam(I));
- // QualType T = InstantiateType(Parm->getType(), *Result,
- // Parm->getLocation(), Parm->getDeclName());
- // if (T.isNull()) // FIXME: Result->Destroy(Context);
- // return 0;
- QualType T = Parm->getType();
+ QualType T = InstantiateType(Parm->getType(), *DeducedArgumentList,
+ Parm->getLocation(), Parm->getDeclName());
+ if (T.isNull()) // FIXME: DeducedArgumentList->Destroy(Context);
+ return 0;
// FIXME: Make sure we didn't overflow our data type!
llvm::APSInt &Value = *Arg.getAsIntegral();
@@ -564,14 +570,134 @@ Sema::DeduceTemplateArguments(ClassTemplatePartialSpecializationDecl *Partial,
Value.setIsSigned(T->isSignedIntegerType());
Arg.setIntegralType(T);
}
+
+ (*DeducedArgumentList)[I] = Arg;
}
-
- // FIXME: This is terrible. DeduceTemplateArguments should use a
- // TemplateArgumentListBuilder directly.
- TemplateArgumentListBuilder Builder(Context);
- for (unsigned I = 0, N = Deduced.size(); I != N; ++I)
- Builder.push_back(Deduced[I]);
-
- return new (Context) TemplateArgumentList(Context, Builder, /*CopyArgs=*/true,
- /*FlattenArgs=*/true);
+
+ // Substitute the deduced template arguments into the template
+ // arguments of the class template partial specialization, and
+ // verify that the instantiated template arguments are both valid
+ // and are equivalent to the template arguments originally provided
+ // to the class template.
+ ClassTemplateDecl *ClassTemplate = Partial->getSpecializedTemplate();
+ const TemplateArgumentList &PartialTemplateArgs = Partial->getTemplateArgs();
+ for (unsigned I = 0, N = PartialTemplateArgs.flat_size(); I != N; ++I) {
+ TemplateArgument InstArg = Instantiate(PartialTemplateArgs[I],
+ *DeducedArgumentList);
+ if (InstArg.isNull()) {
+ // FIXME: DeducedArgumentList->Destroy(Context); (or use RAII)
+ return 0;
+ }
+
+ Decl *Param
+ = const_cast<Decl *>(ClassTemplate->getTemplateParameters()->getParam(I));
+ if (isa<TemplateTypeParmDecl>(Param)) {
+ if (InstArg.getKind() != TemplateArgument::Type ||
+ Context.getCanonicalType(InstArg.getAsType())
+ != Context.getCanonicalType(TemplateArgs[I].getAsType()))
+ // FIXME: DeducedArgumentList->Destroy(Context); (or use RAII)
+ return 0;
+ } else if (NonTypeTemplateParmDecl *NTTP
+ = dyn_cast<NonTypeTemplateParmDecl>(Param)) {
+ QualType T = InstantiateType(NTTP->getType(), TemplateArgs,
+ NTTP->getLocation(), NTTP->getDeclName());
+ if (T.isNull())
+ // FIXME: DeducedArgumentList->Destroy(Context); (or use RAII)
+ return 0;
+
+ if (InstArg.getKind() == TemplateArgument::Declaration ||
+ InstArg.getKind() == TemplateArgument::Expression) {
+ // Turn the template argument into an expression, so that we can
+ // perform type checking on it and convert it to the type of the
+ // non-type template parameter. FIXME: Will this expression be
+ // leaked? It's hard to tell, since our ownership model for
+ // expressions in template arguments is so poor.
+ Expr *E = 0;
+ if (InstArg.getKind() == TemplateArgument::Declaration) {
+ NamedDecl *D = cast<NamedDecl>(InstArg.getAsDecl());
+ QualType T = Context.OverloadTy;
+ if (ValueDecl *VD = dyn_cast<ValueDecl>(D))
+ T = VD->getType().getNonReferenceType();
+ E = new (Context) DeclRefExpr(D, T, InstArg.getLocation());
+ } else {
+ E = InstArg.getAsExpr();
+ }
+
+ // Check that the template argument can be used to initialize
+ // the corresponding template parameter.
+ if (CheckTemplateArgument(NTTP, T, E, InstArg))
+ return 0;
+ }
+
+ switch (InstArg.getKind()) {
+ case TemplateArgument::Null:
+ assert(false && "Null template arguments cannot get here");
+ return 0;
+
+ case TemplateArgument::Type:
+ assert(false && "Type/value mismatch");
+ return 0;
+
+ case TemplateArgument::Integral: {
+ llvm::APSInt &Value = *InstArg.getAsIntegral();
+ if (T->isIntegralType() || T->isEnumeralType()) {
+ QualType IntegerType = Context.getCanonicalType(T);
+ if (const EnumType *Enum = dyn_cast<EnumType>(IntegerType))
+ IntegerType = Context.getCanonicalType(
+ Enum->getDecl()->getIntegerType());
+
+ // Check that an unsigned parameter does not receive a negative
+ // value.
+ if (IntegerType->isUnsignedIntegerType()
+ && (Value.isSigned() && Value.isNegative()))
+ return 0;
+
+ // Check for truncation. If the number of bits in the
+ // instantiated template argument exceeds what is allowed by
+ // the type, template argument deduction fails.
+ unsigned AllowedBits = Context.getTypeSize(IntegerType);
+ if (Value.getActiveBits() > AllowedBits)
+ return 0;
+
+ if (Value.getBitWidth() != AllowedBits)
+ Value.extOrTrunc(AllowedBits);
+ Value.setIsSigned(IntegerType->isSignedIntegerType());
+
+ // Check that the instantiated value is the same as the
+ // value provided as a template argument.
+ if (Value != *TemplateArgs[I].getAsIntegral())
+ return 0;
+ } else if (T->isPointerType() || T->isMemberPointerType()) {
+ // Deal with NULL pointers that are used to initialize
+ // pointer and pointer-to-member non-type template
+ // parameters (C++0x).
+ if (TemplateArgs[I].getAsDecl())
+ return 0; // Not a NULL declaration
+
+ // Check that the integral value is 0, the NULL pointer
+ // constant.
+ if (Value != 0)
+ return 0;
+ } else
+ return 0;
+ break;
+ }
+
+ case TemplateArgument::Declaration:
+ if (Context.getCanonicalDecl(InstArg.getAsDecl())
+ != Context.getCanonicalDecl(TemplateArgs[I].getAsDecl()))
+ return 0;
+ break;
+
+ case TemplateArgument::Expression:
+ // FIXME: Check equality of expressions
+ break;
+ }
+ } else {
+ assert(isa<TemplateTemplateParmDecl>(Param));
+ // FIXME: Check template template arguments
+ }
+ }
+
+ return DeducedArgumentList;
}
diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp
index ee74b9a8bd..964d3b1485 100644
--- a/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/lib/Sema/SemaTemplateInstantiate.cpp
@@ -185,18 +185,12 @@ void Sema::PrintInstantiationStack() {
case ActiveTemplateInstantiation::PartialSpecDeductionInstantiation: {
ClassTemplatePartialSpecializationDecl *PartialSpec
= cast<ClassTemplatePartialSpecializationDecl>((Decl *)Active->Entity);
- std::string TemplateArgsStr
- = TemplateSpecializationType::PrintTemplateArgumentList(
- PartialSpec->getTemplateArgs().getFlatArgumentList(),
- PartialSpec->getTemplateArgs().flat_size(),
- Context.PrintingPolicy);
// FIXME: The active template instantiation's template arguments
// are interesting, too. We should add something like [with T =
// foo, U = bar, etc.] to the string.
Diags.Report(FullSourceLoc(Active->PointOfInstantiation, SourceMgr),
diag::note_partial_spec_deduct_instantiation_here)
- << (PartialSpec->getSpecializedTemplate()->getNameAsString() +
- TemplateArgsStr)
+ << Context.getTypeDeclType(PartialSpec)
<< Active->InstantiationRange;
break;
}
@@ -441,7 +435,7 @@ InstantiateFunctionProtoType(const FunctionProtoType *T,
ParamTypes.push_back(P);
}
- return SemaRef.BuildFunctionType(ResultType, &ParamTypes[0],
+ return SemaRef.BuildFunctionType(ResultType, ParamTypes.data(),
ParamTypes.size(),
T->isVariadic(), T->getTypeQuals(),
Loc, Entity);
@@ -567,7 +561,7 @@ InstantiateTemplateSpecializationType(
TemplateArgs);
return SemaRef.CheckTemplateIdType(Name, Loc, SourceLocation(),
- &InstantiatedTemplateArgs[0],
+ InstantiatedTemplateArgs.data(),
InstantiatedTemplateArgs.size(),
SourceLocation());
}
diff --git a/test/SemaTemplate/temp_class_spec.cpp b/test/SemaTemplate/temp_class_spec.cpp
index c02ce9b728..0d83a9bcc3 100644
--- a/test/SemaTemplate/temp_class_spec.cpp
+++ b/test/SemaTemplate/temp_class_spec.cpp
@@ -224,6 +224,8 @@ int is_member_function_pointer3[
int is_member_function_pointer4[
is_member_function_pointer<int (X::*)(float) const>::value? 1 : -1];
+// Test substitution of non-dependent arguments back into the template
+// argument list of the class template partial specialization.
template<typename T, typename ValueType = T>
struct is_nested_value_type_identity {
static const bool value = false;
@@ -245,11 +247,56 @@ struct HasIdentityValueType {
struct NoValueType { };
-// FIXME: Need substitution into the template arguments of the partial spec
-//int is_nested_value_type_identity0[
-// is_nested_value_type_identity<HasValueType<int> >::value? -1 : 1];
+int is_nested_value_type_identity0[
+ is_nested_value_type_identity<HasValueType<int> >::value? -1 : 1];
int is_nested_value_type_identity1[
is_nested_value_type_identity<HasIdentityValueType>::value? 1 : -1];
// FIXME: Enable when we have SFINAE support
-//int is_nested_value_type_identity0[
+//int is_nested_value_type_identity2[
// is_nested_value_type_identity<NoValueType>::value? -1 : 1];
+
+// FIXME: The tests that follow are stress-tests for the substitution
+// of deduced template arguments into the template argument list of a
+// partial specialization. I believe that we'll need this code for
+// substitution into function templates, but note that the examples
+// below are ill-formed and should eventually be removed.
+template<typename T, int N>
+struct is_sizeof_T {
+ static const bool value = false;
+};
+
+template<typename T>
+struct is_sizeof_T<T, sizeof(T)> {
+ static const bool value = true;
+};
+
+int is_sizeof_T0[is_sizeof_T<int, sizeof(int)>::value? 1 : -1];
+int is_sizeof_T1[is_sizeof_T<int, sizeof(char)>::value? -1 : 1];
+
+template<typename T>
+struct HasStaticOfT {
+ static T value;
+ static T other_value;
+};
+
+struct DerivedStaticOfInt : HasStaticOfT<int> { };
+
+template<typename X, typename T, T *Ptr>
+struct is_static_int_val {
+ static const bool value = false;
+};
+
+template<typename X, typename T>
+struct is_static_int_val<X, T, &X::value> {
+ static const bool value = true;
+};
+
+int is_static_int_val0[
+ is_static_int_val<HasStaticOfT<int>, int,
+ &HasStaticOfT<int>::value>::value ? 1 : -1];
+int is_static_int_val1[
+ is_static_int_val<HasStaticOfT<int>, int,
+ &HasStaticOfT<int>::other_value>::value ? -1 : 1];
+int is_static_int_val2[
+ is_static_int_val<DerivedStaticOfInt, int,
+ &HasStaticOfT<int>::value>::value ? 1 : -1];