Skip to content

Commit 299be61

Browse files
authored
[clang] Fix CodeComplete crash involving CWG1432 (#129436)
This skips the provisional resolution of CWG1432 just when ordering the candidates for function call code completion, as otherwise this breaks some assumptions the implementation makes about how closely related the candidates are. As a drive-by, deduplicate the implementation with the one used for class template partial ordering, and strenghten an assertion which was previosuly dependent on the order of candidates. Also add a test for the fix for CWG1432 when partial ordering function templates, which was otherwise untested. Fixes #125500
1 parent 4396237 commit 299be61

File tree

8 files changed

+131
-65
lines changed

8 files changed

+131
-65
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ related warnings within the method body.
165165
print_status("%s (%#08x)\n");
166166
// order of %s and %x is swapped but there is no diagnostic
167167
}
168-
168+
169169
Before the introducion of ``format_matches``, this code cannot be verified
170170
at compile-time. ``format_matches`` plugs that hole:
171171

@@ -233,6 +233,8 @@ Bug Fixes in This Version
233233
signed char values (#GH102798).
234234
- Fixed rejects-valid problem when #embed appears in std::initializer_list or
235235
when it can affect template argument deduction (#GH122306).
236+
- Fix crash on code completion of function calls involving partial order of function templates
237+
(#GH125500).
236238

237239
Bug Fixes to Compiler Builtins
238240
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/Sema/Overload.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1265,11 +1265,11 @@ class Sema;
12651265

12661266
};
12671267

1268-
bool isBetterOverloadCandidate(Sema &S,
1269-
const OverloadCandidate &Cand1,
1268+
bool isBetterOverloadCandidate(Sema &S, const OverloadCandidate &Cand1,
12701269
const OverloadCandidate &Cand2,
12711270
SourceLocation Loc,
1272-
OverloadCandidateSet::CandidateSetKind Kind);
1271+
OverloadCandidateSet::CandidateSetKind Kind,
1272+
bool PartialOverloading = false);
12731273

12741274
struct ConstructorInfo {
12751275
DeclAccessPair FoundDecl;

clang/include/clang/Sema/Sema.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12617,7 +12617,8 @@ class Sema final : public SemaBase {
1261712617
FunctionTemplateDecl *getMoreSpecializedTemplate(
1261812618
FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc,
1261912619
TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1,
12620-
QualType RawObj1Ty = {}, QualType RawObj2Ty = {}, bool Reversed = false);
12620+
QualType RawObj1Ty = {}, QualType RawObj2Ty = {}, bool Reversed = false,
12621+
bool PartialOverloading = false);
1262112622

1262212623
/// Retrieve the most specialized of the given function template
1262312624
/// specializations.

clang/lib/Sema/SemaCodeComplete.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6229,8 +6229,8 @@ static void mergeCandidatesWithResults(
62296229
// Sort the overload candidate set by placing the best overloads first.
62306230
llvm::stable_sort(CandidateSet, [&](const OverloadCandidate &X,
62316231
const OverloadCandidate &Y) {
6232-
return isBetterOverloadCandidate(SemaRef, X, Y, Loc,
6233-
CandidateSet.getKind());
6232+
return isBetterOverloadCandidate(SemaRef, X, Y, Loc, CandidateSet.getKind(),
6233+
/*PartialOverloading=*/true);
62346234
});
62356235

62366236
// Add the remaining viable overload candidates as code-completion results.

clang/lib/Sema/SemaOverload.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10429,7 +10429,8 @@ getMorePartialOrderingConstrained(Sema &S, FunctionDecl *Fn1, FunctionDecl *Fn2,
1042910429
/// candidate is a better candidate than the second (C++ 13.3.3p1).
1043010430
bool clang::isBetterOverloadCandidate(
1043110431
Sema &S, const OverloadCandidate &Cand1, const OverloadCandidate &Cand2,
10432-
SourceLocation Loc, OverloadCandidateSet::CandidateSetKind Kind) {
10432+
SourceLocation Loc, OverloadCandidateSet::CandidateSetKind Kind,
10433+
bool PartialOverloading) {
1043310434
// Define viable functions to be better candidates than non-viable
1043410435
// functions.
1043510436
if (!Cand2.Viable)
@@ -10666,7 +10667,7 @@ bool clang::isBetterOverloadCandidate(
1066610667
: QualType{},
1066710668
Obj2Context ? QualType(Obj2Context->getTypeForDecl(), 0)
1066810669
: QualType{},
10669-
Cand1.isReversed() ^ Cand2.isReversed())) {
10670+
Cand1.isReversed() ^ Cand2.isReversed(), PartialOverloading)) {
1067010671
return BetterTemplate == Cand1.Function->getPrimaryTemplate();
1067110672
}
1067210673
}

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 56 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -5862,10 +5862,42 @@ static bool isAtLeastAsSpecializedAs(
58625862
return true;
58635863
}
58645864

5865+
enum class MoreSpecializedTrailingPackTieBreakerResult { Equal, Less, More };
5866+
5867+
// This a speculative fix for CWG1432 (Similar to the fix for CWG1395) that
5868+
// there is no wording or even resolution for this issue.
5869+
static MoreSpecializedTrailingPackTieBreakerResult
5870+
getMoreSpecializedTrailingPackTieBreaker(
5871+
const TemplateSpecializationType *TST1,
5872+
const TemplateSpecializationType *TST2) {
5873+
ArrayRef<TemplateArgument> As1 = TST1->template_arguments(),
5874+
As2 = TST2->template_arguments();
5875+
const TemplateArgument &TA1 = As1.back(), &TA2 = As2.back();
5876+
bool IsPack = TA1.getKind() == TemplateArgument::Pack;
5877+
assert(IsPack == (TA2.getKind() == TemplateArgument::Pack));
5878+
if (!IsPack)
5879+
return MoreSpecializedTrailingPackTieBreakerResult::Equal;
5880+
assert(As1.size() == As2.size());
5881+
5882+
unsigned PackSize1 = TA1.pack_size(), PackSize2 = TA2.pack_size();
5883+
bool IsPackExpansion1 =
5884+
PackSize1 && TA1.pack_elements().back().isPackExpansion();
5885+
bool IsPackExpansion2 =
5886+
PackSize2 && TA2.pack_elements().back().isPackExpansion();
5887+
if (PackSize1 == PackSize2 && IsPackExpansion1 == IsPackExpansion2)
5888+
return MoreSpecializedTrailingPackTieBreakerResult::Equal;
5889+
if (PackSize1 > PackSize2 && IsPackExpansion1)
5890+
return MoreSpecializedTrailingPackTieBreakerResult::More;
5891+
if (PackSize1 < PackSize2 && IsPackExpansion2)
5892+
return MoreSpecializedTrailingPackTieBreakerResult::Less;
5893+
return MoreSpecializedTrailingPackTieBreakerResult::Equal;
5894+
}
5895+
58655896
FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
58665897
FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc,
58675898
TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1,
5868-
QualType RawObj1Ty, QualType RawObj2Ty, bool Reversed) {
5899+
QualType RawObj1Ty, QualType RawObj2Ty, bool Reversed,
5900+
bool PartialOverloading) {
58695901
SmallVector<QualType> Args1;
58705902
SmallVector<QualType> Args2;
58715903
const FunctionDecl *FD1 = FT1->getTemplatedDecl();
@@ -6001,34 +6033,27 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
60016033
return FT1;
60026034
}
60036035

6004-
// This a speculative fix for CWG1432 (Similar to the fix for CWG1395) that
6005-
// there is no wording or even resolution for this issue.
6006-
for (int i = 0, e = std::min(NumParams1, NumParams2); i < e; ++i) {
6036+
// Skip this tie breaker if we are performing overload resolution with partial
6037+
// arguments, as this breaks some assumptions about how closely related the
6038+
// candidates are.
6039+
for (int i = 0, e = std::min(NumParams1, NumParams2);
6040+
!PartialOverloading && i < e; ++i) {
60076041
QualType T1 = Param1[i].getCanonicalType();
60086042
QualType T2 = Param2[i].getCanonicalType();
60096043
auto *TST1 = dyn_cast<TemplateSpecializationType>(T1);
60106044
auto *TST2 = dyn_cast<TemplateSpecializationType>(T2);
60116045
if (!TST1 || !TST2)
60126046
continue;
6013-
const TemplateArgument &TA1 = TST1->template_arguments().back();
6014-
if (TA1.getKind() == TemplateArgument::Pack) {
6015-
assert(TST1->template_arguments().size() ==
6016-
TST2->template_arguments().size());
6017-
const TemplateArgument &TA2 = TST2->template_arguments().back();
6018-
assert(TA2.getKind() == TemplateArgument::Pack);
6019-
unsigned PackSize1 = TA1.pack_size();
6020-
unsigned PackSize2 = TA2.pack_size();
6021-
bool IsPackExpansion1 =
6022-
PackSize1 && TA1.pack_elements().back().isPackExpansion();
6023-
bool IsPackExpansion2 =
6024-
PackSize2 && TA2.pack_elements().back().isPackExpansion();
6025-
if (PackSize1 != PackSize2 && IsPackExpansion1 != IsPackExpansion2) {
6026-
if (PackSize1 > PackSize2 && IsPackExpansion1)
6027-
return FT2;
6028-
if (PackSize1 < PackSize2 && IsPackExpansion2)
6029-
return FT1;
6030-
}
6047+
switch (getMoreSpecializedTrailingPackTieBreaker(TST1, TST2)) {
6048+
case MoreSpecializedTrailingPackTieBreakerResult::Less:
6049+
return FT1;
6050+
case MoreSpecializedTrailingPackTieBreakerResult::More:
6051+
return FT2;
6052+
case MoreSpecializedTrailingPackTieBreakerResult::Equal:
6053+
continue;
60316054
}
6055+
llvm_unreachable(
6056+
"unknown MoreSpecializedTrailingPackTieBreakerResult value");
60326057
}
60336058

60346059
if (!Context.getLangOpts().CPlusPlus20)
@@ -6375,28 +6400,15 @@ getMoreSpecialized(Sema &S, QualType T1, QualType T2, TemplateLikeDecl *P1,
63756400
if (!Better1 && !Better2)
63766401
return nullptr;
63776402

6378-
// This a speculative fix for CWG1432 (Similar to the fix for CWG1395) that
6379-
// there is no wording or even resolution for this issue.
6380-
auto *TST1 = cast<TemplateSpecializationType>(T1);
6381-
auto *TST2 = cast<TemplateSpecializationType>(T2);
6382-
const TemplateArgument &TA1 = TST1->template_arguments().back();
6383-
if (TA1.getKind() == TemplateArgument::Pack) {
6384-
assert(TST1->template_arguments().size() ==
6385-
TST2->template_arguments().size());
6386-
const TemplateArgument &TA2 = TST2->template_arguments().back();
6387-
assert(TA2.getKind() == TemplateArgument::Pack);
6388-
unsigned PackSize1 = TA1.pack_size();
6389-
unsigned PackSize2 = TA2.pack_size();
6390-
bool IsPackExpansion1 =
6391-
PackSize1 && TA1.pack_elements().back().isPackExpansion();
6392-
bool IsPackExpansion2 =
6393-
PackSize2 && TA2.pack_elements().back().isPackExpansion();
6394-
if (PackSize1 != PackSize2 && IsPackExpansion1 != IsPackExpansion2) {
6395-
if (PackSize1 > PackSize2 && IsPackExpansion1)
6396-
return GetP2()(P1, P2);
6397-
if (PackSize1 < PackSize2 && IsPackExpansion2)
6398-
return P1;
6399-
}
6403+
switch (getMoreSpecializedTrailingPackTieBreaker(
6404+
cast<TemplateSpecializationType>(T1),
6405+
cast<TemplateSpecializationType>(T2))) {
6406+
case MoreSpecializedTrailingPackTieBreakerResult::Less:
6407+
return P1;
6408+
case MoreSpecializedTrailingPackTieBreakerResult::More:
6409+
return GetP2()(P1, P2);
6410+
case MoreSpecializedTrailingPackTieBreakerResult::Equal:
6411+
break;
64006412
}
64016413

64026414
if (!S.Context.getLangOpts().CPlusPlus20)

clang/test/CXX/drs/cwg14xx.cpp

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -59,22 +59,34 @@ namespace cwg1423 { // cwg1423: 11
5959

6060
namespace cwg1432 { // cwg1432: 16
6161
#if __cplusplus >= 201103L
62-
template<typename T> T declval();
62+
namespace class_template_partial_spec {
63+
template<typename T> T declval();
6364

64-
template <class... T>
65-
struct common_type;
65+
template <class... T>
66+
struct common_type;
6667

67-
template <class T, class U>
68-
struct common_type<T, U> {
69-
typedef decltype(true ? declval<T>() : declval<U>()) type;
70-
};
68+
template <class T, class U>
69+
struct common_type<T, U> {
70+
typedef decltype(true ? declval<T>() : declval<U>()) type;
71+
};
7172

72-
template <class T, class U, class... V>
73-
struct common_type<T, U, V...> {
74-
typedef typename common_type<typename common_type<T, U>::type, V...>::type type;
75-
};
73+
template <class T, class U, class... V>
74+
struct common_type<T, U, V...> {
75+
typedef typename common_type<typename common_type<T, U>::type, V...>::type type;
76+
};
77+
78+
template struct common_type<int, double>;
79+
} // namespace class_template_partial_spec
80+
namespace function_template {
81+
template <int I, class... Ts> struct A {};
7682

77-
template struct common_type<int, double>;
83+
template <int I, class... Ts> void f(A<I, Ts...>) = delete;
84+
template <int I> void f(A<I>);
85+
86+
void test() {
87+
f(A<0>());
88+
}
89+
} // namespace function_template
7890
#endif
7991
} // namespace cwg1432
8092

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Note: the run lines follow their respective tests, since line/column
2+
// matter in this test.
3+
4+
template <int...> struct B {};
5+
template <int> class C;
6+
7+
namespace method {
8+
struct S {
9+
template <int Z>
10+
void waldo(C<Z>);
11+
12+
template <int... Is, int Z>
13+
void waldo(B<Is...>, C<Z>);
14+
};
15+
16+
void foo() {
17+
S().waldo(/*invoke completion here*/);
18+
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):15 %s -o - | FileCheck -check-prefix=CHECK-METHOD %s
19+
// CHECK-METHOD-LABEL: OPENING_PAREN_LOC:
20+
// CHECK-METHOD-NEXT: OVERLOAD: [#void#]waldo(<#C<Z>#>)
21+
// CHECK-METHOD-NEXT: OVERLOAD: [#void#]waldo(<#B<>#>, C<Z>)
22+
}
23+
} // namespace method
24+
namespace function {
25+
template <int Z>
26+
void waldo(C<Z>);
27+
28+
template <int... Is, int Z>
29+
void waldo(B<Is...>, C<Z>);
30+
31+
void foo() {
32+
waldo(/*invoke completion here*/);
33+
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):11 %s -o - | FileCheck -check-prefix=CHECK-FUNC %s
34+
// CHECK-FUNC-LABEL: OPENING_PAREN_LOC:
35+
// CHECK-FUNC-NEXT: OVERLOAD: [#void#]waldo(<#B<>#>, C<Z>)
36+
// CHECK-FUNC-NEXT: OVERLOAD: [#void#]waldo(<#C<Z>#>)
37+
}
38+
} // namespace function

0 commit comments

Comments
 (0)