Skip to content

Commit 25ca9b4

Browse files
committed
[Sema] Compare canonical conversion function
With lazy template loading, it is possible to find non-canonical FunctionDecls, depending on when redecl chains are completed. This is a problem for templated conversion operators that would allow to call either the copy assignment or the move assignment operator. This ambiguity is resolved by isBetterReferenceBindingKind (called from CompareStandardConversionSequences) ranking rvalue refs over lvalue refs. Unfortunately, this fix is hard to test in isolation without the changes in #133057 that make lazy template loading more likely to complete redecl chains at "inconvenient" times. The added reproducer passes before and after this commit, but would have failed with the proposed changes of the linked PR. Kudos to Maksim Ivanov for providing an initial version of the reproducer that I further simplified.
1 parent 6aafe65 commit 25ca9b4

File tree

2 files changed

+150
-2
lines changed

2 files changed

+150
-2
lines changed

clang/lib/Sema/SemaOverload.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4404,14 +4404,19 @@ CompareImplicitConversionSequences(Sema &S, SourceLocation Loc,
44044404
Result = CompareStandardConversionSequences(S, Loc,
44054405
ICS1.Standard, ICS2.Standard);
44064406
else if (ICS1.isUserDefined()) {
4407+
auto ConvFunc1 = ICS1.UserDefined.ConversionFunction;
4408+
if (ConvFunc1)
4409+
ConvFunc1 = ConvFunc1->getCanonicalDecl();
4410+
auto ConvFunc2 = ICS2.UserDefined.ConversionFunction;
4411+
if (ConvFunc2)
4412+
ConvFunc2 = ConvFunc2->getCanonicalDecl();
44074413
// User-defined conversion sequence U1 is a better conversion
44084414
// sequence than another user-defined conversion sequence U2 if
44094415
// they contain the same user-defined conversion function or
44104416
// constructor and if the second standard conversion sequence of
44114417
// U1 is better than the second standard conversion sequence of
44124418
// U2 (C++ 13.3.3.2p3).
4413-
if (ICS1.UserDefined.ConversionFunction ==
4414-
ICS2.UserDefined.ConversionFunction)
4419+
if (ConvFunc1 == ConvFunc2)
44154420
Result = CompareStandardConversionSequences(S, Loc,
44164421
ICS1.UserDefined.After,
44174422
ICS2.UserDefined.After);

clang/test/Modules/pr133057.cpp

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
// RUN: rm -rf %t
2+
// RUN: mkdir -p %t
3+
// RUN: split-file %s %t
4+
//
5+
// RUN: %clang_cc1 -xc++ -std=c++20 -emit-module -fmodule-name=hf -fno-cxx-modules -fmodules -fno-implicit-modules %t/CMO.cppmap -o %t/WI9.pcm
6+
// RUN: %clang_cc1 -xc++ -std=c++20 -emit-module -fmodule-name=g -fno-cxx-modules -fmodules -fno-implicit-modules -fmodule-file=%t/WI9.pcm %t/E6H.cppmap -o %t/4BK.pcm
7+
// RUN: %clang_cc1 -xc++ -std=c++20 -emit-module -fmodule-name=r -fno-cxx-modules -fmodules -fno-implicit-modules -fmodule-file=%t/WI9.pcm %t/HMT.cppmap -o %t/LUM.pcm
8+
// RUN: %clang_cc1 -xc++ -std=c++20 -emit-module -fmodule-name=q -fno-cxx-modules -fmodules -fno-implicit-modules -fmodule-file=%t/LUM.pcm -fmodule-file=%t/4BK.pcm %t/JOV.cppmap -o %t/9VX.pcm
9+
// RUN: %clang_cc1 -xc++ -std=c++20 -verify -fsyntax-only -fno-cxx-modules -fmodules -fno-implicit-modules -fmodule-file=%t/9VX.pcm %t/XFD.cc
10+
11+
//--- 2OT.h
12+
#include "LQ1.h"
13+
14+
namespace ciy {
15+
namespace xqk {
16+
template <typename>
17+
class vum {
18+
public:
19+
using sc = std::C::wmd;
20+
friend bool operator==(vum, vum);
21+
};
22+
template <typename>
23+
class me {
24+
public:
25+
using vbh = vum<me>;
26+
using sc = std::C::vy<vbh>::sc;
27+
template <typename db>
28+
operator db() { return {}; }
29+
};
30+
} // namespace xqk
31+
template <typename vus>
32+
xqk::me<vus> uvo(std::C::wmd, vus);
33+
} // namespace ciy
34+
35+
class ua {
36+
std::C::wmd kij() {
37+
ciy::uvo(kij(), '-');
38+
return {};
39+
}
40+
};
41+
42+
//--- 9KF.h
43+
#include "LQ1.h"
44+
#include "2OT.h"
45+
namespace {
46+
void al(std::C::wmd lou) { std::C::jv<std::C::wmd> yt = ciy::uvo(lou, '/'); }
47+
} // namespace
48+
49+
//--- CMO.cppmap
50+
module "hf" {
51+
header "LQ1.h"
52+
}
53+
54+
55+
//--- E6H.cppmap
56+
module "g" {
57+
export *
58+
header "2OT.h"
59+
}
60+
61+
62+
//--- HMT.cppmap
63+
module "r" {
64+
header "2OT.h"
65+
}
66+
67+
68+
//--- JOV.cppmap
69+
module "q" {
70+
header "9KF.h"
71+
}
72+
73+
74+
//--- LQ1.h
75+
namespace std {
76+
namespace C {
77+
template <class zd>
78+
struct vy : zd {};
79+
template <class ub>
80+
struct vy<ub*> {
81+
typedef ub jz;
82+
};
83+
struct wmd {};
84+
template <class uo, class zt>
85+
void sk(uo k, zt gf) {
86+
(void)(k != gf);
87+
}
88+
template <class uo>
89+
class fm {
90+
public:
91+
fm(uo);
92+
};
93+
template <class kj, class kju>
94+
bool operator==(kj, kju);
95+
template <class epn>
96+
void afm(epn) {
97+
using yp = vy<epn>;
98+
if (__is_trivially_copyable(yp)) {
99+
sk(fm(epn()), nullptr);
100+
}
101+
}
102+
template <class ub>
103+
class jv {
104+
public:
105+
constexpr void gq();
106+
ub *nef;
107+
};
108+
template <class ub>
109+
constexpr void jv<ub>::gq() {
110+
afm(nef);
111+
}
112+
} // namespace C
113+
} // namespace std
114+
namespace ciy {
115+
} // namespace ciy
116+
117+
//--- XFD.cc
118+
// expected-no-diagnostics
119+
#include "LQ1.h"
120+
#include "2OT.h"
121+
class wiy {
122+
public:
123+
std::C::wmd eyb();
124+
};
125+
template <typename wpa>
126+
void i(wpa fg) {
127+
std::C::jv<std::C::wmd> zs;
128+
zs = ciy::uvo(fg.eyb(), '\n');
129+
}
130+
namespace ciy {
131+
namespace xqk {
132+
struct sbv;
133+
std::C::jv<sbv> ns() {
134+
std::C::jv<sbv> ubs;
135+
ubs.gq();
136+
return ubs;
137+
}
138+
} // namespace xqk
139+
} // namespace ciy
140+
void s() {
141+
wiy fg;
142+
i(fg);
143+
}

0 commit comments

Comments
 (0)