Skip to content

Commit bf0fa95

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 27c1aa9 commit bf0fa95

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"
@@ -10389,9 +10390,26 @@ class Sema final : public SemaBase {
1038910390
OverloadCandidateSet &CandidateSet, bool SuppressUserConversions = false,
1039010391
bool PartialOverloading = false, OverloadCandidateParamOrder PO = {});
1039110392

10393+
void AddMethodTemplateCandidateImmediately(
10394+
OverloadCandidateSet &CandidateSet, FunctionTemplateDecl *MethodTmpl,
10395+
DeclAccessPair FoundDecl, CXXRecordDecl *ActingContext,
10396+
TemplateArgumentListInfo *ExplicitTemplateArgs, QualType ObjectType,
10397+
Expr::Classification ObjectClassification, ArrayRef<Expr *> Args,
10398+
bool SuppressUserConversions, bool PartialOverloading,
10399+
OverloadCandidateParamOrder PO);
10400+
1039210401
/// Add a C++ function template specialization as a candidate
1039310402
/// in the candidate set, using template argument deduction to produce
1039410403
/// an appropriate function template specialization.
10404+
10405+
void AddTemplateOverloadCandidateImmediately(
10406+
OverloadCandidateSet &CandidateSet,
10407+
FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
10408+
TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *> Args,
10409+
bool SuppressUserConversions, bool PartialOverloading, bool AllowExplicit,
10410+
ADLCallKind IsADLCandidate, OverloadCandidateParamOrder PO,
10411+
bool AggregateCandidateDeduction);
10412+
1039510413
void AddTemplateOverloadCandidate(
1039610414
FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
1039710415
TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *> Args,
@@ -10436,6 +10454,13 @@ class Sema final : public SemaBase {
1043610454
OverloadCandidateSet &CandidateSet, bool AllowObjCConversionOnExplicit,
1043710455
bool AllowExplicit, bool AllowResultConversion = true);
1043810456

10457+
void AddTemplateConversionCandidateImmediately(
10458+
OverloadCandidateSet &CandidateSet,
10459+
FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
10460+
CXXRecordDecl *ActingContext, Expr *From, QualType ToType,
10461+
bool AllowObjCConversionOnExplicit, bool AllowExplicit,
10462+
bool AllowResultConversion);
10463+
1043910464
/// AddSurrogateCandidate - Adds a "surrogate" candidate function that
1044010465
/// converts the given @c Object to a function pointer via the
1044110466
/// 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
@@ -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: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10029,12 +10029,15 @@ 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+
MutableArrayRef<Expr *> TmpInits =
10033+
Candidates.getPersistentArgsArray(Inits.size());
10034+
for (auto [I, E] : llvm::enumerate(Inits)) {
1003410035
if (auto *DI = dyn_cast<DesignatedInitExpr>(E))
10035-
TmpInits.push_back(DI->getInit());
10036+
TmpInits[I] = DI->getInit();
1003610037
else
10037-
TmpInits.push_back(E);
10038+
TmpInits[I] = E;
10039+
}
10040+
1003810041
AddTemplateOverloadCandidate(
1003910042
TD, FoundDecl, /*ExplicitArgs=*/nullptr, TmpInits, Candidates,
1004010043
/*SuppressUserConversions=*/false,

0 commit comments

Comments
 (0)