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(Context.getCanonicalDecl(Member)); - Converted->push_back(TemplateArgument(StartLoc, Member)); - } - + Member = cast_or_null(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(Context.getCanonicalDecl(Entity)); - Converted->push_back(TemplateArgument(StartLoc, Entity)); - } + Entity = cast_or_null(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(Context.getCanonicalDecl(Entity)); - Converted->push_back(TemplateArgument(StartLoc, Entity)); - } - + Entity = cast_or_null(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(Context.getCanonicalDecl(Entity)); - Converted->push_back(TemplateArgument(StartLoc, Entity)); - } - + Entity = cast(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(Context.getCanonicalDecl(Member)); - Converted->push_back(TemplateArgument(StartLoc, Member)); - } - + Member = cast_or_null(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(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(ClassTemplate->getTemplateParameters()->getParam(I)); + if (isa(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(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(InstArg.getAsDecl()); + QualType T = Context.OverloadTy; + if (ValueDecl *VD = dyn_cast(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(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(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((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::value? 1 : -1]; +// Test substitution of non-dependent arguments back into the template +// argument list of the class template partial specialization. template 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 >::value? -1 : 1]; +int is_nested_value_type_identity0[ + is_nested_value_type_identity >::value? -1 : 1]; int is_nested_value_type_identity1[ is_nested_value_type_identity::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::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 +struct is_sizeof_T { + static const bool value = false; +}; + +template +struct is_sizeof_T { + static const bool value = true; +}; + +int is_sizeof_T0[is_sizeof_T::value? 1 : -1]; +int is_sizeof_T1[is_sizeof_T::value? -1 : 1]; + +template +struct HasStaticOfT { + static T value; + static T other_value; +}; + +struct DerivedStaticOfInt : HasStaticOfT { }; + +template +struct is_static_int_val { + static const bool value = false; +}; + +template +struct is_static_int_val { + static const bool value = true; +}; + +int is_static_int_val0[ + is_static_int_val, int, + &HasStaticOfT::value>::value ? 1 : -1]; +int is_static_int_val1[ + is_static_int_val, int, + &HasStaticOfT::other_value>::value ? -1 : 1]; +int is_static_int_val2[ + is_static_int_val::value>::value ? 1 : -1];