Skip to content

Commit 6b69229

Browse files
committed
[Clang][WIP][RFC] Bypass TAD during overload resolution if a perfect match exists
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 llvm#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 llvm#62096 Fixes llvm#74581
1 parent b6c0ce0 commit 6b69229

File tree

5 files changed

+395
-40
lines changed

5 files changed

+395
-40
lines changed

clang/include/clang/Sema/Overload.h

Lines changed: 125 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include <cstddef>
3939
#include <cstdint>
4040
#include <utility>
41+
#include <variant>
4142

4243
namespace clang {
4344

@@ -743,6 +744,12 @@ class Sema;
743744
Standard.setAllToTypes(T);
744745
}
745746

747+
bool isPerfect(const ASTContext &C) const {
748+
return (isStandard() && Standard.isIdentityConversion() &&
749+
C.hasSameType(Standard.getFromType(), Standard.getToType(2))) ||
750+
getKind() == StaticObjectArgumentConversion;
751+
}
752+
746753
// True iff this is a conversion sequence from an initializer list to an
747754
// array or std::initializer.
748755
bool hasInitializerListContainerType() const {
@@ -979,6 +986,18 @@ class Sema;
979986
return false;
980987
}
981988

989+
bool isPerfectMatch(const ASTContext &Ctx) const {
990+
if (!Viable)
991+
return false;
992+
for (auto &C : Conversions) {
993+
if (!C.isInitialized())
994+
return false;
995+
if (!C.isPerfect(Ctx))
996+
return false;
997+
}
998+
return true;
999+
}
1000+
9821001
bool TryToFixBadConversion(unsigned Idx, Sema &S) {
9831002
bool CanFix = Fix.tryToFixConversion(
9841003
Conversions[Idx].Bad.FromExpr,
@@ -1015,6 +1034,61 @@ class Sema;
10151034
RewriteKind(CRK_None) {}
10161035
};
10171036

1037+
struct NonDeducedConversionTemplateOverloadCandidate {
1038+
FunctionTemplateDecl *FunctionTemplate;
1039+
DeclAccessPair FoundDecl;
1040+
CXXRecordDecl *ActingContext;
1041+
Expr *From;
1042+
QualType ToType;
1043+
1044+
LLVM_PREFERRED_TYPE(bool)
1045+
unsigned AllowObjCConversionOnExplicit : 1;
1046+
LLVM_PREFERRED_TYPE(bool)
1047+
unsigned AllowExplicit : 1;
1048+
LLVM_PREFERRED_TYPE(bool)
1049+
unsigned AllowResultConversion : 1;
1050+
};
1051+
1052+
struct NonDeducedMethodTemplateOverloadCandidate {
1053+
FunctionTemplateDecl *FunctionTemplate;
1054+
DeclAccessPair FoundDecl;
1055+
ArrayRef<Expr *> Args;
1056+
CXXRecordDecl *ActingContext;
1057+
Expr::Classification ObjectClassification;
1058+
QualType ObjectType;
1059+
1060+
OverloadCandidateParamOrder PO;
1061+
LLVM_PREFERRED_TYPE(bool)
1062+
unsigned SuppressUserConversions : 1;
1063+
LLVM_PREFERRED_TYPE(bool)
1064+
unsigned PartialOverloading : 1;
1065+
};
1066+
1067+
struct NonDeducedFunctionTemplateOverloadCandidate {
1068+
FunctionTemplateDecl *FunctionTemplate;
1069+
DeclAccessPair FoundDecl;
1070+
ArrayRef<Expr *> Args;
1071+
1072+
CallExpr::ADLCallKind IsADLCandidate;
1073+
OverloadCandidateParamOrder PO;
1074+
LLVM_PREFERRED_TYPE(bool)
1075+
unsigned SuppressUserConversions : 1;
1076+
LLVM_PREFERRED_TYPE(bool)
1077+
unsigned PartialOverloading : 1;
1078+
LLVM_PREFERRED_TYPE(bool)
1079+
unsigned AllowExplicit : 1;
1080+
LLVM_PREFERRED_TYPE(bool)
1081+
unsigned AggregateCandidateDeduction : 1;
1082+
};
1083+
1084+
using NonDeducedTemplateOverloadCandidate =
1085+
std::variant<NonDeducedConversionTemplateOverloadCandidate,
1086+
NonDeducedMethodTemplateOverloadCandidate,
1087+
NonDeducedFunctionTemplateOverloadCandidate>;
1088+
1089+
static_assert(
1090+
std::is_trivially_destructible_v<NonDeducedTemplateOverloadCandidate>);
1091+
10181092
/// OverloadCandidateSet - A set of overload candidates, used in C++
10191093
/// overload resolution (C++ 13.3).
10201094
class OverloadCandidateSet {
@@ -1043,6 +1117,8 @@ class Sema;
10431117
/// C++ [over.match.call.general]
10441118
/// Resolve a call through the address of an overload set.
10451119
CSK_AddressOfOverloadSet,
1120+
1121+
CSK_CodeCompletion,
10461122
};
10471123

10481124
/// Information about operator rewrites to consider when adding operator
@@ -1116,6 +1192,7 @@ class Sema;
11161192
private:
11171193
SmallVector<OverloadCandidate, 16> Candidates;
11181194
llvm::SmallPtrSet<uintptr_t, 16> Functions;
1195+
SmallVector<NonDeducedTemplateOverloadCandidate, 8> NonDeducedCandidates;
11191196

11201197
// Allocator for ConversionSequenceLists. We store the first few of these
11211198
// inline to avoid allocation for small sets.
@@ -1126,7 +1203,7 @@ class Sema;
11261203
OperatorRewriteInfo RewriteInfo;
11271204

11281205
constexpr static unsigned NumInlineBytes =
1129-
24 * sizeof(ImplicitConversionSequence);
1206+
32 * sizeof(ImplicitConversionSequence);
11301207
unsigned NumInlineBytesUsed = 0;
11311208
alignas(void *) char InlineSpace[NumInlineBytes];
11321209

@@ -1144,8 +1221,8 @@ class Sema;
11441221
// It's simpler if this doesn't need to consider alignment.
11451222
static_assert(alignof(T) == alignof(void *),
11461223
"Only works for pointer-aligned types.");
1147-
static_assert(std::is_trivial<T>::value ||
1148-
std::is_same<ImplicitConversionSequence, T>::value,
1224+
static_assert(std::is_trivially_destructible_v<T> ||
1225+
(std::is_same_v<ImplicitConversionSequence, T>),
11491226
"Add destruction logic to OverloadCandidateSet::clear().");
11501227

11511228
unsigned NBytes = sizeof(T) * N;
@@ -1199,8 +1276,12 @@ class Sema;
11991276
iterator begin() { return Candidates.begin(); }
12001277
iterator end() { return Candidates.end(); }
12011278

1202-
size_t size() const { return Candidates.size(); }
1203-
bool empty() const { return Candidates.empty(); }
1279+
size_t size() const {
1280+
return Candidates.size() + NonDeducedCandidates.size();
1281+
}
1282+
bool empty() const {
1283+
return Candidates.empty() && NonDeducedCandidates.empty();
1284+
}
12041285

12051286
/// Allocate storage for conversion sequences for NumConversions
12061287
/// conversions.
@@ -1216,6 +1297,19 @@ class Sema;
12161297
return ConversionSequenceList(Conversions, NumConversions);
12171298
}
12181299

1300+
llvm::MutableArrayRef<Expr *> getPersistentArgsArray(unsigned N) {
1301+
Expr **Exprs = slabAllocate<Expr *>(N);
1302+
return llvm::MutableArrayRef<Expr *>(Exprs, N);
1303+
}
1304+
1305+
template <typename... T>
1306+
llvm::MutableArrayRef<Expr *> getPersistentArgsArray(T *...Exprs) {
1307+
llvm::MutableArrayRef<Expr *> Arr =
1308+
getPersistentArgsArray(sizeof...(Exprs));
1309+
llvm::copy(std::initializer_list<Expr *>{Exprs...}, Arr.data());
1310+
return Arr;
1311+
}
1312+
12191313
/// Add a new candidate with NumConversions conversion sequence slots
12201314
/// to the overload set.
12211315
OverloadCandidate &addCandidate(unsigned NumConversions = 0,
@@ -1231,10 +1325,36 @@ class Sema;
12311325
return C;
12321326
}
12331327

1328+
void AddNonDeducedTemplateCandidate(
1329+
FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
1330+
ArrayRef<Expr *> Args, bool SuppressUserConversions,
1331+
bool PartialOverloading, bool AllowExplicit,
1332+
CallExpr::ADLCallKind IsADLCandidate, OverloadCandidateParamOrder PO,
1333+
bool AggregateCandidateDeduction);
1334+
1335+
void AddNonDeducedMethodTemplateCandidate(
1336+
FunctionTemplateDecl *MethodTmpl, DeclAccessPair FoundDecl,
1337+
CXXRecordDecl *ActingContext, QualType ObjectType,
1338+
Expr::Classification ObjectClassification, ArrayRef<Expr *> Args,
1339+
bool SuppressUserConversions, bool PartialOverloading,
1340+
OverloadCandidateParamOrder PO);
1341+
1342+
void AddNonDeducedConversionTemplateCandidate(
1343+
FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
1344+
CXXRecordDecl *ActingContext, Expr *From, QualType ToType,
1345+
bool AllowObjCConversionOnExplicit, bool AllowExplicit,
1346+
bool AllowResultConversion);
1347+
1348+
void InjectNonDeducedTemplateCandidates(Sema &S);
1349+
12341350
/// Find the best viable function on this overload set, if it exists.
12351351
OverloadingResult BestViableFunction(Sema &S, SourceLocation Loc,
12361352
OverloadCandidateSet::iterator& Best);
12371353

1354+
OverloadingResult
1355+
BestViableFunctionImpl(Sema &S, SourceLocation Loc,
1356+
OverloadCandidateSet::iterator &Best);
1357+
12381358
SmallVector<OverloadCandidate *, 32> CompleteCandidates(
12391359
Sema &S, OverloadCandidateDisplayKind OCD, ArrayRef<Expr *> Args,
12401360
SourceLocation OpLoc = SourceLocation(),

clang/include/clang/Sema/Sema.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
#include "clang/Sema/DeclSpec.h"
6161
#include "clang/Sema/ExternalSemaSource.h"
6262
#include "clang/Sema/IdentifierResolver.h"
63+
#include "clang/Sema/Overload.h"
6364
#include "clang/Sema/Ownership.h"
6465
#include "clang/Sema/ParsedAttr.h"
6566
#include "clang/Sema/Redeclaration.h"
@@ -10344,9 +10345,26 @@ class Sema final : public SemaBase {
1034410345
OverloadCandidateSet &CandidateSet, bool SuppressUserConversions = false,
1034510346
bool PartialOverloading = false, OverloadCandidateParamOrder PO = {});
1034610347

10348+
void AddMethodTemplateCandidateImmediately(
10349+
OverloadCandidateSet &CandidateSet, FunctionTemplateDecl *MethodTmpl,
10350+
DeclAccessPair FoundDecl, CXXRecordDecl *ActingContext,
10351+
TemplateArgumentListInfo *ExplicitTemplateArgs, QualType ObjectType,
10352+
Expr::Classification ObjectClassification, ArrayRef<Expr *> Args,
10353+
bool SuppressUserConversions, bool PartialOverloading,
10354+
OverloadCandidateParamOrder PO);
10355+
1034710356
/// Add a C++ function template specialization as a candidate
1034810357
/// in the candidate set, using template argument deduction to produce
1034910358
/// an appropriate function template specialization.
10359+
10360+
void AddTemplateOverloadCandidateImmediately(
10361+
OverloadCandidateSet &CandidateSet,
10362+
FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
10363+
TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *> Args,
10364+
bool SuppressUserConversions, bool PartialOverloading, bool AllowExplicit,
10365+
ADLCallKind IsADLCandidate, OverloadCandidateParamOrder PO,
10366+
bool AggregateCandidateDeduction);
10367+
1035010368
void AddTemplateOverloadCandidate(
1035110369
FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
1035210370
TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *> Args,
@@ -10391,6 +10409,13 @@ class Sema final : public SemaBase {
1039110409
OverloadCandidateSet &CandidateSet, bool AllowObjCConversionOnExplicit,
1039210410
bool AllowExplicit, bool AllowResultConversion = true);
1039310411

10412+
void AddTemplateConversionCandidateImmediately(
10413+
OverloadCandidateSet &CandidateSet,
10414+
FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
10415+
CXXRecordDecl *ActingContext, Expr *From, QualType ToType,
10416+
bool AllowObjCConversionOnExplicit, bool AllowExplicit,
10417+
bool AllowResultConversion);
10418+
1039410419
/// AddSurrogateCandidate - Adds a "surrogate" candidate function that
1039510420
/// converts the given @c Object to a function pointer via the
1039610421
/// conversion function @c Conversion, and then attempts to call it

clang/lib/Sema/SemaCodeComplete.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6365,7 +6365,8 @@ SemaCodeCompletion::ProduceCallSignatureHelp(Expr *Fn, ArrayRef<Expr *> Args,
63656365
Expr *NakedFn = Fn->IgnoreParenCasts();
63666366
// Build an overload candidate set based on the functions we find.
63676367
SourceLocation Loc = Fn->getExprLoc();
6368-
OverloadCandidateSet CandidateSet(Loc, OverloadCandidateSet::CSK_Normal);
6368+
OverloadCandidateSet CandidateSet(Loc,
6369+
OverloadCandidateSet::CSK_CodeCompletion);
63696370

63706371
if (auto ULE = dyn_cast<UnresolvedLookupExpr>(NakedFn)) {
63716372
SemaRef.AddOverloadedCallCandidates(ULE, ArgsWithoutDependentTypes,
@@ -6568,7 +6569,8 @@ QualType SemaCodeCompletion::ProduceConstructorSignatureHelp(
65686569
// FIXME: Provide support for variadic template constructors.
65696570

65706571
if (CRD) {
6571-
OverloadCandidateSet CandidateSet(Loc, OverloadCandidateSet::CSK_Normal);
6572+
OverloadCandidateSet CandidateSet(Loc,
6573+
OverloadCandidateSet::CSK_CodeCompletion);
65726574
for (NamedDecl *C : SemaRef.LookupConstructors(CRD)) {
65736575
if (auto *FD = dyn_cast<FunctionDecl>(C)) {
65746576
// FIXME: we can't yet provide correct signature help for initializer

clang/lib/Sema/SemaInit.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10043,12 +10043,15 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
1004310043
// When [...] the constructor [...] is a candidate by
1004410044
// - [over.match.copy] (in all cases)
1004510045
if (TD) {
10046-
SmallVector<Expr *, 8> TmpInits;
10047-
for (Expr *E : Inits)
10046+
MutableArrayRef<Expr *> TmpInits =
10047+
Candidates.getPersistentArgsArray(Inits.size());
10048+
for (auto [I, E] : llvm::enumerate(Inits)) {
1004810049
if (auto *DI = dyn_cast<DesignatedInitExpr>(E))
10049-
TmpInits.push_back(DI->getInit());
10050+
TmpInits[I] = DI->getInit();
1005010051
else
10051-
TmpInits.push_back(E);
10052+
TmpInits[I] = E;
10053+
}
10054+
1005210055
AddTemplateOverloadCandidate(
1005310056
TD, FoundDecl, /*ExplicitArgs=*/nullptr, TmpInits, Candidates,
1005410057
/*SuppressUserConversions=*/false,

0 commit comments

Comments
 (0)