Perform the function-to-pointer adjustment during template argument

deduction where the parameter is a function reference, function
pointer, or member function pointer and the argument is an overloaded
function. Fixes <rdar://problem/8360106>, a template argument
deduction issue found by Boost.Filesystem.

llvm-svn: 112523
This commit is contained in:
Douglas Gregor 2010-08-30 21:04:23 +00:00
parent 4cd8a126c3
commit 66d2c8e886
2 changed files with 61 additions and 31 deletions

View File

@ -1542,12 +1542,20 @@ static QualType GetTypeOfFunction(ASTContext &Context,
/// undeduced context
static QualType
ResolveOverloadForDeduction(Sema &S, TemplateParameterList *TemplateParams,
Expr *Arg, QualType ParamType) {
Expr *Arg, QualType ParamType,
bool ParamWasReference) {
OverloadExpr::FindResult R = OverloadExpr::find(Arg);
OverloadExpr *Ovl = R.Expression;
// C++0x [temp.deduct.call]p4
unsigned TDF = 0;
if (ParamWasReference)
TDF |= TDF_ParamWithReferenceType;
if (R.IsAddressOfOperand)
TDF |= TDF_IgnoreQualifiers;
// If there were explicit template arguments, we can only find
// something via C++ [temp.arg.explicit]p3, i.e. if the arguments
// unambiguously name a full specialization.
@ -1583,6 +1591,11 @@ ResolveOverloadForDeduction(Sema &S, TemplateParameterList *TemplateParams,
QualType ArgType = GetTypeOfFunction(S.Context, R, Fn);
if (ArgType.isNull()) continue;
// Function-to-pointer conversion.
if (!ParamWasReference && ParamType->isPointerType() &&
ArgType->isFunctionType())
ArgType = S.Context.getPointerType(ArgType);
// - If the argument is an overload set (not containing function
// templates), trial argument deduction is attempted using each
// of the members of the set. If deduction succeeds for only one
@ -1598,8 +1611,6 @@ ResolveOverloadForDeduction(Sema &S, TemplateParameterList *TemplateParams,
llvm::SmallVector<DeducedTemplateArgument, 8>
Deduced(TemplateParams->size());
TemplateDeductionInfo Info(S.Context, Ovl->getNameLoc());
unsigned TDF = 0;
Sema::TemplateDeductionResult Result
= DeduceTemplateArguments(S, TemplateParams,
ParamType, ArgType,
@ -1694,20 +1705,40 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate,
QualType ParamType = ParamTypes[I];
QualType ArgType = Args[I]->getType();
// C++0x [temp.deduct.call]p3:
// If P is a cv-qualified type, the top level cv-qualifiers of Ps type
// are ignored for type deduction.
if (ParamType.getCVRQualifiers())
ParamType = ParamType.getLocalUnqualifiedType();
const ReferenceType *ParamRefType = ParamType->getAs<ReferenceType>();
if (ParamRefType) {
// [...] If P is a reference type, the type referred to by P is used
// for type deduction.
ParamType = ParamRefType->getPointeeType();
}
// Overload sets usually make this parameter an undeduced
// context, but there are sometimes special circumstances.
if (ArgType == Context.OverloadTy) {
ArgType = ResolveOverloadForDeduction(*this, TemplateParams,
Args[I], ParamType);
Args[I], ParamType,
ParamRefType != 0);
if (ArgType.isNull())
continue;
}
// C++ [temp.deduct.call]p2:
// If P is not a reference type:
QualType CanonParamType = Context.getCanonicalType(ParamType);
bool ParamWasReference = isa<ReferenceType>(CanonParamType);
if (!ParamWasReference) {
if (ParamRefType) {
// C++0x [temp.deduct.call]p3:
// [...] If P is of the form T&&, where T is a template parameter, and
// the argument is an lvalue, the type A& is used in place of A for
// type deduction.
if (ParamRefType->isRValueReferenceType() &&
ParamRefType->getAs<TemplateTypeParmType>() &&
Args[I]->isLvalue(Context) == Expr::LV_Valid)
ArgType = Context.getLValueReferenceType(ArgType);
} else {
// C++ [temp.deduct.call]p2:
// If P is not a reference type:
// - If A is an array type, the pointer type produced by the
// array-to-pointer standard conversion (4.2) is used in place of
// A for type deduction; otherwise,
@ -1722,30 +1753,11 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate,
// - If A is a cv-qualified type, the top level cv-qualifiers of As
// type are ignored for type deduction.
QualType CanonArgType = Context.getCanonicalType(ArgType);
if (CanonArgType.getLocalCVRQualifiers())
ArgType = CanonArgType.getLocalUnqualifiedType();
if (ArgType.getCVRQualifiers())
ArgType = ArgType.getUnqualifiedType();
}
}
// C++0x [temp.deduct.call]p3:
// If P is a cv-qualified type, the top level cv-qualifiers of Ps type
// are ignored for type deduction.
if (CanonParamType.getLocalCVRQualifiers())
ParamType = CanonParamType.getLocalUnqualifiedType();
if (const ReferenceType *ParamRefType = ParamType->getAs<ReferenceType>()) {
// [...] If P is a reference type, the type referred to by P is used
// for type deduction.
ParamType = ParamRefType->getPointeeType();
// [...] If P is of the form T&&, where T is a template parameter, and
// the argument is an lvalue, the type A& is used in place of A for
// type deduction.
if (isa<RValueReferenceType>(ParamRefType) &&
ParamRefType->getAs<TemplateTypeParmType>() &&
Args[I]->isLvalue(Context) == Expr::LV_Valid)
ArgType = Context.getLValueReferenceType(ArgType);
}
// C++0x [temp.deduct.call]p4:
// In general, the deduction process attempts to find template argument
// values that will make the deduced A identical to A (after the type A
@ -1755,7 +1767,7 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate,
// - If the original P is a reference type, the deduced A (i.e., the
// type referred to by the reference) can be more cv-qualified than
// the transformed A.
if (ParamWasReference)
if (ParamRefType)
TDF |= TDF_ParamWithReferenceType;
// - The transformed A can be another pointer or pointer to member
// type that can be converted to the deduced A via a qualification

View File

@ -93,3 +93,21 @@ namespace test1 {
invoke(&temp2<int, int>); // expected-error {{no matching function for call to 'invoke'}}
}
}
namespace rdar8360106 {
template<typename R, typename T> void f0(R (*)(T), T);
template<typename R, typename T> void f1(R (&)(T) , T); // expected-note{{candidate template ignored: couldn't infer template argument 'R'}}
template<typename R, typename T> void f2(R (* const&)(T), T); // expected-note{{candidate template ignored: couldn't infer template argument 'R'}}
int g(int);
int g(int, int);
void h() {
f0(g, 1);
f0(&g, 1);
f1(g, 1);
f1(&g, 1); // expected-error{{no matching function for call to 'f1'}}
f2(g, 1); // expected-error{{no matching function for call to 'f2'}}
f2(&g, 1);
}
}