forked from OSchip/llvm-project
PR22435: Correctly implement tiebreaker for reference ordering in function
template partial ordering rules. This rule applies per pair of types being compared, not per pair of function templates being compared. llvm-svn: 229965
This commit is contained in:
parent
4041f2217b
commit
ed563c27fe
|
@ -91,30 +91,6 @@ DeduceTemplateArguments(Sema &S,
|
|||
TemplateDeductionInfo &Info,
|
||||
SmallVectorImpl<DeducedTemplateArgument> &Deduced);
|
||||
|
||||
/// \brief Whether template argument deduction for two reference parameters
|
||||
/// resulted in the argument type, parameter type, or neither type being more
|
||||
/// qualified than the other.
|
||||
enum DeductionQualifierComparison {
|
||||
NeitherMoreQualified = 0,
|
||||
ParamMoreQualified,
|
||||
ArgMoreQualified
|
||||
};
|
||||
|
||||
/// \brief Stores the result of comparing two reference parameters while
|
||||
/// performing template argument deduction for partial ordering of function
|
||||
/// templates.
|
||||
struct RefParamPartialOrderingComparison {
|
||||
/// \brief Whether the parameter type is an rvalue reference type.
|
||||
bool ParamIsRvalueRef;
|
||||
/// \brief Whether the argument type is an rvalue reference type.
|
||||
bool ArgIsRvalueRef;
|
||||
|
||||
/// \brief Whether the parameter or argument (or neither) is more qualified.
|
||||
DeductionQualifierComparison Qualifiers;
|
||||
};
|
||||
|
||||
|
||||
|
||||
static Sema::TemplateDeductionResult
|
||||
DeduceTemplateArgumentsByTypeMatch(Sema &S,
|
||||
TemplateParameterList *TemplateParams,
|
||||
|
@ -124,9 +100,7 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S,
|
|||
SmallVectorImpl<DeducedTemplateArgument> &
|
||||
Deduced,
|
||||
unsigned TDF,
|
||||
bool PartialOrdering = false,
|
||||
SmallVectorImpl<RefParamPartialOrderingComparison> *
|
||||
RefParamComparisons = nullptr);
|
||||
bool PartialOrdering = false);
|
||||
|
||||
static Sema::TemplateDeductionResult
|
||||
DeduceTemplateArguments(Sema &S,
|
||||
|
@ -784,9 +758,6 @@ private:
|
|||
/// deduction for during partial ordering for a call
|
||||
/// (C++0x [temp.deduct.partial]).
|
||||
///
|
||||
/// \param RefParamComparisons If we're performing template argument deduction
|
||||
/// in the context of partial ordering, the set of qualifier comparisons.
|
||||
///
|
||||
/// \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.
|
||||
|
@ -798,9 +769,7 @@ DeduceTemplateArguments(Sema &S,
|
|||
TemplateDeductionInfo &Info,
|
||||
SmallVectorImpl<DeducedTemplateArgument> &Deduced,
|
||||
unsigned TDF,
|
||||
bool PartialOrdering = false,
|
||||
SmallVectorImpl<RefParamPartialOrderingComparison> *
|
||||
RefParamComparisons = nullptr) {
|
||||
bool PartialOrdering = false) {
|
||||
// Fast-path check to see if we have too many/too few arguments.
|
||||
if (NumParams != NumArgs &&
|
||||
!(NumParams && isa<PackExpansionType>(Params[NumParams - 1])) &&
|
||||
|
@ -836,8 +805,7 @@ DeduceTemplateArguments(Sema &S,
|
|||
= DeduceTemplateArgumentsByTypeMatch(S, TemplateParams,
|
||||
Params[ParamIdx], Args[ArgIdx],
|
||||
Info, Deduced, TDF,
|
||||
PartialOrdering,
|
||||
RefParamComparisons))
|
||||
PartialOrdering))
|
||||
return Result;
|
||||
|
||||
++ArgIdx;
|
||||
|
@ -869,8 +837,7 @@ DeduceTemplateArguments(Sema &S,
|
|||
if (Sema::TemplateDeductionResult Result
|
||||
= DeduceTemplateArgumentsByTypeMatch(S, TemplateParams, Pattern,
|
||||
Args[ArgIdx], Info, Deduced,
|
||||
TDF, PartialOrdering,
|
||||
RefParamComparisons))
|
||||
TDF, PartialOrdering))
|
||||
return Result;
|
||||
|
||||
PackScope.nextPackElement();
|
||||
|
@ -967,9 +934,6 @@ bool Sema::isSameOrCompatibleFunctionType(CanQualType Param,
|
|||
/// \param PartialOrdering Whether we're performing template argument deduction
|
||||
/// in the context of partial ordering (C++0x [temp.deduct.partial]).
|
||||
///
|
||||
/// \param RefParamComparisons If we're performing template argument deduction
|
||||
/// in the context of partial ordering, the set of qualifier comparisons.
|
||||
///
|
||||
/// \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.
|
||||
|
@ -980,9 +944,7 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S,
|
|||
TemplateDeductionInfo &Info,
|
||||
SmallVectorImpl<DeducedTemplateArgument> &Deduced,
|
||||
unsigned TDF,
|
||||
bool PartialOrdering,
|
||||
SmallVectorImpl<RefParamPartialOrderingComparison> *
|
||||
RefParamComparisons) {
|
||||
bool PartialOrdering) {
|
||||
// We only want to look at the canonical types, since typedefs and
|
||||
// sugar are not part of template argument deduction.
|
||||
QualType Param = S.Context.getCanonicalType(ParamIn);
|
||||
|
@ -995,7 +957,7 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S,
|
|||
Arg = ArgExpansion->getPattern();
|
||||
|
||||
if (PartialOrdering) {
|
||||
// C++0x [temp.deduct.partial]p5:
|
||||
// C++11 [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.
|
||||
|
@ -1008,42 +970,42 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S,
|
|||
if (ArgRef)
|
||||
Arg = ArgRef->getPointeeType();
|
||||
|
||||
if (RefParamComparisons && 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.
|
||||
if (ParamRef && ArgRef && S.Context.hasSameUnqualifiedType(Param, Arg)) {
|
||||
// C++11 [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 both
|
||||
// P and A were reference types [...]:
|
||||
// - if [one type] was an lvalue reference and [the other type] was
|
||||
// not, [the other type] is not considered to be at least as
|
||||
// specialized as [the first type]
|
||||
// - if [one type] is more cv-qualified than [the other type],
|
||||
// [the other type] is not considered to be at least as specialized
|
||||
// as [the first type]
|
||||
// Objective-C ARC adds:
|
||||
// - [one type] has non-trivial lifetime, [the other type] has
|
||||
// __unsafe_unretained lifetime, and the types are otherwise
|
||||
// identical
|
||||
//
|
||||
// We save this information for later, using it only when deduction
|
||||
// succeeds in both directions.
|
||||
RefParamPartialOrderingComparison Comparison;
|
||||
Comparison.ParamIsRvalueRef = ParamRef->getAs<RValueReferenceType>();
|
||||
Comparison.ArgIsRvalueRef = ArgRef->getAs<RValueReferenceType>();
|
||||
Comparison.Qualifiers = NeitherMoreQualified;
|
||||
|
||||
// A is "considered to be at least as specialized" as P iff deduction
|
||||
// succeeds, so we model this as a deduction failure. Note that
|
||||
// [the first type] is P and [the other type] is A here; the standard
|
||||
// gets this backwards.
|
||||
Qualifiers ParamQuals = Param.getQualifiers();
|
||||
Qualifiers ArgQuals = Arg.getQualifiers();
|
||||
if (ParamQuals.isStrictSupersetOf(ArgQuals))
|
||||
Comparison.Qualifiers = ParamMoreQualified;
|
||||
else if (ArgQuals.isStrictSupersetOf(ParamQuals))
|
||||
Comparison.Qualifiers = ArgMoreQualified;
|
||||
else if (ArgQuals.getObjCLifetime() != ParamQuals.getObjCLifetime() &&
|
||||
ArgQuals.withoutObjCLifetime()
|
||||
== ParamQuals.withoutObjCLifetime()) {
|
||||
// Prefer binding to non-__unsafe_autoretained parameters.
|
||||
if (ArgQuals.getObjCLifetime() == Qualifiers::OCL_ExplicitNone &&
|
||||
ParamQuals.getObjCLifetime())
|
||||
Comparison.Qualifiers = ParamMoreQualified;
|
||||
else if (ParamQuals.getObjCLifetime() == Qualifiers::OCL_ExplicitNone &&
|
||||
ArgQuals.getObjCLifetime())
|
||||
Comparison.Qualifiers = ArgMoreQualified;
|
||||
if ((ParamRef->isLValueReferenceType() &&
|
||||
!ArgRef->isLValueReferenceType()) ||
|
||||
ParamQuals.isStrictSupersetOf(ArgQuals) ||
|
||||
(ParamQuals.hasNonTrivialObjCLifetime() &&
|
||||
ArgQuals.getObjCLifetime() == Qualifiers::OCL_ExplicitNone &&
|
||||
ParamQuals.withoutObjCLifetime() ==
|
||||
ArgQuals.withoutObjCLifetime())) {
|
||||
Info.FirstArg = TemplateArgument(ParamIn);
|
||||
Info.SecondArg = TemplateArgument(ArgIn);
|
||||
return Sema::TDK_NonDeducedMismatch;
|
||||
}
|
||||
RefParamComparisons->push_back(Comparison);
|
||||
}
|
||||
|
||||
// C++0x [temp.deduct.partial]p7:
|
||||
// C++11 [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.
|
||||
|
@ -4146,8 +4108,7 @@ static bool isAtLeastAsSpecializedAs(Sema &S,
|
|||
FunctionTemplateDecl *FT1,
|
||||
FunctionTemplateDecl *FT2,
|
||||
TemplatePartialOrderingContext TPOC,
|
||||
unsigned NumCallArguments1,
|
||||
SmallVectorImpl<RefParamPartialOrderingComparison> *RefParamComparisons) {
|
||||
unsigned NumCallArguments1) {
|
||||
FunctionDecl *FD1 = FT1->getTemplatedDecl();
|
||||
FunctionDecl *FD2 = FT2->getTemplatedDecl();
|
||||
const FunctionProtoType *Proto1 = FD1->getType()->getAs<FunctionProtoType>();
|
||||
|
@ -4212,8 +4173,7 @@ static bool isAtLeastAsSpecializedAs(Sema &S,
|
|||
Args2.resize(NumComparedArguments);
|
||||
if (DeduceTemplateArguments(S, TemplateParams, Args2.data(), Args2.size(),
|
||||
Args1.data(), Args1.size(), Info, Deduced,
|
||||
TDF_None, /*PartialOrdering=*/true,
|
||||
RefParamComparisons))
|
||||
TDF_None, /*PartialOrdering=*/true))
|
||||
return false;
|
||||
|
||||
break;
|
||||
|
@ -4225,7 +4185,7 @@ static bool isAtLeastAsSpecializedAs(Sema &S,
|
|||
if (DeduceTemplateArgumentsByTypeMatch(
|
||||
S, TemplateParams, Proto2->getReturnType(), Proto1->getReturnType(),
|
||||
Info, Deduced, TDF_None,
|
||||
/*PartialOrdering=*/true, RefParamComparisons))
|
||||
/*PartialOrdering=*/true))
|
||||
return false;
|
||||
break;
|
||||
|
||||
|
@ -4235,8 +4195,7 @@ static bool isAtLeastAsSpecializedAs(Sema &S,
|
|||
if (DeduceTemplateArgumentsByTypeMatch(S, TemplateParams,
|
||||
FD2->getType(), FD1->getType(),
|
||||
Info, Deduced, TDF_None,
|
||||
/*PartialOrdering=*/true,
|
||||
RefParamComparisons))
|
||||
/*PartialOrdering=*/true))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
@ -4335,83 +4294,17 @@ Sema::getMoreSpecializedTemplate(FunctionTemplateDecl *FT1,
|
|||
TemplatePartialOrderingContext TPOC,
|
||||
unsigned NumCallArguments1,
|
||||
unsigned NumCallArguments2) {
|
||||
SmallVector<RefParamPartialOrderingComparison, 4> RefParamComparisons;
|
||||
bool Better1 = isAtLeastAsSpecializedAs(*this, Loc, FT1, FT2, TPOC,
|
||||
NumCallArguments1, nullptr);
|
||||
NumCallArguments1);
|
||||
bool Better2 = isAtLeastAsSpecializedAs(*this, Loc, FT2, FT1, TPOC,
|
||||
NumCallArguments2,
|
||||
&RefParamComparisons);
|
||||
NumCallArguments2);
|
||||
|
||||
if (Better1 != Better2) // We have a clear winner
|
||||
return Better1? FT1 : FT2;
|
||||
return Better1 ? FT1 : FT2;
|
||||
|
||||
if (!Better1 && !Better2) // Neither is better than the other
|
||||
return nullptr;
|
||||
|
||||
// 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 = RefParamComparisons.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 both P and A
|
||||
// were reference types (before being replaced with the type referred to
|
||||
// above):
|
||||
|
||||
// -- if the type from the argument template was an lvalue reference
|
||||
// and the type from the parameter template was not, the argument
|
||||
// type is considered to be more specialized than the other;
|
||||
// otherwise,
|
||||
if (!RefParamComparisons[I].ArgIsRvalueRef &&
|
||||
RefParamComparisons[I].ParamIsRvalueRef) {
|
||||
Better2 = true;
|
||||
if (Better1)
|
||||
return nullptr;
|
||||
continue;
|
||||
} else if (!RefParamComparisons[I].ParamIsRvalueRef &&
|
||||
RefParamComparisons[I].ArgIsRvalueRef) {
|
||||
Better1 = true;
|
||||
if (Better2)
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
// -- if the type from the argument template is more cv-qualified than
|
||||
// the type from the parameter template (as described above), the
|
||||
// argument type is considered to be more specialized than the
|
||||
// other; otherwise,
|
||||
switch (RefParamComparisons[I].Qualifiers) {
|
||||
case NeitherMoreQualified:
|
||||
break;
|
||||
|
||||
case ParamMoreQualified:
|
||||
Better1 = true;
|
||||
if (Better2)
|
||||
return nullptr;
|
||||
continue;
|
||||
|
||||
case ArgMoreQualified:
|
||||
Better2 = true;
|
||||
if (Better1)
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
// -- neither type is more specialized than the other.
|
||||
}
|
||||
|
||||
assert(!(Better1 && Better2) && "Should have broken out in the loop above");
|
||||
if (Better1)
|
||||
return FT1;
|
||||
else if (Better2)
|
||||
return FT2;
|
||||
|
||||
// FIXME: This mimics what GCC implements, but doesn't match up with the
|
||||
// proposed resolution for core issue 692. This area needs to be sorted out,
|
||||
// but for now we attempt to maintain compatibility.
|
||||
|
@ -4584,8 +4477,7 @@ Sema::getMoreSpecializedPartialSpecialization(
|
|||
bool Better1 = !DeduceTemplateArgumentsByTypeMatch(*this,
|
||||
PS2->getTemplateParameters(),
|
||||
PT2, PT1, Info, Deduced, TDF_None,
|
||||
/*PartialOrdering=*/true,
|
||||
/*RefParamComparisons=*/nullptr);
|
||||
/*PartialOrdering=*/true);
|
||||
if (Better1) {
|
||||
SmallVector<TemplateArgument, 4> DeducedArgs(Deduced.begin(),Deduced.end());
|
||||
InstantiatingTemplate Inst(*this, Loc, PS2, DeducedArgs, Info);
|
||||
|
@ -4598,8 +4490,7 @@ Sema::getMoreSpecializedPartialSpecialization(
|
|||
Deduced.resize(PS1->getTemplateParameters()->size());
|
||||
bool Better2 = !DeduceTemplateArgumentsByTypeMatch(
|
||||
*this, PS1->getTemplateParameters(), PT1, PT2, Info, Deduced, TDF_None,
|
||||
/*PartialOrdering=*/true,
|
||||
/*RefParamComparisons=*/nullptr);
|
||||
/*PartialOrdering=*/true);
|
||||
if (Better2) {
|
||||
SmallVector<TemplateArgument, 4> DeducedArgs(Deduced.begin(),
|
||||
Deduced.end());
|
||||
|
@ -4642,8 +4533,7 @@ Sema::getMoreSpecializedPartialSpecialization(
|
|||
Deduced.resize(PS2->getTemplateParameters()->size());
|
||||
bool Better1 = !DeduceTemplateArgumentsByTypeMatch(
|
||||
*this, PS2->getTemplateParameters(), PT2, PT1, Info, Deduced, TDF_None,
|
||||
/*PartialOrdering=*/true,
|
||||
/*RefParamComparisons=*/nullptr);
|
||||
/*PartialOrdering=*/true);
|
||||
if (Better1) {
|
||||
SmallVector<TemplateArgument, 4> DeducedArgs(Deduced.begin(),
|
||||
Deduced.end());
|
||||
|
@ -4659,8 +4549,7 @@ Sema::getMoreSpecializedPartialSpecialization(
|
|||
bool Better2 = !DeduceTemplateArgumentsByTypeMatch(*this,
|
||||
PS1->getTemplateParameters(),
|
||||
PT1, PT2, Info, Deduced, TDF_None,
|
||||
/*PartialOrdering=*/true,
|
||||
/*RefParamComparisons=*/nullptr);
|
||||
/*PartialOrdering=*/true);
|
||||
if (Better2) {
|
||||
SmallVector<TemplateArgument, 4> DeducedArgs(Deduced.begin(),Deduced.end());
|
||||
InstantiatingTemplate Inst(*this, Loc, PS1, DeducedArgs, Info);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only -verify %s
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 %s
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
|
||||
|
||||
template<class T> struct A { A(); };
|
||||
template<class T> int &f(T);
|
||||
|
@ -21,3 +22,23 @@ void m() {
|
|||
const A<int> z2;
|
||||
int &ir1 = h(z2);
|
||||
}
|
||||
|
||||
|
||||
namespace core_26909 {
|
||||
template<typename T> struct A {};
|
||||
template<typename T, typename U> void f(T&, U); // expected-note {{candidate}}
|
||||
template<typename T, typename U> void f(T&&, A<U>); // expected-note {{candidate}} expected-warning 0-1{{extension}}
|
||||
template<typename T, typename U> void g(const T&, U); // expected-note {{candidate}}
|
||||
template<typename T, typename U> void g(T&, A<U>); // expected-note {{candidate}}
|
||||
|
||||
void h(int a, const char b, A<int> c) {
|
||||
f(a, c); // expected-error{{ambiguous}}
|
||||
g(b, c); // expected-error{{ambiguous}}
|
||||
}
|
||||
}
|
||||
|
||||
namespace PR22435 {
|
||||
template<typename T, typename U> void foo(void (*)(T), const U &); // expected-note {{candidate}}
|
||||
template<typename T, typename U> bool foo(void (*)(T &), U &); // expected-note {{candidate}}
|
||||
void bar(const int x) { bool b = foo<char>(0, x); } // expected-error {{ambiguous}}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue