mirror of
https://github.com/intel/llvm.git
synced 2026-01-26 03:56:16 +08:00
[C++20] Implement P2113R0: Changes to the Partial Ordering of Constrained Functions
This implementation matches GCC behavior in that [[ https://eel.is/c++draft/temp.func.order#6.2.1 | temp.func.order p6.2.1 ]] is not implemented [1]. I reached out to the GCC author to confirm that some changes elsewhere to overload resolution are probably needed, but no solution has been developed sufficiently [3]. Most of the wordings are implemented straightforwardly. However, for [[ https://eel.is/c++draft/temp.func.order#6.2.2 | temp.func.order p6.2.2 ]] "... or if the function parameters that positionally correspond between the two templates are not of the same type", the "same type" is not very clear ([2] is a bug related to this). Here is a quick example ``` template <C T, C U> int f(T, U); template <typename T, C U> int f(U, T); int x = f(0, 0); ``` Is the `U` and `T` from different `f`s the "same type"? The answer is NO even though both `U` and `T` are deduced to be `int` in this case. The reason is that `U` and `T` are dependent types, according to [[ https://eel.is/c++draft/temp.over.link#3 | temp.over.link p3 ]], they can not be the "same type". To check if two function parameters are the "same type": * For //function template//: compare the function parameter canonical types and return type between two function templates. * For //class template/partial specialization//: by [[ https://eel.is/c++draft/temp.spec.partial.order#1.2 | temp.spec.partial.order p1.2 ]], compare the injected template arguments between two templates using hashing(TemplateArgument::Profile) is enough. [1] https://gcc.gnu.org/git/gitweb.cgi?p=gcc.git;h=57b4daf8dc4ed7b669cc70638866ddb00f5b7746 [2] https://github.com/llvm/llvm-project/issues/49308 [3] https://lists.isocpp.org/core/2020/06/index.php#msg9392 Fixes https://github.com/llvm/llvm-project/issues/54039 Fixes https://github.com/llvm/llvm-project/issues/49308 (PR49964) Reviewed By: royjacobson, #clang-language-wg, mizvekov Differential Revision: https://reviews.llvm.org/D128750
This commit is contained in:
@@ -534,6 +534,9 @@ C++20 Feature Support
|
||||
which removes the requirement for the ``typename`` keyword in certain contexts.
|
||||
- Implemented The Equality Operator You Are Looking For (`P2468 <http://wg21.link/p2468r2>`_).
|
||||
|
||||
- Implemented `P2113R0: Proposed resolution for 2019 comment CA 112 <https://wg21.link/P2113R0>`_
|
||||
([temp.func.order]p6.2.1 is not implemented, matching GCC).
|
||||
|
||||
C++2b Feature Support
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
@@ -847,6 +847,15 @@ protected:
|
||||
/// The first value in the array is the number of specializations/partial
|
||||
/// specializations that follow.
|
||||
uint32_t *LazySpecializations = nullptr;
|
||||
|
||||
/// The set of "injected" template arguments used within this
|
||||
/// template.
|
||||
///
|
||||
/// This pointer refers to the template arguments (there are as
|
||||
/// many template arguments as template parameaters) for the
|
||||
/// template, and is allocated lazily, since most templates do not
|
||||
/// require the use of this information.
|
||||
TemplateArgument *InjectedArgs = nullptr;
|
||||
};
|
||||
|
||||
/// Pointer to the common data shared by all declarations of this
|
||||
@@ -954,6 +963,14 @@ public:
|
||||
getCommonPtr()->InstantiatedFromMember.setPointer(TD);
|
||||
}
|
||||
|
||||
/// Retrieve the "injected" template arguments that correspond to the
|
||||
/// template parameters of this template.
|
||||
///
|
||||
/// Although the C++ standard has no notion of the "injected" template
|
||||
/// arguments for a template, the notion is convenient when
|
||||
/// we need to perform substitutions inside the definition of a template.
|
||||
ArrayRef<TemplateArgument> getInjectedTemplateArgs();
|
||||
|
||||
using redecl_range = redeclarable_base::redecl_range;
|
||||
using redecl_iterator = redeclarable_base::redecl_iterator;
|
||||
|
||||
@@ -998,15 +1015,6 @@ protected:
|
||||
/// template, including explicit specializations and instantiations.
|
||||
llvm::FoldingSetVector<FunctionTemplateSpecializationInfo> Specializations;
|
||||
|
||||
/// The set of "injected" template arguments used within this
|
||||
/// function template.
|
||||
///
|
||||
/// This pointer refers to the template arguments (there are as
|
||||
/// many template arguments as template parameaters) for the function
|
||||
/// template, and is allocated lazily, since most function templates do not
|
||||
/// require the use of this information.
|
||||
TemplateArgument *InjectedArgs = nullptr;
|
||||
|
||||
Common() = default;
|
||||
};
|
||||
|
||||
@@ -1106,15 +1114,6 @@ public:
|
||||
return makeSpecIterator(getSpecializations(), true);
|
||||
}
|
||||
|
||||
/// Retrieve the "injected" template arguments that correspond to the
|
||||
/// template parameters of this function template.
|
||||
///
|
||||
/// Although the C++ standard has no notion of the "injected" template
|
||||
/// arguments for a function template, the notion is convenient when
|
||||
/// we need to perform substitutions inside the definition of a function
|
||||
/// template.
|
||||
ArrayRef<TemplateArgument> getInjectedTemplateArgs();
|
||||
|
||||
/// Return whether this function template is an abbreviated function template,
|
||||
/// e.g. `void foo(auto x)` or `template<typename T> void foo(auto x)`
|
||||
bool isAbbreviated() const {
|
||||
|
||||
@@ -8329,14 +8329,17 @@ public:
|
||||
const NamedDecl *NewInstFrom, TemplateParameterList *New,
|
||||
const NamedDecl *OldInstFrom, TemplateParameterList *Old, bool Complain,
|
||||
TemplateParameterListEqualKind Kind,
|
||||
SourceLocation TemplateArgLoc = SourceLocation());
|
||||
SourceLocation TemplateArgLoc = SourceLocation(),
|
||||
bool PartialOrdering = false);
|
||||
|
||||
bool TemplateParameterListsAreEqual(
|
||||
TemplateParameterList *New, TemplateParameterList *Old, bool Complain,
|
||||
TemplateParameterListEqualKind Kind,
|
||||
SourceLocation TemplateArgLoc = SourceLocation()) {
|
||||
SourceLocation TemplateArgLoc = SourceLocation(),
|
||||
bool PartialOrdering = false) {
|
||||
return TemplateParameterListsAreEqual(nullptr, New, nullptr, Old, Complain,
|
||||
Kind, TemplateArgLoc);
|
||||
Kind, TemplateArgLoc,
|
||||
PartialOrdering);
|
||||
}
|
||||
|
||||
bool CheckTemplateDeclScope(Scope *S, TemplateParameterList *TemplateParams);
|
||||
@@ -9021,8 +9024,7 @@ public:
|
||||
FunctionTemplateDecl *getMoreSpecializedTemplate(
|
||||
FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc,
|
||||
TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1,
|
||||
unsigned NumCallArguments2, bool Reversed = false,
|
||||
bool AllowOrderingByConstraints = true);
|
||||
unsigned NumCallArguments2, bool Reversed = false);
|
||||
UnresolvedSetIterator
|
||||
getMostSpecialized(UnresolvedSetIterator SBegin, UnresolvedSetIterator SEnd,
|
||||
TemplateSpecCandidateSet &FailedCandidates,
|
||||
|
||||
@@ -353,6 +353,22 @@ void RedeclarableTemplateDecl::addSpecializationImpl(
|
||||
SETraits::getDecl(Entry));
|
||||
}
|
||||
|
||||
ArrayRef<TemplateArgument> RedeclarableTemplateDecl::getInjectedTemplateArgs() {
|
||||
TemplateParameterList *Params = getTemplateParameters();
|
||||
auto *CommonPtr = getCommonPtr();
|
||||
if (!CommonPtr->InjectedArgs) {
|
||||
auto &Context = getASTContext();
|
||||
SmallVector<TemplateArgument, 16> TemplateArgs;
|
||||
Context.getInjectedTemplateArgs(Params, TemplateArgs);
|
||||
CommonPtr->InjectedArgs =
|
||||
new (Context) TemplateArgument[TemplateArgs.size()];
|
||||
std::copy(TemplateArgs.begin(), TemplateArgs.end(),
|
||||
CommonPtr->InjectedArgs);
|
||||
}
|
||||
|
||||
return llvm::makeArrayRef(CommonPtr->InjectedArgs, Params->size());
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// FunctionTemplateDecl Implementation
|
||||
//===----------------------------------------------------------------------===//
|
||||
@@ -403,22 +419,6 @@ void FunctionTemplateDecl::addSpecialization(
|
||||
InsertPos);
|
||||
}
|
||||
|
||||
ArrayRef<TemplateArgument> FunctionTemplateDecl::getInjectedTemplateArgs() {
|
||||
TemplateParameterList *Params = getTemplateParameters();
|
||||
Common *CommonPtr = getCommonPtr();
|
||||
if (!CommonPtr->InjectedArgs) {
|
||||
auto &Context = getASTContext();
|
||||
SmallVector<TemplateArgument, 16> TemplateArgs;
|
||||
Context.getInjectedTemplateArgs(Params, TemplateArgs);
|
||||
CommonPtr->InjectedArgs =
|
||||
new (Context) TemplateArgument[TemplateArgs.size()];
|
||||
std::copy(TemplateArgs.begin(), TemplateArgs.end(),
|
||||
CommonPtr->InjectedArgs);
|
||||
}
|
||||
|
||||
return llvm::makeArrayRef(CommonPtr->InjectedArgs, Params->size());
|
||||
}
|
||||
|
||||
void FunctionTemplateDecl::mergePrevDecl(FunctionTemplateDecl *Prev) {
|
||||
using Base = RedeclarableTemplateDecl;
|
||||
|
||||
|
||||
@@ -1104,6 +1104,18 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, NamedDecl *D, const Expr *E) {
|
||||
// - The normal form of an expression (E) is the normal form of E.
|
||||
// [...]
|
||||
E = E->IgnoreParenImpCasts();
|
||||
|
||||
// C++2a [temp.param]p4:
|
||||
// [...] If T is not a pack, then E is E', otherwise E is (E' && ...).
|
||||
//
|
||||
// Using the pattern suffices because the partial ordering rules guarantee
|
||||
// the template paramaters are equivalent.
|
||||
if (auto *FoldE = dyn_cast<const CXXFoldExpr>(E)) {
|
||||
assert(FoldE->isRightFold() && FoldE->getOperator() == BO_LAnd);
|
||||
assert(E->IgnoreParenImpCasts() == E);
|
||||
E = FoldE->getPattern();
|
||||
}
|
||||
|
||||
if (LogicalBinOp BO = E) {
|
||||
auto LHS = fromConstraintExpr(S, D, BO.getLHS());
|
||||
if (!LHS)
|
||||
|
||||
@@ -9776,19 +9776,13 @@ static bool haveSameParameterTypes(ASTContext &Context, const FunctionDecl *F1,
|
||||
|
||||
/// We're allowed to use constraints partial ordering only if the candidates
|
||||
/// have the same parameter types:
|
||||
/// [temp.func.order]p6.2.2 [...] or if the function parameters that
|
||||
/// positionally correspond between the two templates are not of the same type,
|
||||
/// neither template is more specialized than the other.
|
||||
/// [over.match.best]p2.6
|
||||
/// F1 and F2 are non-template functions with the same parameter-type-lists,
|
||||
/// and F1 is more constrained than F2 [...]
|
||||
static bool canCompareFunctionConstraints(Sema &S,
|
||||
static bool sameFunctionParameterTypeLists(Sema &S,
|
||||
const OverloadCandidate &Cand1,
|
||||
const OverloadCandidate &Cand2) {
|
||||
// FIXME: Per P2113R0 we also need to compare the template parameter lists
|
||||
// when comparing template functions.
|
||||
if (Cand1.Function && Cand2.Function && Cand1.Function->hasPrototype() &&
|
||||
Cand2.Function->hasPrototype()) {
|
||||
if (Cand1.Function && Cand2.Function) {
|
||||
auto *PT1 = cast<FunctionProtoType>(Cand1.Function->getFunctionType());
|
||||
auto *PT2 = cast<FunctionProtoType>(Cand2.Function->getFunctionType());
|
||||
if (PT1->getNumParams() == PT2->getNumParams() &&
|
||||
@@ -10031,15 +10025,14 @@ bool clang::isBetterOverloadCandidate(
|
||||
isa<CXXConversionDecl>(Cand1.Function) ? TPOC_Conversion
|
||||
: TPOC_Call,
|
||||
Cand1.ExplicitCallArguments, Cand2.ExplicitCallArguments,
|
||||
Cand1.isReversed() ^ Cand2.isReversed(),
|
||||
canCompareFunctionConstraints(S, Cand1, Cand2)))
|
||||
Cand1.isReversed() ^ Cand2.isReversed()))
|
||||
return BetterTemplate == Cand1.Function->getPrimaryTemplate();
|
||||
}
|
||||
|
||||
// -— F1 and F2 are non-template functions with the same
|
||||
// parameter-type-lists, and F1 is more constrained than F2 [...],
|
||||
if (!Cand1IsSpecialization && !Cand2IsSpecialization &&
|
||||
canCompareFunctionConstraints(S, Cand1, Cand2)) {
|
||||
sameFunctionParameterTypeLists(S, Cand1, Cand2)) {
|
||||
Expr *RC1 = Cand1.Function->getTrailingRequiresClause();
|
||||
Expr *RC2 = Cand2.Function->getTrailingRequiresClause();
|
||||
if (RC1 && RC2) {
|
||||
|
||||
@@ -7652,10 +7652,10 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
|
||||
|
||||
if (isTemplateTemplateParameterAtLeastAsSpecializedAs(Params, Template,
|
||||
Arg.getLocation())) {
|
||||
// C++2a[temp.func.order]p2
|
||||
// P2113
|
||||
// C++20[temp.func.order]p2
|
||||
// [...] If both deductions succeed, the partial ordering selects the
|
||||
// more constrained template as described by the rules in
|
||||
// [temp.constr.order].
|
||||
// more constrained template (if one exists) as determined below.
|
||||
SmallVector<const Expr *, 3> ParamsAC, TemplateAC;
|
||||
Params->getAssociatedConstraints(ParamsAC);
|
||||
// C++2a[temp.arg.template]p3
|
||||
@@ -7864,7 +7864,8 @@ Sema::BuildExpressionFromIntegralTemplateArgument(const TemplateArgument &Arg,
|
||||
static bool MatchTemplateParameterKind(
|
||||
Sema &S, NamedDecl *New, const NamedDecl *NewInstFrom, NamedDecl *Old,
|
||||
const NamedDecl *OldInstFrom, bool Complain,
|
||||
Sema::TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc) {
|
||||
Sema::TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc,
|
||||
bool PartialOrdering) {
|
||||
// Check the actual kind (type, non-type, template).
|
||||
if (Old->getKind() != New->getKind()) {
|
||||
if (Complain) {
|
||||
@@ -7952,11 +7953,11 @@ static bool MatchTemplateParameterKind(
|
||||
(Kind == Sema::TPL_TemplateMatch
|
||||
? Sema::TPL_TemplateTemplateParmMatch
|
||||
: Kind),
|
||||
TemplateArgLoc))
|
||||
TemplateArgLoc, PartialOrdering))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Kind != Sema::TPL_TemplateTemplateArgumentMatch &&
|
||||
if (!PartialOrdering && Kind != Sema::TPL_TemplateTemplateArgumentMatch &&
|
||||
!isa<TemplateTemplateParmDecl>(Old)) {
|
||||
const Expr *NewC = nullptr, *OldC = nullptr;
|
||||
|
||||
@@ -8049,7 +8050,8 @@ void DiagnoseTemplateParameterListArityMismatch(Sema &S,
|
||||
bool Sema::TemplateParameterListsAreEqual(
|
||||
const NamedDecl *NewInstFrom, TemplateParameterList *New,
|
||||
const NamedDecl *OldInstFrom, TemplateParameterList *Old, bool Complain,
|
||||
TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc) {
|
||||
TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc,
|
||||
bool PartialOrdering) {
|
||||
if (Old->size() != New->size() && Kind != TPL_TemplateTemplateArgumentMatch) {
|
||||
if (Complain)
|
||||
DiagnoseTemplateParameterListArityMismatch(*this, New, Old, Kind,
|
||||
@@ -8081,7 +8083,7 @@ bool Sema::TemplateParameterListsAreEqual(
|
||||
|
||||
if (!MatchTemplateParameterKind(*this, *NewParm, NewInstFrom, *OldParm,
|
||||
OldInstFrom, Complain, Kind,
|
||||
TemplateArgLoc))
|
||||
TemplateArgLoc, PartialOrdering))
|
||||
return false;
|
||||
|
||||
++NewParm;
|
||||
@@ -8098,7 +8100,7 @@ bool Sema::TemplateParameterListsAreEqual(
|
||||
for (; NewParm != NewParmEnd; ++NewParm) {
|
||||
if (!MatchTemplateParameterKind(*this, *NewParm, NewInstFrom, *OldParm,
|
||||
OldInstFrom, Complain, Kind,
|
||||
TemplateArgLoc))
|
||||
TemplateArgLoc, PartialOrdering))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -8112,7 +8114,7 @@ bool Sema::TemplateParameterListsAreEqual(
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Kind != TPL_TemplateTemplateArgumentMatch) {
|
||||
if (!PartialOrdering && Kind != TPL_TemplateTemplateArgumentMatch) {
|
||||
const Expr *NewRC = New->getRequiresClause();
|
||||
const Expr *OldRC = Old->getRequiresClause();
|
||||
|
||||
|
||||
@@ -5172,43 +5172,26 @@ static bool isAtLeastAsSpecializedAs(Sema &S,
|
||||
/// candidate with a reversed parameter order. In this case, the corresponding
|
||||
/// P/A pairs between FT1 and FT2 are reversed.
|
||||
///
|
||||
/// \param AllowOrderingByConstraints If \c is false, don't check whether one
|
||||
/// of the templates is more constrained than the other. Default is true.
|
||||
///
|
||||
/// \returns the more specialized function template. If neither
|
||||
/// template is more specialized, returns NULL.
|
||||
FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
|
||||
FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc,
|
||||
TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1,
|
||||
unsigned NumCallArguments2, bool Reversed,
|
||||
bool AllowOrderingByConstraints) {
|
||||
|
||||
auto JudgeByConstraints = [&]() -> FunctionTemplateDecl * {
|
||||
if (!AllowOrderingByConstraints)
|
||||
return nullptr;
|
||||
llvm::SmallVector<const Expr *, 3> AC1, AC2;
|
||||
FT1->getAssociatedConstraints(AC1);
|
||||
FT2->getAssociatedConstraints(AC2);
|
||||
bool AtLeastAsConstrained1, AtLeastAsConstrained2;
|
||||
if (IsAtLeastAsConstrained(FT1, AC1, FT2, AC2, AtLeastAsConstrained1))
|
||||
return nullptr;
|
||||
if (IsAtLeastAsConstrained(FT2, AC2, FT1, AC1, AtLeastAsConstrained2))
|
||||
return nullptr;
|
||||
if (AtLeastAsConstrained1 == AtLeastAsConstrained2)
|
||||
return nullptr;
|
||||
return AtLeastAsConstrained1 ? FT1 : FT2;
|
||||
};
|
||||
unsigned NumCallArguments2, bool Reversed) {
|
||||
|
||||
bool Better1 = isAtLeastAsSpecializedAs(*this, Loc, FT1, FT2, TPOC,
|
||||
NumCallArguments1, Reversed);
|
||||
bool Better2 = isAtLeastAsSpecializedAs(*this, Loc, FT2, FT1, TPOC,
|
||||
NumCallArguments2, Reversed);
|
||||
|
||||
// C++ [temp.deduct.partial]p10:
|
||||
// F is more specialized than G if F is at least as specialized as G and G
|
||||
// is not at least as specialized as F.
|
||||
if (Better1 != Better2) // We have a clear winner
|
||||
return Better1 ? FT1 : FT2;
|
||||
|
||||
if (!Better1 && !Better2) // Neither is better than the other
|
||||
return JudgeByConstraints();
|
||||
return nullptr;
|
||||
|
||||
// C++ [temp.deduct.partial]p11:
|
||||
// ... and if G has a trailing function parameter pack for which F does not
|
||||
@@ -5260,7 +5243,61 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
|
||||
}
|
||||
}
|
||||
|
||||
return JudgeByConstraints();
|
||||
if (!Context.getLangOpts().CPlusPlus20 || isa<CXXDeductionGuideDecl>(FD1) ||
|
||||
isa<CXXDeductionGuideDecl>(FD2))
|
||||
return nullptr;
|
||||
|
||||
// Match GCC on not implementing [temp.func.order]p6.2.1.
|
||||
|
||||
// C++20 [temp.func.order]p6:
|
||||
// If deduction against the other template succeeds for both transformed
|
||||
// templates, constraints can be considered as follows:
|
||||
|
||||
// C++20 [temp.func.order]p6.1:
|
||||
// If their template-parameter-lists (possibly including template-parameters
|
||||
// invented for an abbreviated function template ([dcl.fct])) or function
|
||||
// parameter lists differ in length, neither template is more specialized
|
||||
// than the other.
|
||||
TemplateParameterList *TPL1 = FT1->getTemplateParameters();
|
||||
TemplateParameterList *TPL2 = FT2->getTemplateParameters();
|
||||
if (TPL1->size() != TPL2->size() || NumParams1 != NumParams2)
|
||||
return nullptr;
|
||||
|
||||
// C++20 [temp.func.order]p6.2.2:
|
||||
// Otherwise, if the corresponding template-parameters of the
|
||||
// template-parameter-lists are not equivalent ([temp.over.link]) or if the
|
||||
// function parameters that positionally correspond between the two
|
||||
// templates are not of the same type, neither template is more specialized
|
||||
// than the other.
|
||||
if (!TemplateParameterListsAreEqual(
|
||||
TPL1, TPL2, false, Sema::TPL_TemplateMatch, SourceLocation(), true))
|
||||
return nullptr;
|
||||
|
||||
for (unsigned i = 0; i < NumParams1; ++i)
|
||||
if (!Context.hasSameType(FD1->getParamDecl(i)->getType(),
|
||||
FD2->getParamDecl(i)->getType()))
|
||||
return nullptr;
|
||||
|
||||
// C++20 [temp.func.order]p6.3:
|
||||
// Otherwise, if the context in which the partial ordering is done is
|
||||
// that of a call to a conversion function and the return types of the
|
||||
// templates are not the same, then neither template is more specialized
|
||||
// than the other.
|
||||
if (TPOC == TPOC_Conversion &&
|
||||
!Context.hasSameType(FD1->getReturnType(), FD2->getReturnType()))
|
||||
return nullptr;
|
||||
|
||||
llvm::SmallVector<const Expr *, 3> AC1, AC2;
|
||||
FT1->getAssociatedConstraints(AC1);
|
||||
FT2->getAssociatedConstraints(AC2);
|
||||
bool AtLeastAsConstrained1, AtLeastAsConstrained2;
|
||||
if (IsAtLeastAsConstrained(FT1, AC1, FT2, AC2, AtLeastAsConstrained1))
|
||||
return nullptr;
|
||||
if (IsAtLeastAsConstrained(FT2, AC2, FT1, AC1, AtLeastAsConstrained2))
|
||||
return nullptr;
|
||||
if (AtLeastAsConstrained1 == AtLeastAsConstrained2)
|
||||
return nullptr;
|
||||
return AtLeastAsConstrained1 ? FT1 : FT2;
|
||||
}
|
||||
|
||||
/// Determine if the two templates are equivalent.
|
||||
@@ -5438,7 +5475,7 @@ static bool isAtLeastAsSpecializedAs(Sema &S, QualType T1, QualType T2,
|
||||
}
|
||||
|
||||
namespace {
|
||||
// A dummy pass to return nullptr instead of P2 when performing "more
|
||||
// A dummy class to return nullptr instead of P2 when performing "more
|
||||
// specialized than primary" check.
|
||||
struct GetP2 {
|
||||
template <typename T1, typename T2,
|
||||
@@ -5452,6 +5489,52 @@ struct GetP2 {
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
// The assumption is that two template argument lists have the same size.
|
||||
struct TemplateArgumentListAreEqual {
|
||||
ASTContext &Ctx;
|
||||
TemplateArgumentListAreEqual(ASTContext &Ctx) : Ctx(Ctx) {}
|
||||
|
||||
template <typename T1, typename T2,
|
||||
std::enable_if_t<std::is_same<T1, T2>::value, bool> = true>
|
||||
bool operator()(T1 *PS1, T2 *PS2) {
|
||||
ArrayRef<TemplateArgument> Args1 = PS1->getTemplateArgs().asArray(),
|
||||
Args2 = PS2->getTemplateArgs().asArray();
|
||||
|
||||
for (unsigned I = 0, E = Args1.size(); I < E; ++I) {
|
||||
// We use profile, instead of structural comparison of the arguments,
|
||||
// because canonicalization can't do the right thing for dependent
|
||||
// expressions.
|
||||
llvm::FoldingSetNodeID IDA, IDB;
|
||||
Args1[I].Profile(IDA, Ctx);
|
||||
Args2[I].Profile(IDB, Ctx);
|
||||
if (IDA != IDB)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T1, typename T2,
|
||||
std::enable_if_t<!std::is_same<T1, T2>::value, bool> = true>
|
||||
bool operator()(T1 *Spec, T2 *Primary) {
|
||||
ArrayRef<TemplateArgument> Args1 = Spec->getTemplateArgs().asArray(),
|
||||
Args2 = Primary->getInjectedTemplateArgs();
|
||||
|
||||
for (unsigned I = 0, E = Args1.size(); I < E; ++I) {
|
||||
// We use profile, instead of structural comparison of the arguments,
|
||||
// because canonicalization can't do the right thing for dependent
|
||||
// expressions.
|
||||
llvm::FoldingSetNodeID IDA, IDB;
|
||||
Args1[I].Profile(IDA, Ctx);
|
||||
// Unlike the specialization arguments, the injected arguments are not
|
||||
// always canonical.
|
||||
Ctx.getCanonicalTemplateArgument(Args2[I]).Profile(IDB, Ctx);
|
||||
if (IDA != IDB)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
/// Returns the more specialized template specialization between T1/P1 and
|
||||
@@ -5491,52 +5574,81 @@ getMoreSpecialized(Sema &S, QualType T1, QualType T2, TemplateLikeDecl *P1,
|
||||
if (IsMoreSpecialThanPrimaryCheck && !Better2)
|
||||
return P1;
|
||||
|
||||
// C++ [temp.deduct.partial]p10:
|
||||
// F is more specialized than G if F is at least as specialized as G and G
|
||||
// is not at least as specialized as F.
|
||||
if (Better1 != Better2) // We have a clear winner
|
||||
return Better1 ? P1 : GetP2()(P1, P2);
|
||||
|
||||
if (!Better1 && !Better2)
|
||||
return nullptr;
|
||||
|
||||
if (Better1 && Better2) {
|
||||
// This a speculative fix for CWG1432 (Similar to the fix for CWG1395) that
|
||||
// there is no wording or even resolution for this issue.
|
||||
bool ClangABICompat15 = S.Context.getLangOpts().getClangABICompat() <=
|
||||
LangOptions::ClangABI::Ver15;
|
||||
if (!ClangABICompat15) {
|
||||
auto *TST1 = cast<TemplateSpecializationType>(T1);
|
||||
auto *TST2 = cast<TemplateSpecializationType>(T2);
|
||||
const TemplateArgument &TA1 = TST1->template_arguments().back();
|
||||
if (TA1.getKind() == TemplateArgument::Pack) {
|
||||
assert(TST1->getNumArgs() == TST2->getNumArgs());
|
||||
const TemplateArgument &TA2 = TST2->template_arguments().back();
|
||||
assert(TA2.getKind() == TemplateArgument::Pack);
|
||||
unsigned PackSize1 = TA1.pack_size();
|
||||
unsigned PackSize2 = TA2.pack_size();
|
||||
bool IsPackExpansion1 =
|
||||
PackSize1 && TA1.pack_elements().back().isPackExpansion();
|
||||
bool IsPackExpansion2 =
|
||||
PackSize2 && TA2.pack_elements().back().isPackExpansion();
|
||||
if (PackSize1 != PackSize2 && IsPackExpansion1 != IsPackExpansion2) {
|
||||
if (PackSize1 > PackSize2 && IsPackExpansion1)
|
||||
return GetP2()(P1, P2);
|
||||
if (PackSize1 < PackSize2 && IsPackExpansion2)
|
||||
return P1;
|
||||
}
|
||||
// This a speculative fix for CWG1432 (Similar to the fix for CWG1395) that
|
||||
// there is no wording or even resolution for this issue.
|
||||
bool ClangABICompat15 = S.Context.getLangOpts().getClangABICompat() <=
|
||||
LangOptions::ClangABI::Ver15;
|
||||
if (!ClangABICompat15) {
|
||||
auto *TST1 = cast<TemplateSpecializationType>(T1);
|
||||
auto *TST2 = cast<TemplateSpecializationType>(T2);
|
||||
const TemplateArgument &TA1 = TST1->template_arguments().back();
|
||||
if (TA1.getKind() == TemplateArgument::Pack) {
|
||||
assert(TST1->getNumArgs() == TST2->getNumArgs());
|
||||
const TemplateArgument &TA2 = TST2->template_arguments().back();
|
||||
assert(TA2.getKind() == TemplateArgument::Pack);
|
||||
unsigned PackSize1 = TA1.pack_size();
|
||||
unsigned PackSize2 = TA2.pack_size();
|
||||
bool IsPackExpansion1 =
|
||||
PackSize1 && TA1.pack_elements().back().isPackExpansion();
|
||||
bool IsPackExpansion2 =
|
||||
PackSize2 && TA2.pack_elements().back().isPackExpansion();
|
||||
if (PackSize1 != PackSize2 && IsPackExpansion1 != IsPackExpansion2) {
|
||||
if (PackSize1 > PackSize2 && IsPackExpansion1)
|
||||
return GetP2()(P1, P2);
|
||||
if (PackSize1 < PackSize2 && IsPackExpansion2)
|
||||
return P1;
|
||||
}
|
||||
}
|
||||
|
||||
llvm::SmallVector<const Expr *, 3> AC1, AC2;
|
||||
P1->getAssociatedConstraints(AC1);
|
||||
P2->getAssociatedConstraints(AC2);
|
||||
bool AtLeastAsConstrained1, AtLeastAsConstrained2;
|
||||
if (S.IsAtLeastAsConstrained(P1, AC1, P2, AC2, AtLeastAsConstrained1) ||
|
||||
(IsMoreSpecialThanPrimaryCheck && !AtLeastAsConstrained1))
|
||||
return nullptr;
|
||||
if (S.IsAtLeastAsConstrained(P2, AC2, P1, AC1, AtLeastAsConstrained2))
|
||||
return nullptr;
|
||||
if (AtLeastAsConstrained1 == AtLeastAsConstrained2)
|
||||
return nullptr;
|
||||
return AtLeastAsConstrained1 ? P1 : GetP2()(P1, P2);
|
||||
}
|
||||
|
||||
return Better1 ? P1 : GetP2()(P1, P2);
|
||||
if (!S.Context.getLangOpts().CPlusPlus20)
|
||||
return nullptr;
|
||||
|
||||
// Match GCC on not implementing [temp.func.order]p6.2.1.
|
||||
|
||||
// C++20 [temp.func.order]p6:
|
||||
// If deduction against the other template succeeds for both transformed
|
||||
// templates, constraints can be considered as follows:
|
||||
|
||||
TemplateParameterList *TPL1 = P1->getTemplateParameters();
|
||||
TemplateParameterList *TPL2 = P2->getTemplateParameters();
|
||||
if (TPL1->size() != TPL2->size())
|
||||
return nullptr;
|
||||
|
||||
// C++20 [temp.func.order]p6.2.2:
|
||||
// Otherwise, if the corresponding template-parameters of the
|
||||
// template-parameter-lists are not equivalent ([temp.over.link]) or if the
|
||||
// function parameters that positionally correspond between the two
|
||||
// templates are not of the same type, neither template is more specialized
|
||||
// than the other.
|
||||
if (!S.TemplateParameterListsAreEqual(
|
||||
TPL1, TPL2, false, Sema::TPL_TemplateMatch, SourceLocation(), true))
|
||||
return nullptr;
|
||||
|
||||
if (!TemplateArgumentListAreEqual(S.getASTContext())(P1, P2))
|
||||
return nullptr;
|
||||
|
||||
llvm::SmallVector<const Expr *, 3> AC1, AC2;
|
||||
P1->getAssociatedConstraints(AC1);
|
||||
P2->getAssociatedConstraints(AC2);
|
||||
bool AtLeastAsConstrained1, AtLeastAsConstrained2;
|
||||
if (S.IsAtLeastAsConstrained(P1, AC1, P2, AC2, AtLeastAsConstrained1) ||
|
||||
(IsMoreSpecialThanPrimaryCheck && !AtLeastAsConstrained1))
|
||||
return nullptr;
|
||||
if (S.IsAtLeastAsConstrained(P2, AC2, P1, AC1, AtLeastAsConstrained2))
|
||||
return nullptr;
|
||||
if (AtLeastAsConstrained1 == AtLeastAsConstrained2)
|
||||
return nullptr;
|
||||
return AtLeastAsConstrained1 ? P1 : GetP2()(P1, P2);
|
||||
}
|
||||
|
||||
/// Returns the more specialized class template partial specialization
|
||||
@@ -5596,17 +5708,11 @@ Sema::getMoreSpecializedPartialSpecialization(
|
||||
|
||||
bool Sema::isMoreSpecializedThanPrimary(
|
||||
VarTemplatePartialSpecializationDecl *Spec, TemplateDeductionInfo &Info) {
|
||||
TemplateDecl *Primary = Spec->getSpecializedTemplate();
|
||||
// FIXME: Cache the injected template arguments rather than recomputing
|
||||
// them for each partial specialization.
|
||||
SmallVector<TemplateArgument, 8> PrimaryArgs;
|
||||
Context.getInjectedTemplateArgs(Primary->getTemplateParameters(),
|
||||
PrimaryArgs);
|
||||
|
||||
VarTemplateDecl *Primary = Spec->getSpecializedTemplate();
|
||||
TemplateName CanonTemplate =
|
||||
Context.getCanonicalTemplateName(TemplateName(Primary));
|
||||
QualType PrimaryT = Context.getTemplateSpecializationType(
|
||||
CanonTemplate, PrimaryArgs);
|
||||
CanonTemplate, Primary->getInjectedTemplateArgs());
|
||||
QualType PartialT = Context.getTemplateSpecializationType(
|
||||
CanonTemplate, Spec->getTemplateArgs().asArray());
|
||||
|
||||
|
||||
@@ -98,26 +98,31 @@ namespace non_template
|
||||
static_assert(is_same_v<decltype(bar<int>()), void>); // expected-error {{call to 'bar' is ambiguous}}
|
||||
|
||||
template<typename T>
|
||||
constexpr int goo(int a) requires AtLeast2<int> && true {
|
||||
constexpr int goo(int a) requires AtLeast2<int> && true { // expected-note {{candidate function}}
|
||||
return 1;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr int goo(const int b) requires AtLeast2<int> {
|
||||
constexpr int goo(const int b) requires AtLeast2<int> { // expected-note {{candidate function}}
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Only trailing requires clauses of redeclarations are compared for overload resolution.
|
||||
// [temp.func.order] p5
|
||||
// Since, in a call context, such type deduction considers only parameters
|
||||
// for which there are explicit call arguments, some parameters are ignored
|
||||
// (namely, function parameter packs, parameters with default arguments, and
|
||||
// ellipsis parameters).
|
||||
template<typename T>
|
||||
constexpr int doo(int a, ...) requires AtLeast2<int> && true { // expected-note {{candidate function}}
|
||||
constexpr int doo(int a, ...) requires AtLeast2<int> && true {
|
||||
return 1;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr int doo(int b) requires AtLeast2<int> { // expected-note {{candidate function}}
|
||||
constexpr int doo(int b) requires AtLeast2<int> {
|
||||
return 2;
|
||||
}
|
||||
|
||||
static_assert(goo<int>(1) == 1);
|
||||
static_assert(doo<int>(2) == 1); // expected-error {{call to 'doo' is ambiguous}}
|
||||
// By temp.func.order-6.2.2, this is ambiguous because parameter a and b have different types.
|
||||
static_assert(goo<int>(1) == 1); // expected-error {{call to 'goo' is ambiguous}}
|
||||
static_assert(doo<int>(2) == 1);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s
|
||||
|
||||
struct A;
|
||||
struct B;
|
||||
|
||||
template <typename> constexpr bool True = true;
|
||||
template <typename T> concept C = True<T>;
|
||||
template <typename T> concept D = C<T> && sizeof(T) > 2;
|
||||
template <typename T> concept E = D<T> && alignof(T) > 1;
|
||||
|
||||
struct A {};
|
||||
template <typename, auto, int, A, typename...> struct S {};
|
||||
template <typename, auto, int, A, auto...> struct S2 {};
|
||||
template <typename T, typename U> struct X {};
|
||||
|
||||
namespace p6 {
|
||||
|
||||
struct B;
|
||||
|
||||
void f(C auto &, auto &) = delete;
|
||||
template <C Q> void f(Q &, C auto &);
|
||||
@@ -13,14 +21,125 @@ void g(struct A *ap, struct B *bp) {
|
||||
f(*ap, *bp);
|
||||
}
|
||||
|
||||
template <typename T, typename U> struct X {};
|
||||
|
||||
#if 0
|
||||
// FIXME: [temp.func.order]p6.2.1 is not implemented, matching GCC.
|
||||
template <typename T, C U, typename V> bool operator==(X<T, U>, V) = delete;
|
||||
template <C T, C U, C V> bool operator==(T, X<U, V>);
|
||||
|
||||
bool h() {
|
||||
return X<void *, int>{} == 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
template<C T, C auto M, int W, A S,
|
||||
template<typename, auto, int, A, typename...> class U,
|
||||
typename... Z>
|
||||
void foo(T, U<T, M, W, S, Z...>) = delete;
|
||||
template<C T, D auto M, int W, A S,
|
||||
template<typename, auto, int, A, typename...> class U,
|
||||
typename... Z>
|
||||
void foo(T, U<T, M, W, S, Z...>) = delete;
|
||||
template<C T, E auto M, int W, A S,
|
||||
template<typename, auto, int, A, typename...> class U,
|
||||
typename... Z>
|
||||
void foo(T, U<T, M, W, S, Z...>);
|
||||
|
||||
// check auto template parameter pack.
|
||||
template<C T, auto M, int W, A S,
|
||||
template<typename, auto, int, A, auto...> class U,
|
||||
C auto... Z>
|
||||
void foo2(T, U<T, M, W, S, Z...>) = delete;
|
||||
template<C T, auto M, int W, A S,
|
||||
template<typename, auto, int, A, auto...> class U,
|
||||
D auto... Z>
|
||||
void foo2(T, U<T, M, W, S, Z...>) = delete;
|
||||
template<C T, auto M, int W, A S,
|
||||
template<typename, auto, int, A, auto...> class U,
|
||||
E auto... Z>
|
||||
void foo2(T, U<T, M, W, S, Z...>);
|
||||
|
||||
void bar(S<int, 1, 1, A{}, int> s, S2<int, 1, 1, A{}, 0, 0u> s2) {
|
||||
foo(0, s);
|
||||
foo2(0, s2);
|
||||
}
|
||||
|
||||
template<C auto... T> void bar2();
|
||||
template<D auto... T> void bar2() = delete;
|
||||
|
||||
} // namespace p6
|
||||
|
||||
namespace TestConversionFunction {
|
||||
struct Y {
|
||||
template<C T, typename U> operator X<T, U>(); // expected-note {{candidate function [with T = int, U = int]}}
|
||||
template<typename T, typename U> operator X<U, T>(); // expected-note {{candidate function [with T = int, U = int]}}
|
||||
};
|
||||
|
||||
X<int,int> f() {
|
||||
return Y{}; // expected-error {{conversion from 'Y' to 'X<int, int>' is ambiguous}}
|
||||
}
|
||||
}
|
||||
|
||||
namespace ClassPartialSpecPartialOrdering {
|
||||
template<D T> struct Y { Y()=delete; }; // expected-note {{template is declared here}}
|
||||
template<C T> struct Y<T> {}; // expected-error {{class template partial specialization is not more specialized than the primary template}}
|
||||
|
||||
template<C T, int I> struct Y1 { Y1()=delete; };
|
||||
template<D T> struct Y1<T, 2> { Y1()=delete; };
|
||||
template<E T> struct Y1<T, 1+1> {};
|
||||
|
||||
template<class T, int I, int U> struct Y2 {};
|
||||
template<class T, int I> struct Y2<T*, I, I+2> {}; // expected-note {{partial specialization matches}}
|
||||
template<C T, int I> struct Y2<T*, I, I+1+1> {}; // expected-note {{partial specialization matches}}
|
||||
|
||||
template<C T, C auto I, int W, A S, template<typename, auto, int, A, typename...> class U, typename... Z>
|
||||
struct Y3 { Y3()=delete; };
|
||||
template<C T, D auto I, int W, A S, template<typename, auto, int, A, typename...> class U, typename... Z>
|
||||
struct Y3<T, I, W, S, U, Z...> { Y3()=delete; };
|
||||
template<C T, E auto I, int W, A S, template<typename, auto, int, A, typename...> class U, typename... Z>
|
||||
struct Y3<T, I, W, S, U, Z...> {};
|
||||
|
||||
void f() {
|
||||
Y1<int, 2> a;
|
||||
Y2<char*, 1, 3> b; // expected-error {{ambiguous partial specializations}}
|
||||
Y3<int, 1, 1, A{}, S, int> c;
|
||||
}
|
||||
|
||||
template<C T, C V> struct Y4; // expected-note {{template is declared here}}
|
||||
template<D T, C V> struct Y4<V, T>; // expected-error {{class template partial specialization is not more specialized than the primary template}}
|
||||
|
||||
template<C auto T> struct W1;
|
||||
template<D auto T> struct W1<T> {};
|
||||
|
||||
template<C auto... T> struct W2;
|
||||
template<D auto... T> struct W2<T...> {};
|
||||
|
||||
template<class T, class U>
|
||||
concept C1 = C<T> && C<U>;
|
||||
template<class T, class U>
|
||||
concept D1 = D<T> && C<U>;
|
||||
|
||||
template<C1<A> auto T> struct W3;
|
||||
template<D1<A> auto T> struct W3<T> {};
|
||||
|
||||
template<C1<A> auto... T> struct W4;
|
||||
template<D1<A> auto... T> struct W4<T...> {};
|
||||
|
||||
// FIXME: enable once Clang support non-trivial auto on NTTP.
|
||||
// template<C auto* T> struct W5;
|
||||
// template<D auto* T> struct W5<T> {};
|
||||
|
||||
// FIXME: enable once Clang support non-trivial auto on NTTP.
|
||||
// template<C auto& T> struct W6;
|
||||
// template<D auto& T> struct W6<T> {};
|
||||
|
||||
struct W1<0> w1;
|
||||
struct W2<0> w2;
|
||||
struct W3<0> w3;
|
||||
struct W4<0> w4;
|
||||
// FIXME: enable once Clang support non-trivial auto on NTTP.
|
||||
// struct W5<(int*)nullptr> w5;
|
||||
// struct W6<w5> w6;
|
||||
}
|
||||
|
||||
namespace PR53640 {
|
||||
|
||||
|
||||
@@ -963,7 +963,7 @@ code. This issue is expected to be rectified soon.
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://wg21.link/p2113r0">P2113R0</a></td>
|
||||
<td rowspan="1" class="none" align="center">No</td>
|
||||
<td rowspan="1" class="unreleased" align="center">Clang 16</td>
|
||||
</tr>
|
||||
<!-- Albuquerque papers -->
|
||||
<tr>
|
||||
|
||||
Reference in New Issue
Block a user