ychen updated this revision to Diff 456528.
ychen added a comment.
This revision is now accepted and ready to land.
- handle constrained placeholder [pack] types
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D128750/new/
https://reviews.llvm.org/D128750
Files:
clang/docs/ReleaseNotes.rst
clang/include/clang/AST/DeclTemplate.h
clang/include/clang/Sema/Sema.h
clang/include/clang/Sema/SemaConcept.h
clang/lib/AST/ASTContext.cpp
clang/lib/AST/DeclTemplate.cpp
clang/lib/Sema/SemaConcept.cpp
clang/lib/Sema/SemaOverload.cpp
clang/lib/Sema/SemaTemplate.cpp
clang/lib/Sema/SemaTemplateDeduction.cpp
clang/lib/Sema/SemaTemplateInstantiate.cpp
clang/test/CXX/over/over.match/over.match.best/p1-2a.cpp
clang/test/CXX/temp/temp.decls/temp.fct/temp.func.order/p6.cpp
clang/www/cxx_status.html
Index: clang/www/cxx_status.html
===================================================================
--- clang/www/cxx_status.html
+++ clang/www/cxx_status.html
@@ -967,7 +967,7 @@
</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>
Index: clang/test/CXX/temp/temp.decls/temp.fct/temp.func.order/p6.cpp
===================================================================
--- clang/test/CXX/temp/temp.decls/temp.fct/temp.func.order/p6.cpp
+++ clang/test/CXX/temp/temp.decls/temp.fct/temp.func.order/p6.cpp
@@ -1,10 +1,17 @@
// 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, int, A, typename...> struct S {};
+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 +20,85 @@
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, int W, A S, template<typename, int, A, typename...> class U, typename... Z>
+void foo(T, U<T, W, S, Z...>) = delete;
+template<D T, int W, A S, template<typename, int, A, typename...> class U, typename... Z>
+void foo(T, U<T, W, S, Z...>) = delete;
+template<E T, int W, A S, template<typename, int, A, typename...> class U, typename... Z>
+void foo(T, U<T, W, S, Z...>);
+
+void bar(S<int, 1, A{}, int> s) {
+ foo(0, s);
+}
+
+} // 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> struct B { B()=delete; };
+template<D T> struct B<T> { B()=delete; };
+template<E T> struct B<T> {};
+
+template<C T, int W, A S, template<typename, int, A, typename...> class U, typename... Z>
+struct Some { Some()=delete; };
+template<D T, int W, A S, template<typename, int, A, typename...> class U, typename... Z>
+struct Some<T, W, S, U, Z...> { Some()=delete; };
+template<E T, int W, A S, template<typename, int, A, typename...> class U, typename... Z>
+struct Some<T, W, S, U, Z...> {};
+
+void f() {
+ B<int> b;
+ Some<int, 0, A{}, S, int> c;
+}
+
+template<C T, C V> struct Z; // expected-note {{template is declared here}}
+template<D T, C V> struct Z<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...> {};
+
+struct W1<0> w1;
+struct W2<0> w2;
+struct W3<0> w3;
+struct W4<0> w4;
+}
namespace PR53640 {
Index: clang/test/CXX/over/over.match/over.match.best/p1-2a.cpp
===================================================================
--- clang/test/CXX/over/over.match/over.match.best/p1-2a.cpp
+++ clang/test/CXX/over/over.match/over.match.best/p1-2a.cpp
@@ -98,26 +98,31 @@
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);
}
Index: clang/lib/Sema/SemaTemplateInstantiate.cpp
===================================================================
--- clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1835,7 +1835,7 @@
assert(Arg.getKind() == TemplateArgument::Type &&
"Template argument kind mismatch");
- QualType Replacement = Arg.getAsType();
+ QualType Replacement = SemaRef.Context.getCanonicalType(Arg.getAsType());
// TODO: only do this uniquing once, at the start of instantiation.
QualType Result
Index: clang/lib/Sema/SemaTemplateDeduction.cpp
===================================================================
--- clang/lib/Sema/SemaTemplateDeduction.cpp
+++ clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -5174,50 +5174,36 @@
/// 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;
+
+ TemplateParameterList *TPL1 = FT1->getTemplateParameters();
+ TemplateParameterList *TPL2 = FT2->getTemplateParameters();
+ FunctionDecl *FD1 = FT1->getTemplatedDecl();
+ FunctionDecl *FD2 = FT2->getTemplatedDecl();
// C++ [temp.deduct.partial]p11:
// ... and if G has a trailing function parameter pack for which F does not
// have a corresponding parameter, and if F does not have a trailing
// function parameter pack, then F is more specialized than G.
- FunctionDecl *FD1 = FT1->getTemplatedDecl();
- FunctionDecl *FD2 = FT2->getTemplatedDecl();
unsigned NumParams1 = FD1->getNumParams();
unsigned NumParams2 = FD2->getNumParams();
bool Variadic1 = NumParams1 && FD1->parameters().back()->isParameterPack();
@@ -5229,7 +5215,67 @@
return FT1;
}
- 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.
+ 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;
+
+ {
+ // The "same type" check is performed by comparing FD1 with a FT2
+ // specialization that instantiated by FT1's injected template args.
+ ArrayRef<TemplateArgument> TemplateArgs = FT1->getInjectedTemplateArgs();
+ TemplateArgumentList TAL(TemplateArgumentList::OnStack, TemplateArgs);
+ FunctionDecl *FD2Inst = InstantiateFunctionDeclaration(FT2, &TAL, Loc);
+ for (unsigned i = 0; i < NumParams1; ++i)
+ if (FD1->getParamDecl(i)->getType().getCanonicalType() !=
+ FD2Inst->getParamDecl(i)->getType().getCanonicalType())
+ 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 &&
+ FD1->getReturnType().getCanonicalType() !=
+ FD2Inst->getReturnType().getCanonicalType())
+ 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.
@@ -5407,7 +5453,7 @@
}
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,
@@ -5421,6 +5467,52 @@
return nullptr;
}
};
+
+// The assumption is that two template argument lists have the same size.
+struct TemplateArgumentListAreEqual {
+ Sema &S;
+ TemplateArgumentListAreEqual(Sema &S) : S(S) {}
+
+ template <typename T1, typename T2,
+ std::enable_if_t<std::is_same<T1, T2>::value, bool> = true>
+ bool operator()(T1 *PS1, T2 *PS2) {
+ // Compare two Template Argument lists using
+ // AtomicConstraint::hasMatchingParameterMapping.
+ AtomicConstraint Atomic1(S, nullptr);
+ Atomic1.ParameterMapping.emplace(
+ PS1->getTemplateArgsAsWritten()->arguments());
+ AtomicConstraint Atomic2(S, nullptr);
+ Atomic2.ParameterMapping.emplace(
+ PS2->getTemplateArgsAsWritten()->arguments());
+ return Atomic1.hasMatchingParameterMapping(S.getASTContext(), Atomic2);
+ }
+
+ template <typename T1, typename T2,
+ std::enable_if_t<!std::is_same<T1, T2>::value, bool> = true>
+ bool operator()(T1 *Spec, T2 *Primary) {
+ SmallVector<TemplateArgumentLoc, 16> Args;
+ for (const TemplateArgument &TA : Primary->getInjectedTemplateArgs()) {
+ // A injected template argument for a parameter pack is
+ // TemplateArgument::Pack. However, the TemplateArgsAsWritten
+ // represents a template argument for a parameter pack as
+ // TemplateArgument::Type with PackExpansionType. Transform the former
+ // to the same TemplateArgument kind as the latter.
+ if (TA.getKind() == TemplateArgument::Pack) {
+ assert(TA.pack_size() == 1);
+ Args.emplace_back(*TA.pack_begin(), TemplateArgumentLocInfo());
+ } else {
+ Args.emplace_back(TA, TemplateArgumentLocInfo());
+ }
+ }
+
+ AtomicConstraint Atomic1(S, nullptr);
+ Atomic1.ParameterMapping.emplace(Args);
+ AtomicConstraint Atomic2(S, nullptr);
+ Atomic2.ParameterMapping.emplace(
+ Spec->getTemplateArgsAsWritten()->arguments());
+ return Atomic1.hasMatchingParameterMapping(S.getASTContext(), Atomic2);
+ }
+};
} // namespace
/// Returns the more specialized template specialization between T1/P1 and
@@ -5460,53 +5552,83 @@
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) {
- bool ClangABICompat15 = S.Context.getLangOpts().getClangABICompat() <=
- LangOptions::ClangABI::Ver15;
- if (!ClangABICompat15) {
- // Consider this a fix for CWG1432. Similar to the fix for CWG1395.
- auto *TST1 = T1->castAs<TemplateSpecializationType>();
- auto *TST2 = T2->castAs<TemplateSpecializationType>();
- if (TST1->getNumArgs()) {
- 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;
- }
+ TemplateParameterList *TPL1 = P1->getTemplateParameters();
+ TemplateParameterList *TPL2 = P2->getTemplateParameters();
+
+ bool ClangABICompat15 = S.Context.getLangOpts().getClangABICompat() <=
+ LangOptions::ClangABI::Ver15;
+ if (!ClangABICompat15) {
+ // Consider this a fix for CWG1432. Similar to the fix for CWG1395.
+ auto *TST1 = T1->castAs<TemplateSpecializationType>();
+ auto *TST2 = T2->castAs<TemplateSpecializationType>();
+ if (TST1->getNumArgs()) {
+ 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:
+
+ 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)(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
@@ -5566,17 +5688,11 @@
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());
Index: clang/lib/Sema/SemaTemplate.cpp
===================================================================
--- clang/lib/Sema/SemaTemplate.cpp
+++ clang/lib/Sema/SemaTemplate.cpp
@@ -1264,8 +1264,7 @@
// unclear in the wording right now.
DeclRefExpr *Ref =
BuildDeclRefExpr(NTTP, NTTP->getType(), VK_PRValue, NTTP->getLocation());
- if (!Ref)
- return true;
+ assert(Ref);
ExprResult ImmediatelyDeclaredConstraint = formImmediatelyDeclaredConstraint(
*this, TL.getNestedNameSpecifierLoc(), TL.getConceptNameInfo(),
TL.getNamedConcept(), TL.getLAngleLoc(), TL.getRAngleLoc(),
@@ -7503,10 +7502,10 @@
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
@@ -7710,10 +7709,10 @@
}
/// Match two template parameters within template parameter lists.
-static bool MatchTemplateParameterKind(Sema &S, NamedDecl *New, NamedDecl *Old,
- bool Complain,
- Sema::TemplateParameterListEqualKind Kind,
- SourceLocation TemplateArgLoc) {
+static bool MatchTemplateParameterKind(
+ Sema &S, NamedDecl *New, NamedDecl *Old, bool Complain,
+ Sema::TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc,
+ bool PartialOrdering) {
// Check the actual kind (type, non-type, template).
if (Old->getKind() != New->getKind()) {
if (Complain) {
@@ -7763,31 +7762,37 @@
= dyn_cast<NonTypeTemplateParmDecl>(Old)) {
NonTypeTemplateParmDecl *NewNTTP = cast<NonTypeTemplateParmDecl>(New);
+ // During partial ordering, two auto types are equivalent, ignoring their
+ // type constraints.
+ bool CompareAutoWhenPartial = PartialOrdering &&
+ OldNTTP->getType()->isUndeducedAutoType() &&
+ NewNTTP->getType()->isUndeducedAutoType();
// If we are matching a template template argument to a template
// template parameter and one of the non-type template parameter types
// is dependent, then we must wait until template instantiation time
// to actually compare the arguments.
- if (Kind != Sema::TPL_TemplateTemplateArgumentMatch ||
- (!OldNTTP->getType()->isDependentType() &&
- !NewNTTP->getType()->isDependentType()))
- if (!S.Context.hasSameType(OldNTTP->getType(), NewNTTP->getType())) {
- if (Complain) {
- unsigned NextDiag = diag::err_template_nontype_parm_different_type;
- if (TemplateArgLoc.isValid()) {
- S.Diag(TemplateArgLoc,
- diag::err_template_arg_template_params_mismatch);
- NextDiag = diag::note_template_nontype_parm_different_type;
- }
- S.Diag(NewNTTP->getLocation(), NextDiag)
- << NewNTTP->getType()
- << (Kind != Sema::TPL_TemplateMatch);
- S.Diag(OldNTTP->getLocation(),
- diag::note_template_nontype_parm_prev_declaration)
- << OldNTTP->getType();
+ bool NonDependentTypes = Kind != Sema::TPL_TemplateTemplateArgumentMatch ||
+ (!OldNTTP->getType()->isDependentType() &&
+ !NewNTTP->getType()->isDependentType());
+
+ if (!CompareAutoWhenPartial && NonDependentTypes &&
+ !S.Context.hasSameType(OldNTTP->getType(), NewNTTP->getType())) {
+ if (Complain) {
+ unsigned NextDiag = diag::err_template_nontype_parm_different_type;
+ if (TemplateArgLoc.isValid()) {
+ S.Diag(TemplateArgLoc,
+ diag::err_template_arg_template_params_mismatch);
+ NextDiag = diag::note_template_nontype_parm_different_type;
}
-
- return false;
+ S.Diag(NewNTTP->getLocation(), NextDiag)
+ << NewNTTP->getType() << (Kind != Sema::TPL_TemplateMatch);
+ S.Diag(OldNTTP->getLocation(),
+ diag::note_template_nontype_parm_prev_declaration)
+ << OldNTTP->getType();
}
+
+ return false;
+ }
}
// For template template parameters, check the template parameter types.
// The template parameter lists of template template
@@ -7801,9 +7806,10 @@
(Kind == Sema::TPL_TemplateMatch
? Sema::TPL_TemplateTemplateParmMatch
: Kind),
- TemplateArgLoc))
+ TemplateArgLoc, PartialOrdering))
return false;
- } else if (Kind != Sema::TPL_TemplateTemplateArgumentMatch) {
+ } else if (!PartialOrdering &&
+ Kind != Sema::TPL_TemplateTemplateArgumentMatch) {
const Expr *NewC = nullptr, *OldC = nullptr;
if (const auto *TC = cast<TemplateTypeParmDecl>(New)->getTypeConstraint())
NewC = TC->getImmediatelyDeclaredConstraint();
@@ -7888,7 +7894,8 @@
TemplateParameterList *Old,
bool Complain,
TemplateParameterListEqualKind Kind,
- SourceLocation TemplateArgLoc) {
+ SourceLocation TemplateArgLoc,
+ bool PartialOrdering) {
if (Old->size() != New->size() && Kind != TPL_TemplateTemplateArgumentMatch) {
if (Complain)
DiagnoseTemplateParameterListArityMismatch(*this, New, Old, Kind,
@@ -7918,8 +7925,8 @@
return false;
}
- if (!MatchTemplateParameterKind(*this, *NewParm, *OldParm, Complain,
- Kind, TemplateArgLoc))
+ if (!MatchTemplateParameterKind(*this, *NewParm, *OldParm, Complain, Kind,
+ TemplateArgLoc, PartialOrdering))
return false;
++NewParm;
@@ -7934,8 +7941,8 @@
// template parameter pack in P (ignoring whether those template
// parameters are template parameter packs).
for (; NewParm != NewParmEnd; ++NewParm) {
- if (!MatchTemplateParameterKind(*this, *NewParm, *OldParm, Complain,
- Kind, TemplateArgLoc))
+ if (!MatchTemplateParameterKind(*this, *NewParm, *OldParm, Complain, Kind,
+ TemplateArgLoc, PartialOrdering))
return false;
}
}
@@ -7949,7 +7956,7 @@
return false;
}
- if (Kind != TPL_TemplateTemplateArgumentMatch) {
+ if (!PartialOrdering && Kind != TPL_TemplateTemplateArgumentMatch) {
const Expr *NewRC = New->getRequiresClause();
const Expr *OldRC = Old->getRequiresClause();
Index: clang/lib/Sema/SemaOverload.cpp
===================================================================
--- clang/lib/Sema/SemaOverload.cpp
+++ clang/lib/Sema/SemaOverload.cpp
@@ -9661,19 +9661,13 @@
/// 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() &&
@@ -9916,15 +9910,14 @@
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) {
Index: clang/lib/Sema/SemaConcept.cpp
===================================================================
--- clang/lib/Sema/SemaConcept.cpp
+++ clang/lib/Sema/SemaConcept.cpp
@@ -816,6 +816,17 @@
// - 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 is suffice 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);
+ E = FoldE->getPattern();
+ }
+
if (LogicalBinOp BO = E) {
auto LHS = fromConstraintExpr(S, D, BO.getLHS());
if (!LHS)
Index: clang/lib/AST/DeclTemplate.cpp
===================================================================
--- clang/lib/AST/DeclTemplate.cpp
+++ clang/lib/AST/DeclTemplate.cpp
@@ -343,6 +343,22 @@
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
//===----------------------------------------------------------------------===//
@@ -393,22 +409,6 @@
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;
Index: clang/lib/AST/ASTContext.cpp
===================================================================
--- clang/lib/AST/ASTContext.cpp
+++ clang/lib/AST/ASTContext.cpp
@@ -5138,21 +5138,22 @@
Arg = TemplateArgument(ArgType);
} else if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param)) {
- QualType T =
- NTTP->getType().getNonPackExpansionType().getNonLValueExprType(*this);
+ QualType NTTPType = NTTP->getType();
+ QualType T = NTTPType.getNonPackExpansionType().getNonLValueExprType(*this);
// For class NTTPs, ensure we include the 'const' so the type matches that
// of a real template argument.
// FIXME: It would be more faithful to model this as something like an
// lvalue-to-rvalue conversion applied to a const-qualified lvalue.
if (T->isRecordType())
T.addConst();
- Expr *E = new (*this) DeclRefExpr(
- *this, NTTP, /*enclosing*/ false, T,
- Expr::getValueKindForType(NTTP->getType()), NTTP->getLocation());
+ Expr *E = new (*this)
+ DeclRefExpr(*this, NTTP, /*enclosing*/ false, T,
+ Expr::getValueKindForType(NTTPType), NTTP->getLocation());
if (NTTP->isParameterPack())
- E = new (*this) PackExpansionExpr(DependentTy, E, NTTP->getLocation(),
- None);
+ E = new (*this) PackExpansionExpr(
+ NTTPType->isUndeducedAutoType() ? NTTPType : DependentTy, E,
+ NTTP->getLocation(), None);
Arg = TemplateArgument(E);
} else {
auto *TTP = cast<TemplateTemplateParmDecl>(Param);
Index: clang/include/clang/Sema/SemaConcept.h
===================================================================
--- clang/include/clang/Sema/SemaConcept.h
+++ clang/include/clang/Sema/SemaConcept.h
@@ -43,11 +43,33 @@
return false;
for (unsigned I = 0, S = ParameterMapping->size(); I < S; ++I) {
+ const TemplateArgument &ArgA = (*ParameterMapping)[I].getArgument();
+ const TemplateArgument &ArgB = (*Other.ParameterMapping)[I].getArgument();
+ if (ArgA.getKind() == TemplateArgument::Expression &&
+ ArgB.getKind() == TemplateArgument::Expression &&
+ ArgA.getAsExpr()->getType()->isUndeducedAutoType() &&
+ ArgB.getAsExpr()->getType()->isUndeducedAutoType())
+ continue;
+
+ if (ArgA.getKind() == TemplateArgument::Type &&
+ ArgB.getKind() == TemplateArgument::Type)
+ if (const auto *SubstA =
+ ArgA.getAsType()->getAs<SubstTemplateTypeParmType>())
+ if (const auto *SubstB =
+ ArgB.getAsType()->getAs<SubstTemplateTypeParmType>()) {
+ QualType ReplacementA = SubstA->getReplacementType();
+ QualType ReplacementB = SubstB->getReplacementType();
+ if (ReplacementA->isDecltypeType() &&
+ ReplacementB->isDecltypeType()) {
+ assert(ReplacementA->isDependentType() &&
+ ReplacementB->isDependentType());
+ continue;
+ }
+ }
+
llvm::FoldingSetNodeID IDA, IDB;
- C.getCanonicalTemplateArgument((*ParameterMapping)[I].getArgument())
- .Profile(IDA, C);
- C.getCanonicalTemplateArgument((*Other.ParameterMapping)[I].getArgument())
- .Profile(IDB, C);
+ C.getCanonicalTemplateArgument(ArgA).Profile(IDA, C);
+ C.getCanonicalTemplateArgument(ArgB).Profile(IDB, C);
if (IDA != IDB)
return false;
}
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -8208,12 +8208,11 @@
TPL_TemplateTemplateArgumentMatch
};
- bool TemplateParameterListsAreEqual(TemplateParameterList *New,
- TemplateParameterList *Old,
- bool Complain,
- TemplateParameterListEqualKind Kind,
- SourceLocation TemplateArgLoc
- = SourceLocation());
+ bool TemplateParameterListsAreEqual(
+ TemplateParameterList *New, TemplateParameterList *Old, bool Complain,
+ TemplateParameterListEqualKind Kind,
+ SourceLocation TemplateArgLoc = SourceLocation(),
+ bool PartialOrdering = false);
bool CheckTemplateDeclScope(Scope *S, TemplateParameterList *TemplateParams);
@@ -8904,8 +8903,7 @@
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,
Index: clang/include/clang/AST/DeclTemplate.h
===================================================================
--- clang/include/clang/AST/DeclTemplate.h
+++ clang/include/clang/AST/DeclTemplate.h
@@ -845,6 +845,15 @@
/// 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
@@ -952,6 +961,14 @@
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;
@@ -996,15 +1013,6 @@
/// 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;
};
@@ -1104,15 +1112,6 @@
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 {
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -191,6 +191,8 @@
and `DR1734 <https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1734>`_.
- Class member variables are now in scope when parsing a ``requires`` clause. Fixes
`GH55216 <https://github.com/llvm/llvm-project/issues/55216>`_.
+- Implemented `P2113R0: Proposed resolution for 2019 comment CA 112 <https://wg21.link/P2113R0>`_
+ ([temp.func.order]p6.2.1 is not implemented, matching GCC).
- Correctly set expression evaluation context as 'immediate function context' in
consteval functions.
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits