Skip to content

Commit facc57f

Browse files
authored
[Clang][RFC] Bypass TAD during overload resolution if a perfect match exists (#133426)
This implements the same overload resolution behavior as GCC, as described in https://wg21.link/p3606 (sections 1-2, not 3) If, during overload resolution, a non-template candidate is always picked because each argument is a perfect match (i.e., the source and target types are the same), we do not perform deduction for any template candidate that might exist. The goal is to be able to merge #122423 without being too disruptive. This change means that the selection of the best viable candidate and template argument deduction become interleaved. To avoid rewriting half of Clang, we store in `OverloadCandidateSet` enough information to deduce template candidates from `OverloadCandidateSet::BestViableFunction`. This means the lifetime of any object used by the template argument must outlive a call to `Add*Template*Candidate`. This two-phase resolution is not performed for some initialization as there are cases where template candidates are a better match per the standard. It's also bypassed for code completion. The change has a nice impact on compile times https://llvm-compile-time-tracker.com/compare.php?from=edc22c64e527171041876f26a491bb1d03d905d5&to=8170b860bd4b70917005796c05a9be013a95abb2&stat=instructions%3Au Fixes #62096 Fixes #74581 Fixes #53454
1 parent d88a3a3 commit facc57f

12 files changed

+910
-217
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,12 @@ C++ Language Changes
9696
asm((std::string_view("nop")) ::: (std::string_view("memory")));
9797
}
9898

99+
- Clang now implements the changes to overload resolution proposed by section 1 and 2 of
100+
`P3606 <https://wg21.link/P3606R0>`_. If a non-template candidate exists in an overload set that is
101+
a perfect match (all conversion sequences are identity conversions) template candiates are not instantiated.
102+
Diagnostics that would have resulted from the instantiation of these template candidates are no longer
103+
produced. This aligns Clang closer to the behavior of GCC, and fixes (#GH62096), (#GH74581), and (#GH74581).
104+
99105
C++2c Feature Support
100106
^^^^^^^^^^^^^^^^^^^^^
101107

clang/include/clang/Sema/Overload.h

Lines changed: 215 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,24 @@ class Sema;
407407
Third == ICK_Identity;
408408
}
409409

410+
/// A conversion sequence is perfect if it is an identity conversion and
411+
/// the type of the source is the same as the type of the target.
412+
bool isPerfect(const ASTContext &C) const {
413+
if (!isIdentityConversion())
414+
return false;
415+
// If we are not performing a reference binding, we can skip comparing
416+
// the types, which has a noticeable performance impact.
417+
if (!ReferenceBinding) {
418+
assert(First || C.hasSameUnqualifiedType(getFromType(), getToType(2)));
419+
return true;
420+
}
421+
if (!C.hasSameType(getFromType(), getToType(2)))
422+
return false;
423+
if (BindsToRvalue && IsLvalueReference)
424+
return false;
425+
return true;
426+
}
427+
410428
ImplicitConversionRank getRank() const;
411429
NarrowingKind
412430
getNarrowingKind(ASTContext &Context, const Expr *Converted,
@@ -743,6 +761,12 @@ class Sema;
743761
Standard.setAllToTypes(T);
744762
}
745763

764+
/// A conversion sequence is perfect if it is an identity conversion and
765+
/// the type of the source is the same as the type of the target.
766+
bool isPerfect(const ASTContext &C) const {
767+
return isStandard() && Standard.isPerfect(C);
768+
}
769+
746770
// True iff this is a conversion sequence from an initializer list to an
747771
// array or std::initializer.
748772
bool hasInitializerListContainerType() const {
@@ -979,6 +1003,20 @@ class Sema;
9791003
return false;
9801004
}
9811005

1006+
// An overload is a perfect match if the conversion
1007+
// sequences for each argument are perfect.
1008+
bool isPerfectMatch(const ASTContext &Ctx) const {
1009+
if (!Viable)
1010+
return false;
1011+
for (const auto &C : Conversions) {
1012+
if (!C.isInitialized() || !C.isPerfect(Ctx))
1013+
return false;
1014+
}
1015+
if (isa_and_nonnull<CXXConversionDecl>(Function))
1016+
return FinalConversion.isPerfect(Ctx);
1017+
return true;
1018+
}
1019+
9821020
bool TryToFixBadConversion(unsigned Idx, Sema &S) {
9831021
bool CanFix = Fix.tryToFixConversion(
9841022
Conversions[Idx].Bad.FromExpr,
@@ -1015,6 +1053,65 @@ class Sema;
10151053
RewriteKind(CRK_None) {}
10161054
};
10171055

1056+
struct DeferredTemplateOverloadCandidate {
1057+
1058+
// intrusive linked list support for allocateDeferredCandidate
1059+
DeferredTemplateOverloadCandidate *Next = nullptr;
1060+
1061+
enum Kind { Function, Method, Conversion };
1062+
1063+
LLVM_PREFERRED_TYPE(Kind)
1064+
unsigned Kind : 2;
1065+
LLVM_PREFERRED_TYPE(bool)
1066+
unsigned AllowObjCConversionOnExplicit : 1;
1067+
LLVM_PREFERRED_TYPE(bool)
1068+
unsigned AllowResultConversion : 1;
1069+
LLVM_PREFERRED_TYPE(bool)
1070+
unsigned AllowExplicit : 1;
1071+
LLVM_PREFERRED_TYPE(bool)
1072+
unsigned SuppressUserConversions : 1;
1073+
LLVM_PREFERRED_TYPE(bool)
1074+
unsigned PartialOverloading : 1;
1075+
LLVM_PREFERRED_TYPE(bool)
1076+
unsigned AggregateCandidateDeduction : 1;
1077+
};
1078+
1079+
struct DeferredFunctionTemplateOverloadCandidate
1080+
: public DeferredTemplateOverloadCandidate {
1081+
FunctionTemplateDecl *FunctionTemplate;
1082+
DeclAccessPair FoundDecl;
1083+
ArrayRef<Expr *> Args;
1084+
CallExpr::ADLCallKind IsADLCandidate;
1085+
OverloadCandidateParamOrder PO;
1086+
};
1087+
static_assert(std::is_trivially_destructible_v<
1088+
DeferredFunctionTemplateOverloadCandidate>);
1089+
1090+
struct DeferredMethodTemplateOverloadCandidate
1091+
: public DeferredTemplateOverloadCandidate {
1092+
FunctionTemplateDecl *FunctionTemplate;
1093+
DeclAccessPair FoundDecl;
1094+
ArrayRef<Expr *> Args;
1095+
CXXRecordDecl *ActingContext;
1096+
Expr::Classification ObjectClassification;
1097+
QualType ObjectType;
1098+
OverloadCandidateParamOrder PO;
1099+
};
1100+
static_assert(std::is_trivially_destructible_v<
1101+
DeferredMethodTemplateOverloadCandidate>);
1102+
1103+
struct DeferredConversionTemplateOverloadCandidate
1104+
: public DeferredTemplateOverloadCandidate {
1105+
FunctionTemplateDecl *FunctionTemplate;
1106+
DeclAccessPair FoundDecl;
1107+
CXXRecordDecl *ActingContext;
1108+
Expr *From;
1109+
QualType ToType;
1110+
};
1111+
1112+
static_assert(std::is_trivially_destructible_v<
1113+
DeferredConversionTemplateOverloadCandidate>);
1114+
10181115
/// OverloadCandidateSet - A set of overload candidates, used in C++
10191116
/// overload resolution (C++ 13.3).
10201117
class OverloadCandidateSet {
@@ -1043,6 +1140,11 @@ class Sema;
10431140
/// C++ [over.match.call.general]
10441141
/// Resolve a call through the address of an overload set.
10451142
CSK_AddressOfOverloadSet,
1143+
1144+
/// When doing overload resolution during code completion,
1145+
/// we want to show all viable candidates, including otherwise
1146+
/// deferred template candidates.
1147+
CSK_CodeCompletion,
10461148
};
10471149

10481150
/// Information about operator rewrites to consider when adding operator
@@ -1117,16 +1219,27 @@ class Sema;
11171219
SmallVector<OverloadCandidate, 16> Candidates;
11181220
llvm::SmallPtrSet<uintptr_t, 16> Functions;
11191221

1120-
// Allocator for ConversionSequenceLists. We store the first few of these
1222+
DeferredTemplateOverloadCandidate *FirstDeferredCandidate = nullptr;
1223+
unsigned DeferredCandidatesCount : 8 * sizeof(unsigned) - 2;
1224+
LLVM_PREFERRED_TYPE(bool)
1225+
unsigned HasDeferredTemplateConstructors : 1;
1226+
LLVM_PREFERRED_TYPE(bool)
1227+
unsigned ResolutionByPerfectCandidateIsDisabled : 1;
1228+
1229+
// Allocator for ConversionSequenceLists and deferred candidate args.
1230+
// We store the first few of these
11211231
// inline to avoid allocation for small sets.
11221232
llvm::BumpPtrAllocator SlabAllocator;
11231233

11241234
SourceLocation Loc;
11251235
CandidateSetKind Kind;
11261236
OperatorRewriteInfo RewriteInfo;
11271237

1238+
/// Small storage size for ImplicitConversionSequences
1239+
/// and the persisted arguments of deferred candidates.
11281240
constexpr static unsigned NumInlineBytes =
1129-
24 * sizeof(ImplicitConversionSequence);
1241+
32 * sizeof(ImplicitConversionSequence);
1242+
11301243
unsigned NumInlineBytesUsed = 0;
11311244
alignas(void *) char InlineSpace[NumInlineBytes];
11321245

@@ -1137,15 +1250,13 @@ class Sema;
11371250
/// from the slab allocator.
11381251
/// FIXME: It would probably be nice to have a SmallBumpPtrAllocator
11391252
/// instead.
1140-
/// FIXME: Now that this only allocates ImplicitConversionSequences, do we
1141-
/// want to un-generalize this?
11421253
template <typename T>
11431254
T *slabAllocate(unsigned N) {
11441255
// It's simpler if this doesn't need to consider alignment.
11451256
static_assert(alignof(T) == alignof(void *),
11461257
"Only works for pointer-aligned types.");
1147-
static_assert(std::is_trivial<T>::value ||
1148-
std::is_same<ImplicitConversionSequence, T>::value,
1258+
static_assert(std::is_trivially_destructible_v<T> ||
1259+
(std::is_same_v<ImplicitConversionSequence, T>),
11491260
"Add destruction logic to OverloadCandidateSet::clear().");
11501261

11511262
unsigned NBytes = sizeof(T) * N;
@@ -1159,12 +1270,34 @@ class Sema;
11591270
return reinterpret_cast<T *>(FreeSpaceStart);
11601271
}
11611272

1273+
// Because the size of OverloadCandidateSet has a noticeable impact on
1274+
// performance, we store each deferred template candidate in the slab
1275+
// allocator such that deferred candidates are ultimately a singly-linked
1276+
// intrusive linked list. This ends up being much more efficient than a
1277+
// SmallVector that is empty in the common case.
1278+
template <typename T> T *allocateDeferredCandidate() {
1279+
T *C = slabAllocate<T>(1);
1280+
if (!FirstDeferredCandidate)
1281+
FirstDeferredCandidate = C;
1282+
else {
1283+
auto *F = FirstDeferredCandidate;
1284+
while (F->Next)
1285+
F = F->Next;
1286+
F->Next = C;
1287+
}
1288+
DeferredCandidatesCount++;
1289+
return C;
1290+
}
1291+
11621292
void destroyCandidates();
11631293

11641294
public:
11651295
OverloadCandidateSet(SourceLocation Loc, CandidateSetKind CSK,
11661296
OperatorRewriteInfo RewriteInfo = {})
1167-
: Loc(Loc), Kind(CSK), RewriteInfo(RewriteInfo) {}
1297+
: FirstDeferredCandidate(nullptr), DeferredCandidatesCount(0),
1298+
HasDeferredTemplateConstructors(false),
1299+
ResolutionByPerfectCandidateIsDisabled(false), Loc(Loc), Kind(CSK),
1300+
RewriteInfo(RewriteInfo) {}
11681301
OverloadCandidateSet(const OverloadCandidateSet &) = delete;
11691302
OverloadCandidateSet &operator=(const OverloadCandidateSet &) = delete;
11701303
~OverloadCandidateSet() { destroyCandidates(); }
@@ -1176,6 +1309,9 @@ class Sema;
11761309
/// Whether diagnostics should be deferred.
11771310
bool shouldDeferDiags(Sema &S, ArrayRef<Expr *> Args, SourceLocation OpLoc);
11781311

1312+
// Whether the resolution of template candidates should be deferred
1313+
bool shouldDeferTemplateArgumentDeduction(const LangOptions &Opts) const;
1314+
11791315
/// Determine when this overload candidate will be new to the
11801316
/// overload set.
11811317
bool isNewCandidate(Decl *F, OverloadCandidateParamOrder PO =
@@ -1199,8 +1335,10 @@ class Sema;
11991335
iterator begin() { return Candidates.begin(); }
12001336
iterator end() { return Candidates.end(); }
12011337

1202-
size_t size() const { return Candidates.size(); }
1203-
bool empty() const { return Candidates.empty(); }
1338+
size_t size() const { return Candidates.size() + DeferredCandidatesCount; }
1339+
bool empty() const {
1340+
return Candidates.empty() && DeferredCandidatesCount == 0;
1341+
}
12041342

12051343
/// Allocate storage for conversion sequences for NumConversions
12061344
/// conversions.
@@ -1216,6 +1354,24 @@ class Sema;
12161354
return ConversionSequenceList(Conversions, NumConversions);
12171355
}
12181356

1357+
/// Provide storage for any Expr* arg that must be preserved
1358+
/// until deferred template candidates are deduced.
1359+
/// Typically this should be used for reversed operator arguments
1360+
/// and any time the argument array is transformed while adding
1361+
/// a template candidate.
1362+
llvm::MutableArrayRef<Expr *> getPersistentArgsArray(unsigned N) {
1363+
Expr **Exprs = slabAllocate<Expr *>(N);
1364+
return llvm::MutableArrayRef<Expr *>(Exprs, N);
1365+
}
1366+
1367+
template <typename... T>
1368+
llvm::MutableArrayRef<Expr *> getPersistentArgsArray(T *...Exprs) {
1369+
llvm::MutableArrayRef<Expr *> Arr =
1370+
getPersistentArgsArray(sizeof...(Exprs));
1371+
llvm::copy(std::initializer_list<Expr *>{Exprs...}, Arr.data());
1372+
return Arr;
1373+
}
1374+
12191375
/// Add a new candidate with NumConversions conversion sequence slots
12201376
/// to the overload set.
12211377
OverloadCandidate &addCandidate(unsigned NumConversions = 0,
@@ -1231,6 +1387,32 @@ class Sema;
12311387
return C;
12321388
}
12331389

1390+
void AddDeferredTemplateCandidate(
1391+
FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
1392+
ArrayRef<Expr *> Args, bool SuppressUserConversions,
1393+
bool PartialOverloading, bool AllowExplicit,
1394+
CallExpr::ADLCallKind IsADLCandidate, OverloadCandidateParamOrder PO,
1395+
bool AggregateCandidateDeduction);
1396+
1397+
void AddDeferredMethodTemplateCandidate(
1398+
FunctionTemplateDecl *MethodTmpl, DeclAccessPair FoundDecl,
1399+
CXXRecordDecl *ActingContext, QualType ObjectType,
1400+
Expr::Classification ObjectClassification, ArrayRef<Expr *> Args,
1401+
bool SuppressUserConversions, bool PartialOverloading,
1402+
OverloadCandidateParamOrder PO);
1403+
1404+
void AddDeferredConversionTemplateCandidate(
1405+
FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
1406+
CXXRecordDecl *ActingContext, Expr *From, QualType ToType,
1407+
bool AllowObjCConversionOnExplicit, bool AllowExplicit,
1408+
bool AllowResultConversion);
1409+
1410+
void InjectNonDeducedTemplateCandidates(Sema &S);
1411+
1412+
void DisableResolutionByPerfectCandidate() {
1413+
ResolutionByPerfectCandidateIsDisabled = true;
1414+
}
1415+
12341416
/// Find the best viable function on this overload set, if it exists.
12351417
OverloadingResult BestViableFunction(Sema &S, SourceLocation Loc,
12361418
OverloadCandidateSet::iterator& Best);
@@ -1263,6 +1445,15 @@ class Sema;
12631445
DestAS = AS;
12641446
}
12651447

1448+
private:
1449+
OverloadingResult ResultForBestCandidate(const iterator &Best);
1450+
void CudaExcludeWrongSideCandidates(
1451+
Sema &S, SmallVectorImpl<OverloadCandidate *> &Candidates);
1452+
OverloadingResult
1453+
BestViableFunctionImpl(Sema &S, SourceLocation Loc,
1454+
OverloadCandidateSet::iterator &Best);
1455+
void PerfectViableFunction(Sema &S, SourceLocation Loc,
1456+
OverloadCandidateSet::iterator &Best);
12661457
};
12671458

12681459
bool isBetterOverloadCandidate(Sema &S, const OverloadCandidate &Cand1,
@@ -1311,6 +1502,21 @@ class Sema;
13111502
// parameter.
13121503
bool shouldEnforceArgLimit(bool PartialOverloading, FunctionDecl *Function);
13131504

1505+
inline bool OverloadCandidateSet::shouldDeferTemplateArgumentDeduction(
1506+
const LangOptions &Opts) const {
1507+
return
1508+
// For user defined conversion we need to check against different
1509+
// combination of CV qualifiers and look at any explicit specifier, so
1510+
// always deduce template candidates.
1511+
Kind != CSK_InitByUserDefinedConversion
1512+
// When doing code completion, we want to see all the
1513+
// viable candidates.
1514+
&& Kind != CSK_CodeCompletion
1515+
// CUDA may prefer template candidates even when a non-candidate
1516+
// is a perfect match
1517+
&& !Opts.CUDA;
1518+
}
1519+
13141520
} // namespace clang
13151521

13161522
#endif // LLVM_CLANG_SEMA_OVERLOAD_H

clang/lib/Sema/SemaCodeComplete.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6354,7 +6354,8 @@ SemaCodeCompletion::ProduceCallSignatureHelp(Expr *Fn, ArrayRef<Expr *> Args,
63546354
Expr *NakedFn = Fn->IgnoreParenCasts();
63556355
// Build an overload candidate set based on the functions we find.
63566356
SourceLocation Loc = Fn->getExprLoc();
6357-
OverloadCandidateSet CandidateSet(Loc, OverloadCandidateSet::CSK_Normal);
6357+
OverloadCandidateSet CandidateSet(Loc,
6358+
OverloadCandidateSet::CSK_CodeCompletion);
63586359

63596360
if (auto ULE = dyn_cast<UnresolvedLookupExpr>(NakedFn)) {
63606361
SemaRef.AddOverloadedCallCandidates(ULE, ArgsWithoutDependentTypes,
@@ -6557,7 +6558,8 @@ QualType SemaCodeCompletion::ProduceConstructorSignatureHelp(
65576558
// FIXME: Provide support for variadic template constructors.
65586559

65596560
if (CRD) {
6560-
OverloadCandidateSet CandidateSet(Loc, OverloadCandidateSet::CSK_Normal);
6561+
OverloadCandidateSet CandidateSet(Loc,
6562+
OverloadCandidateSet::CSK_CodeCompletion);
65616563
for (NamedDecl *C : SemaRef.LookupConstructors(CRD)) {
65626564
if (auto *FD = dyn_cast<FunctionDecl>(C)) {
65636565
// FIXME: we can't yet provide correct signature help for initializer

clang/lib/Sema/SemaInit.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10029,12 +10029,19 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
1002910029
// When [...] the constructor [...] is a candidate by
1003010030
// - [over.match.copy] (in all cases)
1003110031
if (TD) {
10032-
SmallVector<Expr *, 8> TmpInits;
10033-
for (Expr *E : Inits)
10032+
10033+
// As template candidates are not deduced immediately,
10034+
// persist the array in the overload set.
10035+
MutableArrayRef<Expr *> TmpInits =
10036+
Candidates.getPersistentArgsArray(Inits.size());
10037+
10038+
for (auto [I, E] : llvm::enumerate(Inits)) {
1003410039
if (auto *DI = dyn_cast<DesignatedInitExpr>(E))
10035-
TmpInits.push_back(DI->getInit());
10040+
TmpInits[I] = DI->getInit();
1003610041
else
10037-
TmpInits.push_back(E);
10042+
TmpInits[I] = E;
10043+
}
10044+
1003810045
AddTemplateOverloadCandidate(
1003910046
TD, FoundDecl, /*ExplicitArgs=*/nullptr, TmpInits, Candidates,
1004010047
/*SuppressUserConversions=*/false,

0 commit comments

Comments
 (0)