Implement partial ordering of function template specializations
(C++ [temp.func.order]). llvm-svn: 81777
This commit is contained in:
parent
80cf843fe9
commit
0ff7d92048
|
@ -959,10 +959,6 @@ def unsup_template_partial_spec_ordering : Error<
|
|||
"partial ordering of class template partial specializations is not yet "
|
||||
"supported">;
|
||||
|
||||
def unsup_function_template_partial_ordering : Warning<
|
||||
"partial ordering of function templates is unsupported; overload resolution "
|
||||
"may result in an ambiguity that would not occur with a conforming compiler">;
|
||||
|
||||
// C++ Template Instantiation
|
||||
def err_template_recursion_depth_exceeded : Error<
|
||||
"recursive template instantiation exceeded maximum depth of %0">,
|
||||
|
|
|
@ -2730,7 +2730,7 @@ public:
|
|||
|
||||
FunctionTemplateDecl *getMoreSpecializedTemplate(FunctionTemplateDecl *FT1,
|
||||
FunctionTemplateDecl *FT2,
|
||||
bool isCallContext);
|
||||
TemplatePartialOrderingContext TPOC);
|
||||
|
||||
void MarkDeducedTemplateParameters(const TemplateArgumentList &TemplateArgs,
|
||||
llvm::SmallVectorImpl<bool> &Deduced);
|
||||
|
|
|
@ -3708,7 +3708,7 @@ Sema::isBetterOverloadCandidate(const OverloadCandidate& Cand1,
|
|||
if (FunctionTemplateDecl *BetterTemplate
|
||||
= getMoreSpecializedTemplate(Cand1.Function->getPrimaryTemplate(),
|
||||
Cand2.Function->getPrimaryTemplate(),
|
||||
true))
|
||||
TPOC_Call))
|
||||
return BetterTemplate == Cand1.Function->getPrimaryTemplate();
|
||||
|
||||
// -- the context is an initialization by user-defined conversion
|
||||
|
@ -4029,7 +4029,7 @@ Sema::ResolveAddressOfOverloadedFunction(Expr *From, QualType ToType,
|
|||
for (++M; M != MEnd; ++M)
|
||||
if (getMoreSpecializedTemplate((*M)->getPrimaryTemplate(),
|
||||
(*Best)->getPrimaryTemplate(),
|
||||
false)
|
||||
TPOC_Other)
|
||||
== (*M)->getPrimaryTemplate())
|
||||
Best = M;
|
||||
|
||||
|
@ -4040,7 +4040,7 @@ Sema::ResolveAddressOfOverloadedFunction(Expr *From, QualType ToType,
|
|||
if (M != Best &&
|
||||
getMoreSpecializedTemplate((*M)->getPrimaryTemplate(),
|
||||
(*Best)->getPrimaryTemplate(),
|
||||
false)
|
||||
TPOC_Other)
|
||||
!= (*Best)->getPrimaryTemplate()) {
|
||||
Ambiguous = true;
|
||||
break;
|
||||
|
|
|
@ -86,6 +86,19 @@ namespace clang {
|
|||
return *TemplateArgumentLists.front();
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief The context in which partial ordering of function templates occurs.
|
||||
enum TemplatePartialOrderingContext {
|
||||
/// \brief Partial ordering of function templates for a function call.
|
||||
TPOC_Call,
|
||||
/// \brief Partial ordering of function templates for a call to a
|
||||
/// conversion function.
|
||||
TPOC_Conversion,
|
||||
/// \brief Partial ordering of function templates in other contexts, e.g.,
|
||||
/// taking the address of a function template or matching a function
|
||||
/// template specialization to a function template.
|
||||
TPOC_Other
|
||||
};
|
||||
}
|
||||
|
||||
#endif // LLVM_CLANG_SEMA_TEMPLATE_H
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "clang/AST/ExprCXX.h"
|
||||
#include "clang/Parse/DeclSpec.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace clang {
|
||||
/// \brief Various flags that control template argument deduction.
|
||||
|
@ -1581,6 +1582,209 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate,
|
|||
return Result;
|
||||
}
|
||||
|
||||
/// \brief Stores the result of comparing the qualifiers of two types.
|
||||
enum DeductionQualifierComparison {
|
||||
NeitherMoreQualified = 0,
|
||||
ParamMoreQualified,
|
||||
ArgMoreQualified
|
||||
};
|
||||
|
||||
/// \brief Deduce the template arguments during partial ordering by comparing
|
||||
/// the parameter type and the argument type (C++0x [temp.deduct.partial]).
|
||||
///
|
||||
/// \param Context the AST context in which this deduction occurs.
|
||||
///
|
||||
/// \param TemplateParams the template parameters that we are deducing
|
||||
///
|
||||
/// \param ParamIn the parameter type
|
||||
///
|
||||
/// \param ArgIn the argument type
|
||||
///
|
||||
/// \param Info information about the template argument deduction itself
|
||||
///
|
||||
/// \param Deduced the deduced template arguments
|
||||
///
|
||||
/// \returns the result of template argument deduction so far. Note that a
|
||||
/// "success" result means that template argument deduction has not yet failed,
|
||||
/// but it may still fail, later, for other reasons.
|
||||
static Sema::TemplateDeductionResult
|
||||
DeduceTemplateArgumentsDuringPartialOrdering(ASTContext &Context,
|
||||
TemplateParameterList *TemplateParams,
|
||||
QualType ParamIn, QualType ArgIn,
|
||||
Sema::TemplateDeductionInfo &Info,
|
||||
llvm::SmallVectorImpl<TemplateArgument> &Deduced,
|
||||
llvm::SmallVectorImpl<DeductionQualifierComparison> *QualifierComparisons) {
|
||||
CanQualType Param = Context.getCanonicalType(ParamIn);
|
||||
CanQualType Arg = Context.getCanonicalType(ArgIn);
|
||||
|
||||
// C++0x [temp.deduct.partial]p5:
|
||||
// Before the partial ordering is done, certain transformations are
|
||||
// performed on the types used for partial ordering:
|
||||
// - If P is a reference type, P is replaced by the type referred to.
|
||||
CanQual<ReferenceType> ParamRef = Param->getAs<ReferenceType>();
|
||||
if (ParamRef)
|
||||
Param = ParamRef->getPointeeType();
|
||||
|
||||
// - If A is a reference type, A is replaced by the type referred to.
|
||||
CanQual<ReferenceType> ArgRef = Arg->getAs<ReferenceType>();
|
||||
if (ArgRef)
|
||||
Arg = ArgRef->getPointeeType();
|
||||
|
||||
if (QualifierComparisons && ParamRef && ArgRef) {
|
||||
// C++0x [temp.deduct.partial]p6:
|
||||
// If both P and A were reference types (before being replaced with the
|
||||
// type referred to above), determine which of the two types (if any) is
|
||||
// more cv-qualified than the other; otherwise the types are considered to
|
||||
// be equally cv-qualified for partial ordering purposes. The result of this
|
||||
// determination will be used below.
|
||||
//
|
||||
// We save this information for later, using it only when deduction
|
||||
// succeeds in both directions.
|
||||
DeductionQualifierComparison QualifierResult = NeitherMoreQualified;
|
||||
if (Param.isMoreQualifiedThan(Arg))
|
||||
QualifierResult = ParamMoreQualified;
|
||||
else if (Arg.isMoreQualifiedThan(Param))
|
||||
QualifierResult = ArgMoreQualified;
|
||||
QualifierComparisons->push_back(QualifierResult);
|
||||
}
|
||||
|
||||
// C++0x [temp.deduct.partial]p7:
|
||||
// Remove any top-level cv-qualifiers:
|
||||
// - If P is a cv-qualified type, P is replaced by the cv-unqualified
|
||||
// version of P.
|
||||
Param = Param.getUnqualifiedType();
|
||||
// - If A is a cv-qualified type, A is replaced by the cv-unqualified
|
||||
// version of A.
|
||||
Arg = Arg.getUnqualifiedType();
|
||||
|
||||
// C++0x [temp.deduct.partial]p8:
|
||||
// Using the resulting types P and A the deduction is then done as
|
||||
// described in 14.9.2.5. If deduction succeeds for a given type, the type
|
||||
// from the argument template is considered to be at least as specialized
|
||||
// as the type from the parameter template.
|
||||
return DeduceTemplateArguments(Context, TemplateParams, Param, Arg, Info,
|
||||
Deduced, TDF_None);
|
||||
}
|
||||
|
||||
static void
|
||||
MarkDeducedTemplateParameters(Sema &SemaRef, QualType T,
|
||||
llvm::SmallVectorImpl<bool> &Deduced);
|
||||
|
||||
/// \brief Determine whether the function template \p FT1 is at least as
|
||||
/// specialized as \p FT2.
|
||||
static bool isAtLeastAsSpecializedAs(Sema &S,
|
||||
FunctionTemplateDecl *FT1,
|
||||
FunctionTemplateDecl *FT2,
|
||||
TemplatePartialOrderingContext TPOC,
|
||||
llvm::SmallVectorImpl<DeductionQualifierComparison> *QualifierComparisons) {
|
||||
FunctionDecl *FD1 = FT1->getTemplatedDecl();
|
||||
FunctionDecl *FD2 = FT2->getTemplatedDecl();
|
||||
const FunctionProtoType *Proto1 = FD1->getType()->getAs<FunctionProtoType>();
|
||||
const FunctionProtoType *Proto2 = FD2->getType()->getAs<FunctionProtoType>();
|
||||
|
||||
assert(Proto1 && Proto2 && "Function templates must have prototypes");
|
||||
TemplateParameterList *TemplateParams = FT2->getTemplateParameters();
|
||||
llvm::SmallVector<TemplateArgument, 4> Deduced;
|
||||
Deduced.resize(TemplateParams->size());
|
||||
|
||||
// C++0x [temp.deduct.partial]p3:
|
||||
// The types used to determine the ordering depend on the context in which
|
||||
// the partial ordering is done:
|
||||
Sema::TemplateDeductionInfo Info(S.Context);
|
||||
switch (TPOC) {
|
||||
case TPOC_Call: {
|
||||
// - In the context of a function call, the function parameter types are
|
||||
// used.
|
||||
unsigned NumParams = std::min(Proto1->getNumArgs(), Proto2->getNumArgs());
|
||||
for (unsigned I = 0; I != NumParams; ++I)
|
||||
if (DeduceTemplateArgumentsDuringPartialOrdering(S.Context,
|
||||
TemplateParams,
|
||||
Proto2->getArgType(I),
|
||||
Proto1->getArgType(I),
|
||||
Info,
|
||||
Deduced,
|
||||
QualifierComparisons))
|
||||
return false;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case TPOC_Conversion:
|
||||
// - In the context of a call to a conversion operator, the return types
|
||||
// of the conversion function templates are used.
|
||||
if (DeduceTemplateArgumentsDuringPartialOrdering(S.Context,
|
||||
TemplateParams,
|
||||
Proto2->getResultType(),
|
||||
Proto1->getResultType(),
|
||||
Info,
|
||||
Deduced,
|
||||
QualifierComparisons))
|
||||
return false;
|
||||
break;
|
||||
|
||||
case TPOC_Other:
|
||||
// - In other contexts (14.6.6.2) the function template’s function type
|
||||
// is used.
|
||||
if (DeduceTemplateArgumentsDuringPartialOrdering(S.Context,
|
||||
TemplateParams,
|
||||
FD2->getType(),
|
||||
FD1->getType(),
|
||||
Info,
|
||||
Deduced,
|
||||
QualifierComparisons))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
// C++0x [temp.deduct.partial]p11:
|
||||
// In most cases, all template parameters must have values in order for
|
||||
// deduction to succeed, but for partial ordering purposes a template
|
||||
// parameter may remain without a value provided it is not used in the
|
||||
// types being used for partial ordering. [ Note: a template parameter used
|
||||
// in a non-deduced context is considered used. -end note]
|
||||
unsigned ArgIdx = 0, NumArgs = Deduced.size();
|
||||
for (; ArgIdx != NumArgs; ++ArgIdx)
|
||||
if (Deduced[ArgIdx].isNull())
|
||||
break;
|
||||
|
||||
if (ArgIdx == NumArgs) {
|
||||
// All template arguments were deduced. FT1 is at least as specialized
|
||||
// as FT2.
|
||||
return true;
|
||||
}
|
||||
|
||||
// FIXME: MarkDeducedTemplateParameters needs to become
|
||||
// MarkUsedTemplateParameters with a flag that tells us whether to mark
|
||||
// template parameters that are used in non-deduced contexts.
|
||||
llvm::SmallVector<bool, 4> UsedParameters;
|
||||
UsedParameters.resize(TemplateParams->size());
|
||||
switch (TPOC) {
|
||||
case TPOC_Call: {
|
||||
unsigned NumParams = std::min(Proto1->getNumArgs(), Proto2->getNumArgs());
|
||||
for (unsigned I = 0; I != NumParams; ++I)
|
||||
::MarkDeducedTemplateParameters(S, Proto2->getArgType(I), UsedParameters);
|
||||
break;
|
||||
}
|
||||
|
||||
case TPOC_Conversion:
|
||||
::MarkDeducedTemplateParameters(S, Proto2->getResultType(), UsedParameters);
|
||||
break;
|
||||
|
||||
case TPOC_Other:
|
||||
::MarkDeducedTemplateParameters(S, FD2->getType(), UsedParameters);
|
||||
break;
|
||||
}
|
||||
|
||||
for (; ArgIdx != NumArgs; ++ArgIdx)
|
||||
// If this argument had no value deduced but was used in one of the types
|
||||
// used for partial ordering, then deduction fails.
|
||||
if (Deduced[ArgIdx].isNull() && UsedParameters[ArgIdx])
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// \brief Returns the more specialization function template according
|
||||
/// to the rules of function template partial ordering (C++ [temp.func.order]).
|
||||
///
|
||||
|
@ -1588,29 +1792,70 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate,
|
|||
///
|
||||
/// \param FT2 the second function template
|
||||
///
|
||||
/// \param isCallContext whether partial ordering is being performed
|
||||
/// for a function call (which ignores the return types of the
|
||||
/// functions).
|
||||
/// \param TPOC the context in which we are performing partial ordering of
|
||||
/// function templates.
|
||||
///
|
||||
/// \returns the more specialization function template. If neither
|
||||
/// template is more specialized, returns NULL.
|
||||
FunctionTemplateDecl *
|
||||
Sema::getMoreSpecializedTemplate(FunctionTemplateDecl *FT1,
|
||||
FunctionTemplateDecl *FT2,
|
||||
bool isCallContext) {
|
||||
#if 0
|
||||
TemplatePartialOrderingContext TPOC) {
|
||||
// FIXME: Implement this
|
||||
bool Better1 = isAtLeastAsSpecializedAs(*this, FT1, FT2, isCallContext);
|
||||
bool Better2 = isAtLeastAsSpecializedAs(*this, FT2, FT1, isCallContext);
|
||||
if (Better1 == Better2)
|
||||
llvm::SmallVector<DeductionQualifierComparison, 4> QualifierComparisons;
|
||||
bool Better1 = isAtLeastAsSpecializedAs(*this, FT1, FT2, TPOC, 0);
|
||||
bool Better2 = isAtLeastAsSpecializedAs(*this, FT2, FT1, TPOC,
|
||||
&QualifierComparisons);
|
||||
|
||||
if (Better1 != Better2) // We have a clear winner
|
||||
return Better1? FT1 : FT2;
|
||||
|
||||
if (!Better1 && !Better2) // Neither is better than the other
|
||||
return 0;
|
||||
|
||||
|
||||
// C++0x [temp.deduct.partial]p10:
|
||||
// If for each type being considered a given template is at least as
|
||||
// specialized for all types and more specialized for some set of types and
|
||||
// the other template is not more specialized for any types or is not at
|
||||
// least as specialized for any types, then the given template is more
|
||||
// specialized than the other template. Otherwise, neither template is more
|
||||
// specialized than the other.
|
||||
Better1 = false;
|
||||
Better2 = false;
|
||||
for (unsigned I = 0, N = QualifierComparisons.size(); I != N; ++I) {
|
||||
// C++0x [temp.deduct.partial]p9:
|
||||
// If, for a given type, deduction succeeds in both directions (i.e., the
|
||||
// types are identical after the transformations above) and if the type
|
||||
// from the argument template is more cv-qualified than the type from the
|
||||
// parameter template (as described above) that type is considered to be
|
||||
// more specialized than the other. If neither type is more cv-qualified
|
||||
// than the other then neither type is more specialized than the other.
|
||||
switch (QualifierComparisons[I]) {
|
||||
case NeitherMoreQualified:
|
||||
break;
|
||||
|
||||
case ParamMoreQualified:
|
||||
Better1 = true;
|
||||
if (Better2)
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case ArgMoreQualified:
|
||||
Better2 = true;
|
||||
if (Better1)
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(!(Better1 && Better2) && "Should have broken out in the loop above");
|
||||
if (Better1)
|
||||
return FT1;
|
||||
return FT2;
|
||||
#else
|
||||
Diag(SourceLocation(), diag::unsup_function_template_partial_ordering);
|
||||
return 0;
|
||||
#endif
|
||||
else if (Better2)
|
||||
return FT2;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
// RUN: clang-cc -fsyntax-only -verify %s
|
||||
|
||||
template<class T> struct A { A(); };
|
||||
template<class T> int &f(T);
|
||||
template<class T> float &f(T*);
|
||||
template<class T> double &f(const T*);
|
||||
|
||||
template<class T> void g(T); // expected-note{{candidate}}
|
||||
template<class T> void g(T&); // expected-note{{candidate}}
|
||||
|
||||
template<class T> int &h(const T&);
|
||||
template<class T> float &h(A<T>&);
|
||||
|
||||
void m() {
|
||||
const int *p;
|
||||
double &dr1 = f(p);
|
||||
float x;
|
||||
g(x); // expected-error{{ambiguous}}
|
||||
A<int> z;
|
||||
float &fr1 = h(z);
|
||||
const A<int> z2;
|
||||
int &ir1 = h(z2);
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
// RUN: clang-cc -fsyntax-only -verify %s
|
||||
template<class T> int &f(T);
|
||||
template<class T> float &f(T*, int=1);
|
||||
|
||||
template<class T> int &g(T);
|
||||
template<class T> float &g(T*, ...);
|
||||
|
||||
int main() {
|
||||
int* ip;
|
||||
float &fr1 = f(ip);
|
||||
float &fr2 = g(ip);
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
// RUN: clang-cc -fsyntax-only -verify %s
|
||||
|
||||
template<typename T>
|
||||
int &f0(T);
|
||||
|
||||
template<typename T>
|
||||
float &f0(T*);
|
||||
|
||||
void test_f0(int i, int *ip) {
|
||||
int &ir = f0(i);
|
||||
float &fr = f0(ip);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
int &f1(T, U);
|
||||
|
||||
template<typename T>
|
||||
float &f1(T, T);
|
||||
|
||||
void test_f1(int i, float f) {
|
||||
int &ir = f1(i, f);
|
||||
float &fr1 = f1(i, i);
|
||||
float &fr2 = f1(f, f);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
struct A { };
|
||||
|
||||
template<typename T>
|
||||
int &f2(T);
|
||||
|
||||
template<typename T, typename U>
|
||||
float &f2(A<T, U>);
|
||||
|
||||
template<typename T>
|
||||
double &f2(A<T, T>);
|
||||
|
||||
void test_f2(int i, A<int, float> aif, A<int, int> aii) {
|
||||
int &ir = f2(i);
|
||||
float &fr = f2(aif);
|
||||
double &dr = f2(aii);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
int &f3(T*, U); // expected-note{{candidate}}
|
||||
|
||||
template<typename T, typename U>
|
||||
float &f3(T, U*); // expected-note{{candidate}}
|
||||
|
||||
void test_f3(int i, int *ip, float *fp) {
|
||||
int &ir = f3(ip, i);
|
||||
float &fr = f3(i, fp);
|
||||
f3(ip, ip); // expected-error{{ambiguous}}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
int &f4(T&);
|
||||
|
||||
template<typename T>
|
||||
float &f4(const T&);
|
||||
|
||||
void test_f4(int i, const int ic) {
|
||||
int &ir1 = f4(i);
|
||||
float &fr1 = f4(ic);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
int &f5(T&, const U&); // expected-note{{candidate}}
|
||||
|
||||
template<typename T, typename U>
|
||||
float &f5(const T&, U&); // expected-note{{candidate}}
|
||||
|
||||
void test_f5(int i, const int ic) {
|
||||
f5(i, i); // expected-error{{ambiguous}}
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
int &f6(T&, U&);
|
||||
|
||||
template<typename T, typename U>
|
||||
float &f6(const T&, U&);
|
||||
|
||||
void test_f6(int i, const int ic) {
|
||||
int &ir = f6(i, i);
|
||||
float &fr = f6(ic, ic);
|
||||
}
|
Loading…
Reference in New Issue