Skip to content

Commit 8c5a307

Browse files
authored
[Clang] Bypass TAD during overload resolution if a perfect match exists (#136203)
This implements the same overload resolution behavior as GCC, as described in https://wg21.link/p3606 (section 1-2, not 3) If during overload resolution, there is a non-template candidate that would be always be picked - because each of the argument is a perfect match (ie the source and target types are the same), we do not perform deduction for any template candidate that might exists. 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 be able to deduce template candidates from OverloadCandidateSet::BestViableFunction. Which means the lifetime of any object used by 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 candidate are better match in these cases 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=719b029c16eeb1035da522fd641dfcc4cee6be74&to=bf7041045c9408490c395230047c5461de72fc39&stat=instructions%3Au Fixes #62096 Fixes #74581 Reapplies #133426
1 parent 23324b8 commit 8c5a307

12 files changed

+958
-219
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 candidates 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: 230 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,34 @@ 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+
#ifndef NDEBUG
419+
auto Decay = [&](QualType T) {
420+
return (T->isArrayType() || T->isFunctionType()) ? C.getDecayedType(T)
421+
: T;
422+
};
423+
// The types might differ if there is an array-to-pointer conversion
424+
// an function-to-pointer conversion, or lvalue-to-rvalue conversion.
425+
// In some cases, this may happen even if First is not set.
426+
assert(C.hasSameUnqualifiedType(Decay(getFromType()),
427+
Decay(getToType(2))));
428+
#endif
429+
return true;
430+
}
431+
if (!C.hasSameType(getFromType(), getToType(2)))
432+
return false;
433+
if (BindsToRvalue && IsLvalueReference)
434+
return false;
435+
return true;
436+
}
437+
410438
ImplicitConversionRank getRank() const;
411439
NarrowingKind
412440
getNarrowingKind(ASTContext &Context, const Expr *Converted,
@@ -743,6 +771,12 @@ class Sema;
743771
Standard.setAllToTypes(T);
744772
}
745773

774+
/// A conversion sequence is perfect if it is an identity conversion and
775+
/// the type of the source is the same as the type of the target.
776+
bool isPerfect(const ASTContext &C) const {
777+
return isStandard() && Standard.isPerfect(C);
778+
}
779+
746780
// True iff this is a conversion sequence from an initializer list to an
747781
// array or std::initializer.
748782
bool hasInitializerListContainerType() const {
@@ -939,6 +973,10 @@ class Sema;
939973
LLVM_PREFERRED_TYPE(CallExpr::ADLCallKind)
940974
unsigned IsADLCandidate : 1;
941975

976+
/// Whether FinalConversion has been set.
977+
LLVM_PREFERRED_TYPE(bool)
978+
unsigned HasFinalConversion : 1;
979+
942980
/// Whether this is a rewritten candidate, and if so, of what kind?
943981
LLVM_PREFERRED_TYPE(OverloadCandidateRewriteKind)
944982
unsigned RewriteKind : 2;
@@ -979,6 +1017,20 @@ class Sema;
9791017
return false;
9801018
}
9811019

1020+
// An overload is a perfect match if the conversion
1021+
// sequences for each argument are perfect.
1022+
bool isPerfectMatch(const ASTContext &Ctx) const {
1023+
if (!Viable)
1024+
return false;
1025+
for (const auto &C : Conversions) {
1026+
if (!C.isInitialized() || !C.isPerfect(Ctx))
1027+
return false;
1028+
}
1029+
if (HasFinalConversion)
1030+
return FinalConversion.isPerfect(Ctx);
1031+
return true;
1032+
}
1033+
9821034
bool TryToFixBadConversion(unsigned Idx, Sema &S) {
9831035
bool CanFix = Fix.tryToFixConversion(
9841036
Conversions[Idx].Bad.FromExpr,
@@ -1012,8 +1064,67 @@ class Sema;
10121064
: IsSurrogate(false), IgnoreObjectArgument(false),
10131065
TookAddressOfOverload(false), StrictPackMatch(false),
10141066
IsADLCandidate(llvm::to_underlying(CallExpr::NotADL)),
1015-
RewriteKind(CRK_None) {}
1067+
HasFinalConversion(false), RewriteKind(CRK_None) {}
1068+
};
1069+
1070+
struct DeferredTemplateOverloadCandidate {
1071+
1072+
// intrusive linked list support for allocateDeferredCandidate
1073+
DeferredTemplateOverloadCandidate *Next = nullptr;
1074+
1075+
enum Kind { Function, Method, Conversion };
1076+
1077+
LLVM_PREFERRED_TYPE(Kind)
1078+
unsigned Kind : 2;
1079+
LLVM_PREFERRED_TYPE(bool)
1080+
unsigned AllowObjCConversionOnExplicit : 1;
1081+
LLVM_PREFERRED_TYPE(bool)
1082+
unsigned AllowResultConversion : 1;
1083+
LLVM_PREFERRED_TYPE(bool)
1084+
unsigned AllowExplicit : 1;
1085+
LLVM_PREFERRED_TYPE(bool)
1086+
unsigned SuppressUserConversions : 1;
1087+
LLVM_PREFERRED_TYPE(bool)
1088+
unsigned PartialOverloading : 1;
1089+
LLVM_PREFERRED_TYPE(bool)
1090+
unsigned AggregateCandidateDeduction : 1;
1091+
};
1092+
1093+
struct DeferredFunctionTemplateOverloadCandidate
1094+
: public DeferredTemplateOverloadCandidate {
1095+
FunctionTemplateDecl *FunctionTemplate;
1096+
DeclAccessPair FoundDecl;
1097+
ArrayRef<Expr *> Args;
1098+
CallExpr::ADLCallKind IsADLCandidate;
1099+
OverloadCandidateParamOrder PO;
1100+
};
1101+
static_assert(std::is_trivially_destructible_v<
1102+
DeferredFunctionTemplateOverloadCandidate>);
1103+
1104+
struct DeferredMethodTemplateOverloadCandidate
1105+
: public DeferredTemplateOverloadCandidate {
1106+
FunctionTemplateDecl *FunctionTemplate;
1107+
DeclAccessPair FoundDecl;
1108+
ArrayRef<Expr *> Args;
1109+
CXXRecordDecl *ActingContext;
1110+
Expr::Classification ObjectClassification;
1111+
QualType ObjectType;
1112+
OverloadCandidateParamOrder PO;
10161113
};
1114+
static_assert(std::is_trivially_destructible_v<
1115+
DeferredMethodTemplateOverloadCandidate>);
1116+
1117+
struct DeferredConversionTemplateOverloadCandidate
1118+
: public DeferredTemplateOverloadCandidate {
1119+
FunctionTemplateDecl *FunctionTemplate;
1120+
DeclAccessPair FoundDecl;
1121+
CXXRecordDecl *ActingContext;
1122+
Expr *From;
1123+
QualType ToType;
1124+
};
1125+
1126+
static_assert(std::is_trivially_destructible_v<
1127+
DeferredConversionTemplateOverloadCandidate>);
10171128

10181129
/// OverloadCandidateSet - A set of overload candidates, used in C++
10191130
/// overload resolution (C++ 13.3).
@@ -1043,6 +1154,11 @@ class Sema;
10431154
/// C++ [over.match.call.general]
10441155
/// Resolve a call through the address of an overload set.
10451156
CSK_AddressOfOverloadSet,
1157+
1158+
/// When doing overload resolution during code completion,
1159+
/// we want to show all viable candidates, including otherwise
1160+
/// deferred template candidates.
1161+
CSK_CodeCompletion,
10461162
};
10471163

10481164
/// Information about operator rewrites to consider when adding operator
@@ -1117,16 +1233,27 @@ class Sema;
11171233
SmallVector<OverloadCandidate, 16> Candidates;
11181234
llvm::SmallPtrSet<uintptr_t, 16> Functions;
11191235

1120-
// Allocator for ConversionSequenceLists. We store the first few of these
1236+
DeferredTemplateOverloadCandidate *FirstDeferredCandidate = nullptr;
1237+
unsigned DeferredCandidatesCount : 8 * sizeof(unsigned) - 2;
1238+
LLVM_PREFERRED_TYPE(bool)
1239+
unsigned HasDeferredTemplateConstructors : 1;
1240+
LLVM_PREFERRED_TYPE(bool)
1241+
unsigned ResolutionByPerfectCandidateIsDisabled : 1;
1242+
1243+
// Allocator for ConversionSequenceLists and deferred candidate args.
1244+
// We store the first few of these
11211245
// inline to avoid allocation for small sets.
11221246
llvm::BumpPtrAllocator SlabAllocator;
11231247

11241248
SourceLocation Loc;
11251249
CandidateSetKind Kind;
11261250
OperatorRewriteInfo RewriteInfo;
11271251

1252+
/// Small storage size for ImplicitConversionSequences
1253+
/// and the persisted arguments of deferred candidates.
11281254
constexpr static unsigned NumInlineBytes =
1129-
24 * sizeof(ImplicitConversionSequence);
1255+
32 * sizeof(ImplicitConversionSequence);
1256+
11301257
unsigned NumInlineBytesUsed = 0;
11311258
alignas(void *) char InlineSpace[NumInlineBytes];
11321259

@@ -1137,15 +1264,13 @@ class Sema;
11371264
/// from the slab allocator.
11381265
/// FIXME: It would probably be nice to have a SmallBumpPtrAllocator
11391266
/// instead.
1140-
/// FIXME: Now that this only allocates ImplicitConversionSequences, do we
1141-
/// want to un-generalize this?
11421267
template <typename T>
11431268
T *slabAllocate(unsigned N) {
11441269
// It's simpler if this doesn't need to consider alignment.
11451270
static_assert(alignof(T) == alignof(void *),
11461271
"Only works for pointer-aligned types.");
1147-
static_assert(std::is_trivial<T>::value ||
1148-
std::is_same<ImplicitConversionSequence, T>::value,
1272+
static_assert(std::is_trivially_destructible_v<T> ||
1273+
(std::is_same_v<ImplicitConversionSequence, T>),
11491274
"Add destruction logic to OverloadCandidateSet::clear().");
11501275

11511276
unsigned NBytes = sizeof(T) * N;
@@ -1159,12 +1284,34 @@ class Sema;
11591284
return reinterpret_cast<T *>(FreeSpaceStart);
11601285
}
11611286

1287+
// Because the size of OverloadCandidateSet has a noticeable impact on
1288+
// performance, we store each deferred template candidate in the slab
1289+
// allocator such that deferred candidates are ultimately a singly-linked
1290+
// intrusive linked list. This ends up being much more efficient than a
1291+
// SmallVector that is empty in the common case.
1292+
template <typename T> T *allocateDeferredCandidate() {
1293+
T *C = slabAllocate<T>(1);
1294+
if (!FirstDeferredCandidate)
1295+
FirstDeferredCandidate = C;
1296+
else {
1297+
auto *F = FirstDeferredCandidate;
1298+
while (F->Next)
1299+
F = F->Next;
1300+
F->Next = C;
1301+
}
1302+
DeferredCandidatesCount++;
1303+
return C;
1304+
}
1305+
11621306
void destroyCandidates();
11631307

11641308
public:
11651309
OverloadCandidateSet(SourceLocation Loc, CandidateSetKind CSK,
11661310
OperatorRewriteInfo RewriteInfo = {})
1167-
: Loc(Loc), Kind(CSK), RewriteInfo(RewriteInfo) {}
1311+
: FirstDeferredCandidate(nullptr), DeferredCandidatesCount(0),
1312+
HasDeferredTemplateConstructors(false),
1313+
ResolutionByPerfectCandidateIsDisabled(false), Loc(Loc), Kind(CSK),
1314+
RewriteInfo(RewriteInfo) {}
11681315
OverloadCandidateSet(const OverloadCandidateSet &) = delete;
11691316
OverloadCandidateSet &operator=(const OverloadCandidateSet &) = delete;
11701317
~OverloadCandidateSet() { destroyCandidates(); }
@@ -1176,6 +1323,9 @@ class Sema;
11761323
/// Whether diagnostics should be deferred.
11771324
bool shouldDeferDiags(Sema &S, ArrayRef<Expr *> Args, SourceLocation OpLoc);
11781325

1326+
// Whether the resolution of template candidates should be deferred
1327+
bool shouldDeferTemplateArgumentDeduction(const LangOptions &Opts) const;
1328+
11791329
/// Determine when this overload candidate will be new to the
11801330
/// overload set.
11811331
bool isNewCandidate(Decl *F, OverloadCandidateParamOrder PO =
@@ -1199,8 +1349,10 @@ class Sema;
11991349
iterator begin() { return Candidates.begin(); }
12001350
iterator end() { return Candidates.end(); }
12011351

1202-
size_t size() const { return Candidates.size(); }
1203-
bool empty() const { return Candidates.empty(); }
1352+
size_t size() const { return Candidates.size() + DeferredCandidatesCount; }
1353+
bool empty() const {
1354+
return Candidates.empty() && DeferredCandidatesCount == 0;
1355+
}
12041356

12051357
/// Allocate storage for conversion sequences for NumConversions
12061358
/// conversions.
@@ -1216,6 +1368,24 @@ class Sema;
12161368
return ConversionSequenceList(Conversions, NumConversions);
12171369
}
12181370

1371+
/// Provide storage for any Expr* arg that must be preserved
1372+
/// until deferred template candidates are deduced.
1373+
/// Typically this should be used for reversed operator arguments
1374+
/// and any time the argument array is transformed while adding
1375+
/// a template candidate.
1376+
llvm::MutableArrayRef<Expr *> getPersistentArgsArray(unsigned N) {
1377+
Expr **Exprs = slabAllocate<Expr *>(N);
1378+
return llvm::MutableArrayRef<Expr *>(Exprs, N);
1379+
}
1380+
1381+
template <typename... T>
1382+
llvm::MutableArrayRef<Expr *> getPersistentArgsArray(T *...Exprs) {
1383+
llvm::MutableArrayRef<Expr *> Arr =
1384+
getPersistentArgsArray(sizeof...(Exprs));
1385+
llvm::copy(std::initializer_list<Expr *>{Exprs...}, Arr.data());
1386+
return Arr;
1387+
}
1388+
12191389
/// Add a new candidate with NumConversions conversion sequence slots
12201390
/// to the overload set.
12211391
OverloadCandidate &addCandidate(unsigned NumConversions = 0,
@@ -1231,6 +1401,32 @@ class Sema;
12311401
return C;
12321402
}
12331403

1404+
void AddDeferredTemplateCandidate(
1405+
FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
1406+
ArrayRef<Expr *> Args, bool SuppressUserConversions,
1407+
bool PartialOverloading, bool AllowExplicit,
1408+
CallExpr::ADLCallKind IsADLCandidate, OverloadCandidateParamOrder PO,
1409+
bool AggregateCandidateDeduction);
1410+
1411+
void AddDeferredMethodTemplateCandidate(
1412+
FunctionTemplateDecl *MethodTmpl, DeclAccessPair FoundDecl,
1413+
CXXRecordDecl *ActingContext, QualType ObjectType,
1414+
Expr::Classification ObjectClassification, ArrayRef<Expr *> Args,
1415+
bool SuppressUserConversions, bool PartialOverloading,
1416+
OverloadCandidateParamOrder PO);
1417+
1418+
void AddDeferredConversionTemplateCandidate(
1419+
FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
1420+
CXXRecordDecl *ActingContext, Expr *From, QualType ToType,
1421+
bool AllowObjCConversionOnExplicit, bool AllowExplicit,
1422+
bool AllowResultConversion);
1423+
1424+
void InjectNonDeducedTemplateCandidates(Sema &S);
1425+
1426+
void DisableResolutionByPerfectCandidate() {
1427+
ResolutionByPerfectCandidateIsDisabled = true;
1428+
}
1429+
12341430
/// Find the best viable function on this overload set, if it exists.
12351431
OverloadingResult BestViableFunction(Sema &S, SourceLocation Loc,
12361432
OverloadCandidateSet::iterator& Best);
@@ -1263,6 +1459,15 @@ class Sema;
12631459
DestAS = AS;
12641460
}
12651461

1462+
private:
1463+
OverloadingResult ResultForBestCandidate(const iterator &Best);
1464+
void CudaExcludeWrongSideCandidates(
1465+
Sema &S, SmallVectorImpl<OverloadCandidate *> &Candidates);
1466+
OverloadingResult
1467+
BestViableFunctionImpl(Sema &S, SourceLocation Loc,
1468+
OverloadCandidateSet::iterator &Best);
1469+
void PerfectViableFunction(Sema &S, SourceLocation Loc,
1470+
OverloadCandidateSet::iterator &Best);
12661471
};
12671472

12681473
bool isBetterOverloadCandidate(Sema &S, const OverloadCandidate &Cand1,
@@ -1311,6 +1516,21 @@ class Sema;
13111516
// parameter.
13121517
bool shouldEnforceArgLimit(bool PartialOverloading, FunctionDecl *Function);
13131518

1519+
inline bool OverloadCandidateSet::shouldDeferTemplateArgumentDeduction(
1520+
const LangOptions &Opts) const {
1521+
return
1522+
// For user defined conversion we need to check against different
1523+
// combination of CV qualifiers and look at any explicit specifier, so
1524+
// always deduce template candidates.
1525+
Kind != CSK_InitByUserDefinedConversion
1526+
// When doing code completion, we want to see all the
1527+
// viable candidates.
1528+
&& Kind != CSK_CodeCompletion
1529+
// CUDA may prefer template candidates even when a non-candidate
1530+
// is a perfect match
1531+
&& !Opts.CUDA;
1532+
}
1533+
13141534
} // namespace clang
13151535

13161536
#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

0 commit comments

Comments
 (0)