[c++] implements tentative DR1432 for partial ordering of function template

D128745 handled DR1432 for the partial ordering of partial specializations, but
missed the handling for the partial ordering of function templates. This patch
implements the latter. While at it, also simplifies the previous implementation to
be more close to the wording without functional changes.

Fixes https://github.com/llvm/llvm-project/issues/56090

Reviewed By: erichkeane, #clang-language-wg, mizvekov

Differential Revision: https://reviews.llvm.org/D133683
This commit is contained in:
Yuanfang Chen 2022-10-03 15:50:24 -07:00
parent c7652dbed4
commit 1fb728e95c
4 changed files with 84 additions and 26 deletions

View File

@ -363,7 +363,9 @@ C2x Feature Support
C++ Language Changes in Clang
-----------------------------
- Implemented DR692, DR1395 and DR1432. Use the ``-fclang-abi-compat=15`` option
to get the old partial ordering behavior regarding packs.
to get the old partial ordering behavior regarding packs. Note that the fix for
DR1432 is speculative that there is no wording or even resolution for this issue.
A speculative fix for DR1432 is needed because it fixes regressions caused by DR692.
- Clang's default C++/ObjC++ standard is now ``gnu++17`` instead of ``gnu++14``.
This means Clang will by default accept code using features from C++17 and
conforming GNU extensions. Projects incompatible with C++17 can add

View File

@ -5222,6 +5222,39 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
return FT1;
}
// 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 =
Context.getLangOpts().getClangABICompat() <= LangOptions::ClangABI::Ver15;
if (!ClangABICompat15) {
for (int i = 0, e = std::min(NumParams1, NumParams2); i < e; ++i) {
QualType T1 = FD1->getParamDecl(i)->getType().getCanonicalType();
QualType T2 = FD2->getParamDecl(i)->getType().getCanonicalType();
auto *TST1 = dyn_cast<TemplateSpecializationType>(T1);
auto *TST2 = dyn_cast<TemplateSpecializationType>(T2);
if (!TST1 || !TST2)
continue;
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 FT2;
if (PackSize1 < PackSize2 && IsPackExpansion2)
return FT1;
}
}
}
}
return JudgeByConstraints();
}
@ -5457,30 +5490,29 @@ getMoreSpecialized(Sema &S, QualType T1, QualType T2, TemplateLikeDecl *P1,
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) {
// 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;
}
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;
}
}
}

View File

@ -1083,13 +1083,23 @@ namespace dr692 { // dr692: no
// Also see dr1395.
namespace temp_func_order_example2 {
template <typename T, typename U> struct A {};
template <typename T, typename U> void f(U, A<U, T> *p = 0); // expected-note {{candidate}}
template <typename U> int &f(U, A<U, U> *p = 0); // expected-note {{candidate}}
template <typename... T> struct A1 {}; // expected-error 0-1{{C++11}}
template <typename U, typename... T> struct A2 {}; // expected-error 0-1{{C++11}}
template <class T1, class... U> void e1(A1<T1, U...>) = delete; // expected-error 0-2{{C++11}}
template <class T1> void e1(A1<T1>);
template <class T1, class... U> void e2(A2<T1, U...>) = delete; // expected-error 0-2{{C++11}}
template <class T1> void e2(A2<T1>);
template <typename T, typename U> void f(U, A1<U, T> *p = 0) = delete; // expected-note {{candidate}} expected-error 0-1{{C++11}}
template <typename U> int &f(U, A1<U, U> *p = 0); // expected-note {{candidate}}
template <typename T> void g(T, T = T()); // expected-note {{candidate}}
template <typename T, typename... U> void g(T, U...); // expected-note {{candidate}} expected-error 0-1{{C++11}}
void h() {
int &r = f<int>(42, (A<int, int> *)0);
A1<int, int> a;
int &r = f<int>(42, &a);
A1<int> b1;
e1(b1);
A2<int> b2;
e2(b2);
f<int>(42); // expected-error {{ambiguous}}
g(42); // expected-error {{ambiguous}}
}

View File

@ -0,0 +1,14 @@
// RUN: %clang_cc1 %s -std=c++11 -verify -fexceptions -fcxx-exceptions -pedantic-errors -fno-spell-checking -fclang-abi-compat=15
template <typename... T> struct A1 {};
template <typename U, typename... T> struct A2 {};
template <class T1, class... U> void e1(A1<T1, U...>); // expected-note {{candidate}}
template <class T1> void e1(A1<T1>); // expected-note {{candidate}}
template <class T1, class... U> void e2(A2<T1, U...>); // expected-note {{candidate}}
template <class T1> void e2(A2<T1>); // expected-note {{candidate}}
void h() {
A1<int> b1;
e1(b1); // expected-error{{call to 'e1' is ambiguous}}
A2<int> b2;
e2(b2); // expected-error{{call to 'e2' is ambiguous}}
}