Implement template argument deduction when taking the address of a

function template. Most of the change here is in factoring out the
common bits used for template argument deduction from a function call
and when taking the address of a function template.

llvm-svn: 75044
This commit is contained in:
Douglas Gregor 2009-07-08 20:55:45 +00:00
parent fb8d6d5b58
commit 9b14658713
6 changed files with 396 additions and 145 deletions

View File

@ -635,7 +635,8 @@ static bool DeclCanBeLvalue(const NamedDecl *Decl, ASTContext &Ctx) {
return isa<VarDecl>(Decl) || isa<FieldDecl>(Decl) ||
// C++ 3.10p2: An lvalue refers to an object or function.
(Ctx.getLangOptions().CPlusPlus &&
(isa<FunctionDecl>(Decl) || isa<OverloadedFunctionDecl>(Decl)));
(isa<FunctionDecl>(Decl) || isa<OverloadedFunctionDecl>(Decl) ||
isa<FunctionTemplateDecl>(Decl)));
}
/// isLvalue - C99 6.3.2.1: an lvalue is an expression with an object type or an
@ -659,7 +660,7 @@ Expr::isLvalueResult Expr::isLvalue(ASTContext &Ctx) const {
// first, check the type (C99 6.3.2.1). Expressions with function
// type in C are not lvalues, but they can be lvalues in C++.
if (TR->isFunctionType())
if (TR->isFunctionType() || TR == Ctx.OverloadTy)
return LV_NotObjectType;
// Allow qualified void which is an incomplete type other than void (yuck).

View File

@ -2350,7 +2350,22 @@ public:
DeduceTemplateArguments(ClassTemplatePartialSpecializationDecl *Partial,
const TemplateArgumentList &TemplateArgs,
TemplateDeductionInfo &Info);
TemplateDeductionResult
SubstituteExplicitTemplateArguments(FunctionTemplateDecl *FunctionTemplate,
const TemplateArgument *ExplicitTemplateArgs,
unsigned NumExplicitTemplateArgs,
llvm::SmallVectorImpl<TemplateArgument> &Deduced,
llvm::SmallVectorImpl<QualType> &ParamTypes,
QualType *FunctionType,
TemplateDeductionInfo &Info);
TemplateDeductionResult
FinishTemplateArgumentDeduction(FunctionTemplateDecl *FunctionTemplate,
llvm::SmallVectorImpl<TemplateArgument> &Deduced,
FunctionDecl *&Specialization,
TemplateDeductionInfo &Info);
TemplateDeductionResult
DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate,
bool HasExplicitTemplateArgs,
@ -2359,6 +2374,15 @@ public:
Expr **Args, unsigned NumArgs,
FunctionDecl *&Specialization,
TemplateDeductionInfo &Info);
TemplateDeductionResult
DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate,
bool HasExplicitTemplateArgs,
const TemplateArgument *ExplicitTemplateArgs,
unsigned NumExplicitTemplateArgs,
QualType ArgFunctionType,
FunctionDecl *&Specialization,
TemplateDeductionInfo &Info);
void MarkDeducedTemplateParameters(const TemplateArgumentList &TemplateArgs,
llvm::SmallVectorImpl<bool> &Deduced);

View File

@ -4684,7 +4684,8 @@ QualType Sema::CheckAddressOfOperand(Expr *op, SourceLocation OpLoc) {
<< "register variable" << op->getSourceRange();
return QualType();
}
} else if (isa<OverloadedFunctionDecl>(dcl)) {
} else if (isa<OverloadedFunctionDecl>(dcl) ||
isa<FunctionTemplateDecl>(dcl)) {
return Context.OverloadTy;
} else if (isa<FieldDecl>(dcl)) {
// Okay: we can take the address of a field.

View File

@ -3702,7 +3702,8 @@ Sema::ResolveAddressOfOverloadedFunction(Expr *From, QualType ToType,
}
// We only look at pointers or references to functions.
if (!FunctionType->isFunctionType())
FunctionType = Context.getCanonicalType(FunctionType.getUnqualifiedType());
if (!FunctionType->isFunctionType())
return 0;
// Find the actual overloaded function declaration.
@ -3722,44 +3723,69 @@ Sema::ResolveAddressOfOverloadedFunction(Expr *From, QualType ToType,
}
// Try to dig out the overloaded function.
if (DeclRefExpr *DR = dyn_cast<DeclRefExpr>(OvlExpr))
FunctionTemplateDecl *FunctionTemplate = 0;
if (DeclRefExpr *DR = dyn_cast<DeclRefExpr>(OvlExpr)) {
Ovl = dyn_cast<OverloadedFunctionDecl>(DR->getDecl());
FunctionTemplate = dyn_cast<FunctionTemplateDecl>(DR->getDecl());
}
// If there's no overloaded function declaration, we're done.
if (!Ovl)
// If there's no overloaded function declaration or function template,
// we're done.
if (!Ovl && !FunctionTemplate)
return 0;
OverloadIterator Fun;
if (Ovl)
Fun = Ovl;
else
Fun = FunctionTemplate;
// Look through all of the overloaded functions, searching for one
// whose type matches exactly.
// FIXME: When templates or using declarations come along, we'll actually
// have to deal with duplicates, partial ordering, etc. For now, we
// can just do a simple search.
FunctionType = Context.getCanonicalType(FunctionType.getUnqualifiedType());
for (OverloadedFunctionDecl::function_iterator Fun = Ovl->function_begin();
Fun != Ovl->function_end(); ++Fun) {
// FIXME: We still need to cope with duplicates, partial ordering, etc.
for (OverloadIterator FunEnd; Fun != FunEnd; ++Fun) {
// C++ [over.over]p3:
// Non-member functions and static member functions match
// targets of type "pointer-to-function" or "reference-to-function."
// Nonstatic member functions match targets of
// type "pointer-to-member-function."
// Note that according to DR 247, the containing class does not matter.
if (FunctionTemplateDecl *FunctionTemplate
= dyn_cast<FunctionTemplateDecl>(*Fun)) {
// C++ [temp.deduct.funcaddr]p1:
// Template arguments can be deduced from the type specified when
// taking the address of an overloaded function (13.4). The function
// templates function type and the specified type are used as the
// types of P and A, and the deduction is done as described in
// 14.8.2.4.
FunctionDecl *Specialization = 0;
TemplateDeductionInfo Info(Context);
if (TemplateDeductionResult Result
= DeduceTemplateArguments(FunctionTemplate, /*FIXME*/false,
/*FIXME:*/0, /*FIXME:*/0,
FunctionType, Specialization, Info)) {
// FIXME: make a note of the failed deduction for diagnostics.
(void)Result;
} else {
assert(FunctionType
== Context.getCanonicalType(Specialization->getType()));
return Specialization;
}
}
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(*Fun)) {
// Skip non-static functions when converting to pointer, and static
// when converting to member pointer.
if (Method->isStatic() == IsMember)
continue;
} else if (IsMember)
} else if (IsMember) // FIXME: member templates
continue;
if (FunctionDecl *FunDecl = dyn_cast<FunctionDecl>(*Fun)) {
if (FunctionType == Context.getCanonicalType(FunDecl->getType()))
return FunDecl;
} else {
unsigned DiagID
= PP.getDiagnostics().getCustomDiagID(Diagnostic::Warning,
"Clang does not yet support templated conversion functions");
Diag(From->getLocStart(), DiagID);
}
}
}
return 0;
@ -4614,8 +4640,9 @@ void Sema::FixOverloadedFunctionReference(Expr *E, FunctionDecl *Fn) {
FixOverloadedFunctionReference(UnOp->getSubExpr(), Fn);
E->setType(Context.getPointerType(UnOp->getSubExpr()->getType()));
} else if (DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E)) {
assert(isa<OverloadedFunctionDecl>(DR->getDecl()) &&
"Expected overloaded function");
assert((isa<OverloadedFunctionDecl>(DR->getDecl()) ||
isa<FunctionTemplateDecl>(DR->getDecl())) &&
"Expected overloaded function or function template");
DR->setDecl(Fn);
E->setType(Fn->getType());
} else if (MemberExpr *MemExpr = dyn_cast<MemberExpr>(E)) {

View File

@ -956,7 +956,220 @@ static bool isSimpleTemplateIdType(QualType T) {
return false;
}
/// \brief Substitute the explicitly-provided template arguments into the
/// given function template according to C++ [temp.arg.explicit].
///
/// \param FunctionTemplate the function template into which the explicit
/// template arguments will be substituted.
///
/// \param ExplicitTemplateArguments the explicitly-specified template
/// arguments.
///
/// \param NumExplicitTemplateArguments the number of explicitly-specified
/// template arguments in @p ExplicitTemplateArguments. This value may be zero.
///
/// \param Deduced the deduced template arguments, which will be populated
/// with the converted and checked explicit template arguments.
///
/// \param ParamTypes will be populated with the instantiated function
/// parameters.
///
/// \param FunctionType if non-NULL, the result type of the function template
/// will also be instantiated and the pointed-to value will be updated with
/// the instantiated function type.
///
/// \param Info if substitution fails for any reason, this object will be
/// populated with more information about the failure.
///
/// \returns TDK_Success if substitution was successful, or some failure
/// condition.
Sema::TemplateDeductionResult
Sema::SubstituteExplicitTemplateArguments(
FunctionTemplateDecl *FunctionTemplate,
const TemplateArgument *ExplicitTemplateArgs,
unsigned NumExplicitTemplateArgs,
llvm::SmallVectorImpl<TemplateArgument> &Deduced,
llvm::SmallVectorImpl<QualType> &ParamTypes,
QualType *FunctionType,
TemplateDeductionInfo &Info) {
FunctionDecl *Function = FunctionTemplate->getTemplatedDecl();
TemplateParameterList *TemplateParams
= FunctionTemplate->getTemplateParameters();
if (NumExplicitTemplateArgs == 0) {
// No arguments to substitute; just copy over the parameter types and
// fill in the function type.
for (FunctionDecl::param_iterator P = Function->param_begin(),
PEnd = Function->param_end();
P != PEnd;
++P)
ParamTypes.push_back((*P)->getType());
if (FunctionType)
*FunctionType = Function->getType();
return TDK_Success;
}
// Substitution of the explicit template arguments into a function template
/// is a SFINAE context. Trap any errors that might occur.
SFINAETrap Trap(*this);
// C++ [temp.arg.explicit]p3:
// Template arguments that are present shall be specified in the
// declaration order of their corresponding template-parameters. The
// template argument list shall not specify more template-arguments than
// there are corresponding template-parameters.
TemplateArgumentListBuilder Builder(TemplateParams,
NumExplicitTemplateArgs);
// Enter a new template instantiation context where we check the
// explicitly-specified template arguments against this function template,
// and then substitute them into the function parameter types.
InstantiatingTemplate Inst(*this, FunctionTemplate->getLocation(),
FunctionTemplate, Deduced.data(), Deduced.size(),
ActiveTemplateInstantiation::ExplicitTemplateArgumentSubstitution);
if (Inst)
return TDK_InstantiationDepth;
if (CheckTemplateArgumentList(FunctionTemplate,
SourceLocation(), SourceLocation(),
ExplicitTemplateArgs,
NumExplicitTemplateArgs,
SourceLocation(),
true,
Builder) || Trap.hasErrorOccurred())
return TDK_InvalidExplicitArguments;
// Form the template argument list from the explicitly-specified
// template arguments.
TemplateArgumentList *ExplicitArgumentList
= new (Context) TemplateArgumentList(Context, Builder, /*TakeArgs=*/true);
Info.reset(ExplicitArgumentList);
// Instantiate the types of each of the function parameters given the
// explicitly-specified template arguments.
for (FunctionDecl::param_iterator P = Function->param_begin(),
PEnd = Function->param_end();
P != PEnd;
++P) {
QualType ParamType = InstantiateType((*P)->getType(),
*ExplicitArgumentList,
(*P)->getLocation(),
(*P)->getDeclName());
if (ParamType.isNull() || Trap.hasErrorOccurred())
return TDK_SubstitutionFailure;
ParamTypes.push_back(ParamType);
}
// If the caller wants a full function type back, instantiate the return
// type and form that function type.
if (FunctionType) {
// FIXME: exception-specifications?
const FunctionProtoType *Proto
= Function->getType()->getAsFunctionProtoType();
assert(Proto && "Function template does not have a prototype?");
QualType ResultType = InstantiateType(Proto->getResultType(),
*ExplicitArgumentList,
Function->getTypeSpecStartLoc(),
Function->getDeclName());
if (ResultType.isNull() || Trap.hasErrorOccurred())
return TDK_SubstitutionFailure;
*FunctionType = BuildFunctionType(ResultType,
ParamTypes.data(), ParamTypes.size(),
Proto->isVariadic(),
Proto->getTypeQuals(),
Function->getLocation(),
Function->getDeclName());
if (FunctionType->isNull() || Trap.hasErrorOccurred())
return TDK_SubstitutionFailure;
}
// C++ [temp.arg.explicit]p2:
// Trailing template arguments that can be deduced (14.8.2) may be
// omitted from the list of explicit template-arguments. If all of the
// template arguments can be deduced, they may all be omitted; in this
// case, the empty template argument list <> itself may also be omitted.
//
// Take all of the explicitly-specified arguments and put them into the
// set of deduced template arguments.
Deduced.reserve(TemplateParams->size());
for (unsigned I = 0, N = ExplicitArgumentList->size(); I != N; ++I)
Deduced.push_back(ExplicitArgumentList->get(I));
return TDK_Success;
}
/// \brief Finish template argument deduction for a function template,
/// checking the deduced template arguments for completeness and forming
/// the function template specialization.
Sema::TemplateDeductionResult
Sema::FinishTemplateArgumentDeduction(FunctionTemplateDecl *FunctionTemplate,
llvm::SmallVectorImpl<TemplateArgument> &Deduced,
FunctionDecl *&Specialization,
TemplateDeductionInfo &Info) {
TemplateParameterList *TemplateParams
= FunctionTemplate->getTemplateParameters();
// C++ [temp.deduct.type]p2:
// [...] or if any template argument remains neither deduced nor
// explicitly specified, template argument deduction fails.
TemplateArgumentListBuilder Builder(TemplateParams, Deduced.size());
for (unsigned I = 0, N = Deduced.size(); I != N; ++I) {
if (Deduced[I].isNull()) {
Info.Param = makeTemplateParameter(
const_cast<Decl *>(TemplateParams->getParam(I)));
return TDK_Incomplete;
}
Builder.Append(Deduced[I]);
}
// Form the template argument list from the deduced template arguments.
TemplateArgumentList *DeducedArgumentList
= new (Context) TemplateArgumentList(Context, Builder, /*TakeArgs=*/true);
Info.reset(DeducedArgumentList);
// Template argument deduction for function templates in a SFINAE context.
// Trap any errors that might occur.
SFINAETrap Trap(*this);
// Enter a new template instantiation context while we instantiate the
// actual function declaration.
InstantiatingTemplate Inst(*this, FunctionTemplate->getLocation(),
FunctionTemplate, Deduced.data(), Deduced.size(),
ActiveTemplateInstantiation::DeducedTemplateArgumentSubstitution);
if (Inst)
return TDK_InstantiationDepth;
// Substitute the deduced template arguments into the function template
// declaration to produce the function template specialization.
Specialization = cast_or_null<FunctionDecl>(
InstantiateDecl(FunctionTemplate->getTemplatedDecl(),
FunctionTemplate->getDeclContext(),
*DeducedArgumentList));
if (!Specialization)
return TDK_SubstitutionFailure;
// If the template argument list is owned by the function template
// specialization, release it.
if (Specialization->getTemplateSpecializationArgs() == DeducedArgumentList)
Info.take();
// There may have been an error that did not prevent us from constructing a
// declaration. Mark the declaration invalid and return with a substitution
// failure.
if (Trap.hasErrorOccurred()) {
Specialization->setInvalidDecl(true);
return TDK_SubstitutionFailure;
}
return TDK_Success;
}
/// \brief Perform template argument deduction from a function call
/// (C++ [temp.deduct.call]).
///
@ -1011,10 +1224,6 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate,
CheckArgs = Function->getNumParams();
}
// Template argument deduction for function templates in a SFINAE context.
// Trap any errors that might occur.
SFINAETrap Trap(*this);
// The types of the parameters from which we will perform template argument
// deduction.
TemplateParameterList *TemplateParams
@ -1022,71 +1231,22 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate,
llvm::SmallVector<TemplateArgument, 4> Deduced;
llvm::SmallVector<QualType, 4> ParamTypes;
if (NumExplicitTemplateArgs) {
// C++ [temp.arg.explicit]p3:
// Template arguments that are present shall be specified in the
// declaration order of their corresponding template-parameters. The
// template argument list shall not specify more template-arguments than
// there are corresponding template-parameters.
TemplateArgumentListBuilder Builder(TemplateParams,
NumExplicitTemplateArgs);
// Enter a new template instantiation context where we check the
// explicitly-specified template arguments against this function template,
// and then substitute them into the function parameter types.
InstantiatingTemplate Inst(*this, FunctionTemplate->getLocation(),
FunctionTemplate, Deduced.data(), Deduced.size(),
ActiveTemplateInstantiation::ExplicitTemplateArgumentSubstitution);
if (Inst)
return TDK_InstantiationDepth;
if (CheckTemplateArgumentList(FunctionTemplate,
SourceLocation(), SourceLocation(),
ExplicitTemplateArgs,
NumExplicitTemplateArgs,
SourceLocation(),
true,
Builder) || Trap.hasErrorOccurred())
return TDK_InvalidExplicitArguments;
// Form the template argument list from the explicitly-specified
// template arguments.
TemplateArgumentList *ExplicitArgumentList
= new (Context) TemplateArgumentList(Context, Builder, /*TakeArgs=*/true);
Info.reset(ExplicitArgumentList);
// Instantiate the types of each of the function parameters given the
// explicitly-specified template arguments.
for (FunctionDecl::param_iterator P = Function->param_begin(),
PEnd = Function->param_end();
P != PEnd;
++P) {
QualType ParamType = InstantiateType((*P)->getType(),
*ExplicitArgumentList,
(*P)->getLocation(),
(*P)->getDeclName());
if (ParamType.isNull() || Trap.hasErrorOccurred())
return TDK_SubstitutionFailure;
ParamTypes.push_back(ParamType);
}
// C++ [temp.arg.explicit]p2:
// Trailing template arguments that can be deduced (14.8.2) may be
// omitted from the list of explicit template- arguments. If all of the
// template arguments can be deduced, they may all be omitted; in this
// case, the empty template argument list <> itself may also be omitted.
//
// Take all of the explicitly-specified arguments and put them into the
// set of deduced template arguments.
Deduced.reserve(TemplateParams->size());
for (unsigned I = 0, N = ExplicitArgumentList->size(); I != N; ++I)
Deduced.push_back(ExplicitArgumentList->get(I));
TemplateDeductionResult Result =
SubstituteExplicitTemplateArguments(FunctionTemplate,
ExplicitTemplateArgs,
NumExplicitTemplateArgs,
Deduced,
ParamTypes,
0,
Info);
if (Result)
return Result;
} else {
// Just fill in the parameter types from the function declaration.
for (unsigned I = 0; I != CheckArgs; ++I)
ParamTypes.push_back(Function->getParamDecl(I)->getType());
}
// Deduce template arguments from the function parameters.
Deduced.resize(TemplateParams->size());
for (unsigned I = 0; I != CheckArgs; ++I) {
@ -1173,65 +1333,81 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate,
// pointer parameters.
}
// C++ [temp.deduct.type]p2:
// [...] or if any template argument remains neither deduced nor
// explicitly specified, template argument deduction fails.
TemplateArgumentListBuilder Builder(TemplateParams, Deduced.size());
for (unsigned I = 0, N = Deduced.size(); I != N; ++I) {
if (Deduced[I].isNull()) {
Decl *Param
= const_cast<Decl *>(TemplateParams->getParam(I));
if (TemplateTypeParmDecl *TTP = dyn_cast<TemplateTypeParmDecl>(Param))
Info.Param = TTP;
else if (NonTypeTemplateParmDecl *NTTP
= dyn_cast<NonTypeTemplateParmDecl>(Param))
Info.Param = NTTP;
else
Info.Param = cast<TemplateTemplateParmDecl>(Param);
return TDK_Incomplete;
}
Builder.Append(Deduced[I]);
}
// Form the template argument list from the deduced template arguments.
TemplateArgumentList *DeducedArgumentList
= new (Context) TemplateArgumentList(Context, Builder, /*TakeArgs=*/true);
Info.reset(DeducedArgumentList);
// Enter a new template instantiation context while we instantiate the
// actual function declaration.
InstantiatingTemplate Inst(*this, FunctionTemplate->getLocation(),
FunctionTemplate, Deduced.data(), Deduced.size(),
ActiveTemplateInstantiation::DeducedTemplateArgumentSubstitution);
if (Inst)
return TDK_InstantiationDepth;
// Substitute the deduced template arguments into the function template
// declaration to produce the function template specialization.
Specialization = cast_or_null<FunctionDecl>(
InstantiateDecl(FunctionTemplate->getTemplatedDecl(),
FunctionTemplate->getDeclContext(),
*DeducedArgumentList));
if (!Specialization)
return TDK_SubstitutionFailure;
// If the template argument list is owned by the function template
// specialization, release it.
if (Specialization->getTemplateSpecializationArgs() == DeducedArgumentList)
Info.take();
// There may have been an error that did not prevent us from constructing a
// declaration. Mark the declaration invalid and return with a substitution
// failure.
if (Trap.hasErrorOccurred()) {
Specialization->setInvalidDecl(true);
return TDK_SubstitutionFailure;
}
return TDK_Success;
return FinishTemplateArgumentDeduction(FunctionTemplate, Deduced,
Specialization, Info);
}
/// \brief Deduce template arguments when taking the address of a function
/// template (C++ [temp.deduct.funcaddr]).
///
/// \param FunctionTemplate the function template for which we are performing
/// template argument deduction.
///
/// \param HasExplicitTemplateArgs whether any template arguments were
/// explicitly specified.
///
/// \param ExplicitTemplateArguments when @p HasExplicitTemplateArgs is true,
/// the explicitly-specified template arguments.
///
/// \param NumExplicitTemplateArguments when @p HasExplicitTemplateArgs is true,
/// the number of explicitly-specified template arguments in
/// @p ExplicitTemplateArguments. This value may be zero.
///
/// \param ArgFunctionType the function type that will be used as the
/// "argument" type (A) when performing template argument deduction from the
/// function template's function type.
///
/// \param Specialization if template argument deduction was successful,
/// this will be set to the function template specialization produced by
/// template argument deduction.
///
/// \param Info the argument will be updated to provide additional information
/// about template argument deduction.
///
/// \returns the result of template argument deduction.
Sema::TemplateDeductionResult
Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate,
bool HasExplicitTemplateArgs,
const TemplateArgument *ExplicitTemplateArgs,
unsigned NumExplicitTemplateArgs,
QualType ArgFunctionType,
FunctionDecl *&Specialization,
TemplateDeductionInfo &Info) {
FunctionDecl *Function = FunctionTemplate->getTemplatedDecl();
TemplateParameterList *TemplateParams
= FunctionTemplate->getTemplateParameters();
QualType FunctionType = Function->getType();
// Substitute any explicit template arguments.
llvm::SmallVector<TemplateArgument, 4> Deduced;
llvm::SmallVector<QualType, 4> ParamTypes;
if (HasExplicitTemplateArgs) {
if (TemplateDeductionResult Result
= SubstituteExplicitTemplateArguments(FunctionTemplate,
ExplicitTemplateArgs,
NumExplicitTemplateArgs,
Deduced, ParamTypes,
&FunctionType, Info))
return Result;
}
// Template argument deduction for function templates in a SFINAE context.
// Trap any errors that might occur.
SFINAETrap Trap(*this);
// Deduce template arguments from the function type.
Deduced.resize(TemplateParams->size());
if (TemplateDeductionResult Result
= ::DeduceTemplateArguments(Context, TemplateParams,
FunctionType, ArgFunctionType, Info,
Deduced, 0))
return Result;
return FinishTemplateArgumentDeduction(FunctionTemplate, Deduced,
Specialization, Info);
}
static void
MarkDeducedTemplateParameters(Sema &SemaRef,
const TemplateArgument &TemplateArg,

View File

@ -0,0 +1,22 @@
// RUN: clang-cc -fsyntax-only %s
template<typename T>
T f0(T, int);
void test_f0() {
int (*f0a)(int, int) = f0;
int (*f0b)(int, int) = &f0;
float (*f0c)(float, int) = &f0;
}
template<typename T> T f1(T, int);
template<typename T> T f1(T);
void test_f1() {
float (*f1a)(float, int) = f1;
float (*f1b)(float, int) = &f1;
float (*f1c)(float) = f1;
float (*f1d)(float) = (f1);
float (*f1e)(float) = &f1;
float (*f1f)(float) = (&f1);
}