[clang] [Clang] Bypass TAD during overload resolution if a perfect match exists (PR #136203)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Apr 17 14:53:39 PDT 2025
https://github.com/cor3ntin updated https://github.com/llvm/llvm-project/pull/136203
>From 8a671568c3f9022c542dbd2a01c9a2a4e087b4ef Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Thu, 27 Mar 2025 16:25:07 +0100
Subject: [PATCH 01/32] [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 #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
---
clang/include/clang/Sema/Overload.h | 130 +++++++++++++-
clang/include/clang/Sema/Sema.h | 25 +++
clang/lib/Sema/SemaCodeComplete.cpp | 6 +-
clang/lib/Sema/SemaInit.cpp | 11 +-
clang/lib/Sema/SemaOverload.cpp | 263 +++++++++++++++++++++++++---
5 files changed, 395 insertions(+), 40 deletions(-)
diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index 6e08762dcc6d7..2cc7e1809e26c 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -38,6 +38,7 @@
#include <cstddef>
#include <cstdint>
#include <utility>
+#include <variant>
namespace clang {
@@ -743,6 +744,12 @@ class Sema;
Standard.setAllToTypes(T);
}
+ bool isPerfect(const ASTContext &C) const {
+ return (isStandard() && Standard.isIdentityConversion() &&
+ C.hasSameType(Standard.getFromType(), Standard.getToType(2))) ||
+ getKind() == StaticObjectArgumentConversion;
+ }
+
// True iff this is a conversion sequence from an initializer list to an
// array or std::initializer.
bool hasInitializerListContainerType() const {
@@ -979,6 +986,18 @@ class Sema;
return false;
}
+ bool isPerfectMatch(const ASTContext &Ctx) const {
+ if (!Viable)
+ return false;
+ for (auto &C : Conversions) {
+ if (!C.isInitialized())
+ return false;
+ if (!C.isPerfect(Ctx))
+ return false;
+ }
+ return true;
+ }
+
bool TryToFixBadConversion(unsigned Idx, Sema &S) {
bool CanFix = Fix.tryToFixConversion(
Conversions[Idx].Bad.FromExpr,
@@ -1015,6 +1034,61 @@ class Sema;
RewriteKind(CRK_None) {}
};
+ struct NonDeducedConversionTemplateOverloadCandidate {
+ FunctionTemplateDecl *FunctionTemplate;
+ DeclAccessPair FoundDecl;
+ CXXRecordDecl *ActingContext;
+ Expr *From;
+ QualType ToType;
+
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned AllowObjCConversionOnExplicit : 1;
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned AllowExplicit : 1;
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned AllowResultConversion : 1;
+ };
+
+ struct NonDeducedMethodTemplateOverloadCandidate {
+ FunctionTemplateDecl *FunctionTemplate;
+ DeclAccessPair FoundDecl;
+ ArrayRef<Expr *> Args;
+ CXXRecordDecl *ActingContext;
+ Expr::Classification ObjectClassification;
+ QualType ObjectType;
+
+ OverloadCandidateParamOrder PO;
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned SuppressUserConversions : 1;
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned PartialOverloading : 1;
+ };
+
+ struct NonDeducedFunctionTemplateOverloadCandidate {
+ FunctionTemplateDecl *FunctionTemplate;
+ DeclAccessPair FoundDecl;
+ ArrayRef<Expr *> Args;
+
+ CallExpr::ADLCallKind IsADLCandidate;
+ OverloadCandidateParamOrder PO;
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned SuppressUserConversions : 1;
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned PartialOverloading : 1;
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned AllowExplicit : 1;
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned AggregateCandidateDeduction : 1;
+ };
+
+ using NonDeducedTemplateOverloadCandidate =
+ std::variant<NonDeducedConversionTemplateOverloadCandidate,
+ NonDeducedMethodTemplateOverloadCandidate,
+ NonDeducedFunctionTemplateOverloadCandidate>;
+
+ static_assert(
+ std::is_trivially_destructible_v<NonDeducedTemplateOverloadCandidate>);
+
/// OverloadCandidateSet - A set of overload candidates, used in C++
/// overload resolution (C++ 13.3).
class OverloadCandidateSet {
@@ -1043,6 +1117,8 @@ class Sema;
/// C++ [over.match.call.general]
/// Resolve a call through the address of an overload set.
CSK_AddressOfOverloadSet,
+
+ CSK_CodeCompletion,
};
/// Information about operator rewrites to consider when adding operator
@@ -1116,6 +1192,7 @@ class Sema;
private:
SmallVector<OverloadCandidate, 16> Candidates;
llvm::SmallPtrSet<uintptr_t, 16> Functions;
+ SmallVector<NonDeducedTemplateOverloadCandidate, 8> NonDeducedCandidates;
// Allocator for ConversionSequenceLists. We store the first few of these
// inline to avoid allocation for small sets.
@@ -1126,7 +1203,7 @@ class Sema;
OperatorRewriteInfo RewriteInfo;
constexpr static unsigned NumInlineBytes =
- 24 * sizeof(ImplicitConversionSequence);
+ 32 * sizeof(ImplicitConversionSequence);
unsigned NumInlineBytesUsed = 0;
alignas(void *) char InlineSpace[NumInlineBytes];
@@ -1144,8 +1221,8 @@ class Sema;
// It's simpler if this doesn't need to consider alignment.
static_assert(alignof(T) == alignof(void *),
"Only works for pointer-aligned types.");
- static_assert(std::is_trivial<T>::value ||
- std::is_same<ImplicitConversionSequence, T>::value,
+ static_assert(std::is_trivially_destructible_v<T> ||
+ (std::is_same_v<ImplicitConversionSequence, T>),
"Add destruction logic to OverloadCandidateSet::clear().");
unsigned NBytes = sizeof(T) * N;
@@ -1199,8 +1276,12 @@ class Sema;
iterator begin() { return Candidates.begin(); }
iterator end() { return Candidates.end(); }
- size_t size() const { return Candidates.size(); }
- bool empty() const { return Candidates.empty(); }
+ size_t size() const {
+ return Candidates.size() + NonDeducedCandidates.size();
+ }
+ bool empty() const {
+ return Candidates.empty() && NonDeducedCandidates.empty();
+ }
/// Allocate storage for conversion sequences for NumConversions
/// conversions.
@@ -1216,6 +1297,19 @@ class Sema;
return ConversionSequenceList(Conversions, NumConversions);
}
+ llvm::MutableArrayRef<Expr *> getPersistentArgsArray(unsigned N) {
+ Expr **Exprs = slabAllocate<Expr *>(N);
+ return llvm::MutableArrayRef<Expr *>(Exprs, N);
+ }
+
+ template <typename... T>
+ llvm::MutableArrayRef<Expr *> getPersistentArgsArray(T *...Exprs) {
+ llvm::MutableArrayRef<Expr *> Arr =
+ getPersistentArgsArray(sizeof...(Exprs));
+ llvm::copy(std::initializer_list<Expr *>{Exprs...}, Arr.data());
+ return Arr;
+ }
+
/// Add a new candidate with NumConversions conversion sequence slots
/// to the overload set.
OverloadCandidate &addCandidate(unsigned NumConversions = 0,
@@ -1231,10 +1325,36 @@ class Sema;
return C;
}
+ void AddNonDeducedTemplateCandidate(
+ FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
+ ArrayRef<Expr *> Args, bool SuppressUserConversions,
+ bool PartialOverloading, bool AllowExplicit,
+ CallExpr::ADLCallKind IsADLCandidate, OverloadCandidateParamOrder PO,
+ bool AggregateCandidateDeduction);
+
+ void AddNonDeducedMethodTemplateCandidate(
+ FunctionTemplateDecl *MethodTmpl, DeclAccessPair FoundDecl,
+ CXXRecordDecl *ActingContext, QualType ObjectType,
+ Expr::Classification ObjectClassification, ArrayRef<Expr *> Args,
+ bool SuppressUserConversions, bool PartialOverloading,
+ OverloadCandidateParamOrder PO);
+
+ void AddNonDeducedConversionTemplateCandidate(
+ FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
+ CXXRecordDecl *ActingContext, Expr *From, QualType ToType,
+ bool AllowObjCConversionOnExplicit, bool AllowExplicit,
+ bool AllowResultConversion);
+
+ void InjectNonDeducedTemplateCandidates(Sema &S);
+
/// Find the best viable function on this overload set, if it exists.
OverloadingResult BestViableFunction(Sema &S, SourceLocation Loc,
OverloadCandidateSet::iterator& Best);
+ OverloadingResult
+ BestViableFunctionImpl(Sema &S, SourceLocation Loc,
+ OverloadCandidateSet::iterator &Best);
+
SmallVector<OverloadCandidate *, 32> CompleteCandidates(
Sema &S, OverloadCandidateDisplayKind OCD, ArrayRef<Expr *> Args,
SourceLocation OpLoc = SourceLocation(),
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index a757f4c6430ae..34beea87e111a 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -60,6 +60,7 @@
#include "clang/Sema/DeclSpec.h"
#include "clang/Sema/ExternalSemaSource.h"
#include "clang/Sema/IdentifierResolver.h"
+#include "clang/Sema/Overload.h"
#include "clang/Sema/Ownership.h"
#include "clang/Sema/ParsedAttr.h"
#include "clang/Sema/Redeclaration.h"
@@ -10389,9 +10390,26 @@ class Sema final : public SemaBase {
OverloadCandidateSet &CandidateSet, bool SuppressUserConversions = false,
bool PartialOverloading = false, OverloadCandidateParamOrder PO = {});
+ void AddMethodTemplateCandidateImmediately(
+ OverloadCandidateSet &CandidateSet, FunctionTemplateDecl *MethodTmpl,
+ DeclAccessPair FoundDecl, CXXRecordDecl *ActingContext,
+ TemplateArgumentListInfo *ExplicitTemplateArgs, QualType ObjectType,
+ Expr::Classification ObjectClassification, ArrayRef<Expr *> Args,
+ bool SuppressUserConversions, bool PartialOverloading,
+ OverloadCandidateParamOrder PO);
+
/// Add a C++ function template specialization as a candidate
/// in the candidate set, using template argument deduction to produce
/// an appropriate function template specialization.
+
+ void AddTemplateOverloadCandidateImmediately(
+ OverloadCandidateSet &CandidateSet,
+ FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
+ TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *> Args,
+ bool SuppressUserConversions, bool PartialOverloading, bool AllowExplicit,
+ ADLCallKind IsADLCandidate, OverloadCandidateParamOrder PO,
+ bool AggregateCandidateDeduction);
+
void AddTemplateOverloadCandidate(
FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *> Args,
@@ -10436,6 +10454,13 @@ class Sema final : public SemaBase {
OverloadCandidateSet &CandidateSet, bool AllowObjCConversionOnExplicit,
bool AllowExplicit, bool AllowResultConversion = true);
+ void AddTemplateConversionCandidateImmediately(
+ OverloadCandidateSet &CandidateSet,
+ FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
+ CXXRecordDecl *ActingContext, Expr *From, QualType ToType,
+ bool AllowObjCConversionOnExplicit, bool AllowExplicit,
+ bool AllowResultConversion);
+
/// AddSurrogateCandidate - Adds a "surrogate" candidate function that
/// converts the given @c Object to a function pointer via the
/// conversion function @c Conversion, and then attempts to call it
diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index 1e4e6fdc78351..ed10730ef986b 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -6354,7 +6354,8 @@ SemaCodeCompletion::ProduceCallSignatureHelp(Expr *Fn, ArrayRef<Expr *> Args,
Expr *NakedFn = Fn->IgnoreParenCasts();
// Build an overload candidate set based on the functions we find.
SourceLocation Loc = Fn->getExprLoc();
- OverloadCandidateSet CandidateSet(Loc, OverloadCandidateSet::CSK_Normal);
+ OverloadCandidateSet CandidateSet(Loc,
+ OverloadCandidateSet::CSK_CodeCompletion);
if (auto ULE = dyn_cast<UnresolvedLookupExpr>(NakedFn)) {
SemaRef.AddOverloadedCallCandidates(ULE, ArgsWithoutDependentTypes,
@@ -6557,7 +6558,8 @@ QualType SemaCodeCompletion::ProduceConstructorSignatureHelp(
// FIXME: Provide support for variadic template constructors.
if (CRD) {
- OverloadCandidateSet CandidateSet(Loc, OverloadCandidateSet::CSK_Normal);
+ OverloadCandidateSet CandidateSet(Loc,
+ OverloadCandidateSet::CSK_CodeCompletion);
for (NamedDecl *C : SemaRef.LookupConstructors(CRD)) {
if (auto *FD = dyn_cast<FunctionDecl>(C)) {
// FIXME: we can't yet provide correct signature help for initializer
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 77d7f821f2011..e8a1e8fff3314 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -10029,12 +10029,15 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
// When [...] the constructor [...] is a candidate by
// - [over.match.copy] (in all cases)
if (TD) {
- SmallVector<Expr *, 8> TmpInits;
- for (Expr *E : Inits)
+ MutableArrayRef<Expr *> TmpInits =
+ Candidates.getPersistentArgsArray(Inits.size());
+ for (auto [I, E] : llvm::enumerate(Inits)) {
if (auto *DI = dyn_cast<DesignatedInitExpr>(E))
- TmpInits.push_back(DI->getInit());
+ TmpInits[I] = DI->getInit();
else
- TmpInits.push_back(E);
+ TmpInits[I] = E;
+ }
+
AddTemplateOverloadCandidate(
TD, FoundDecl, /*ExplicitArgs=*/nullptr, TmpInits, Candidates,
/*SuppressUserConversions=*/false,
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 55634aa75ae25..337a7033bbfcc 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -31,6 +31,7 @@
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Overload.h"
#include "clang/Sema/SemaCUDA.h"
+#include "clang/Sema/SemaCodeCompletion.h"
#include "clang/Sema/SemaObjC.h"
#include "clang/Sema/Template.h"
#include "clang/Sema/TemplateDeduction.h"
@@ -45,6 +46,7 @@
#include <cstddef>
#include <cstdlib>
#include <optional>
+#include <variant>
using namespace clang;
using namespace sema;
@@ -7805,6 +7807,28 @@ void Sema::AddMethodTemplateCandidate(
if (!CandidateSet.isNewCandidate(MethodTmpl, PO))
return;
+ if (CandidateSet.getKind() == OverloadCandidateSet::CSK_CodeCompletion ||
+ ExplicitTemplateArgs) {
+ AddMethodTemplateCandidateImmediately(
+ CandidateSet, MethodTmpl, FoundDecl, ActingContext,
+ ExplicitTemplateArgs, ObjectType, ObjectClassification, Args,
+ SuppressUserConversions, PartialOverloading, PO);
+ return;
+ }
+
+ CandidateSet.AddNonDeducedMethodTemplateCandidate(
+ MethodTmpl, FoundDecl, ActingContext, ObjectType, ObjectClassification,
+ Args, SuppressUserConversions, PartialOverloading, PO);
+}
+
+void Sema::AddMethodTemplateCandidateImmediately(
+ OverloadCandidateSet &CandidateSet, FunctionTemplateDecl *MethodTmpl,
+ DeclAccessPair FoundDecl, CXXRecordDecl *ActingContext,
+ TemplateArgumentListInfo *ExplicitTemplateArgs, QualType ObjectType,
+ Expr::Classification ObjectClassification, ArrayRef<Expr *> Args,
+ bool SuppressUserConversions, bool PartialOverloading,
+ OverloadCandidateParamOrder PO) {
+
// C++ [over.match.funcs]p7:
// In each case where a candidate is a function template, candidate
// function template specializations are generated using template argument
@@ -7834,7 +7858,7 @@ void Sema::AddMethodTemplateCandidate(
Candidate.Function = MethodTmpl->getTemplatedDecl();
Candidate.Viable = false;
Candidate.RewriteKind =
- CandidateSet.getRewriteInfo().getRewriteKind(Candidate.Function, PO);
+ CandidateSet.getRewriteInfo().getRewriteKind(Candidate.Function, PO);
Candidate.IsSurrogate = false;
Candidate.IgnoreObjectArgument =
cast<CXXMethodDecl>(Candidate.Function)->isStatic() ||
@@ -7844,8 +7868,8 @@ void Sema::AddMethodTemplateCandidate(
Candidate.FailureKind = ovl_fail_bad_conversion;
else {
Candidate.FailureKind = ovl_fail_bad_deduction;
- Candidate.DeductionFailure = MakeDeductionFailureInfo(Context, Result,
- Info);
+ Candidate.DeductionFailure =
+ MakeDeductionFailureInfo(Context, Result, Info);
}
return;
}
@@ -7876,6 +7900,28 @@ void Sema::AddTemplateOverloadCandidate(
if (!CandidateSet.isNewCandidate(FunctionTemplate, PO))
return;
+ if (CandidateSet.getKind() == OverloadCandidateSet::CSK_CodeCompletion ||
+ ExplicitTemplateArgs) {
+ AddTemplateOverloadCandidateImmediately(
+ CandidateSet, FunctionTemplate, FoundDecl, ExplicitTemplateArgs, Args,
+ SuppressUserConversions, PartialOverloading, AllowExplicit,
+ IsADLCandidate, PO, AggregateCandidateDeduction);
+ return;
+ }
+
+ CandidateSet.AddNonDeducedTemplateCandidate(
+ FunctionTemplate, FoundDecl, Args, SuppressUserConversions,
+ PartialOverloading, AllowExplicit, IsADLCandidate, PO,
+ AggregateCandidateDeduction);
+}
+
+void Sema::AddTemplateOverloadCandidateImmediately(
+ OverloadCandidateSet &CandidateSet, FunctionTemplateDecl *FunctionTemplate,
+ DeclAccessPair FoundDecl, TemplateArgumentListInfo *ExplicitTemplateArgs,
+ ArrayRef<Expr *> Args, bool SuppressUserConversions,
+ bool PartialOverloading, bool AllowExplicit, ADLCallKind IsADLCandidate,
+ OverloadCandidateParamOrder PO, bool AggregateCandidateDeduction) {
+
// If the function template has a non-dependent explicit specification,
// exclude it now if appropriate; we are not permitted to perform deduction
// and substitution in this case.
@@ -7919,7 +7965,7 @@ void Sema::AddTemplateOverloadCandidate(
Candidate.Function = FunctionTemplate->getTemplatedDecl();
Candidate.Viable = false;
Candidate.RewriteKind =
- CandidateSet.getRewriteInfo().getRewriteKind(Candidate.Function, PO);
+ CandidateSet.getRewriteInfo().getRewriteKind(Candidate.Function, PO);
Candidate.IsSurrogate = false;
Candidate.IsADLCandidate = llvm::to_underlying(IsADLCandidate);
// Ignore the object argument if there is one, since we don't have an object
@@ -7932,8 +7978,8 @@ void Sema::AddTemplateOverloadCandidate(
Candidate.FailureKind = ovl_fail_bad_conversion;
else {
Candidate.FailureKind = ovl_fail_bad_deduction;
- Candidate.DeductionFailure = MakeDeductionFailureInfo(Context, Result,
- Info);
+ Candidate.DeductionFailure =
+ MakeDeductionFailureInfo(Context, Result, Info);
}
return;
}
@@ -8275,6 +8321,25 @@ void Sema::AddTemplateConversionCandidate(
if (!CandidateSet.isNewCandidate(FunctionTemplate))
return;
+ if (CandidateSet.getKind() == OverloadCandidateSet::CSK_CodeCompletion) {
+ AddTemplateConversionCandidateImmediately(
+ CandidateSet, FunctionTemplate, FoundDecl, ActingDC, From, ToType,
+ AllowObjCConversionOnExplicit, AllowExplicit, AllowResultConversion);
+
+ return;
+ }
+
+ CandidateSet.AddNonDeducedConversionTemplateCandidate(
+ FunctionTemplate, FoundDecl, ActingDC, From, ToType,
+ AllowObjCConversionOnExplicit, AllowExplicit, AllowResultConversion);
+}
+
+void Sema::AddTemplateConversionCandidateImmediately(
+ OverloadCandidateSet &CandidateSet, FunctionTemplateDecl *FunctionTemplate,
+ DeclAccessPair FoundDecl, CXXRecordDecl *ActingContext, Expr *From,
+ QualType ToType, bool AllowObjCConversionOnExplicit, bool AllowExplicit,
+ bool AllowResultConversion) {
+
// If the function template has a non-dependent explicit specification,
// exclude it now if appropriate; we are not permitted to perform deduction
// and substitution in this case.
@@ -8302,15 +8367,15 @@ void Sema::AddTemplateConversionCandidate(
Candidate.Viable = false;
Candidate.FailureKind = ovl_fail_bad_deduction;
Candidate.ExplicitCallArguments = 1;
- Candidate.DeductionFailure = MakeDeductionFailureInfo(Context, Result,
- Info);
+ Candidate.DeductionFailure =
+ MakeDeductionFailureInfo(Context, Result, Info);
return;
}
// Add the conversion function template specialization produced by
// template argument deduction as a candidate.
assert(Specialization && "Missing function template specialization?");
- AddConversionCandidate(Specialization, FoundDecl, ActingDC, From, ToType,
+ AddConversionCandidate(Specialization, FoundDecl, ActingContext, From, ToType,
CandidateSet, AllowObjCConversionOnExplicit,
AllowExplicit, AllowResultConversion,
Info.hasStrictPackMatch());
@@ -8449,6 +8514,13 @@ void Sema::AddNonMemberOperatorCandidates(
NamedDecl *D = F.getDecl()->getUnderlyingDecl();
ArrayRef<Expr *> FunctionArgs = Args;
+ auto ReversedArgs = [&, Arr = ArrayRef<Expr *>{}]() mutable {
+ if (Arr.empty())
+ Arr = CandidateSet.getPersistentArgsArray(FunctionArgs[1],
+ FunctionArgs[0]);
+ return Arr;
+ };
+
FunctionTemplateDecl *FunTmpl = dyn_cast<FunctionTemplateDecl>(D);
FunctionDecl *FD =
FunTmpl ? FunTmpl->getTemplatedDecl() : cast<FunctionDecl>(D);
@@ -8463,18 +8535,18 @@ void Sema::AddNonMemberOperatorCandidates(
if (FunTmpl) {
AddTemplateOverloadCandidate(FunTmpl, F.getPair(), ExplicitTemplateArgs,
FunctionArgs, CandidateSet);
- if (CandidateSet.getRewriteInfo().shouldAddReversed(*this, Args, FD))
- AddTemplateOverloadCandidate(
- FunTmpl, F.getPair(), ExplicitTemplateArgs,
- {FunctionArgs[1], FunctionArgs[0]}, CandidateSet, false, false,
- true, ADLCallKind::NotADL, OverloadCandidateParamOrder::Reversed);
+ if (CandidateSet.getRewriteInfo().shouldAddReversed(*this, Args, FD)) {
+ AddTemplateOverloadCandidate(FunTmpl, F.getPair(), ExplicitTemplateArgs,
+ ReversedArgs(), CandidateSet, false, false,
+ true, ADLCallKind::NotADL,
+ OverloadCandidateParamOrder::Reversed);
+ }
} else {
if (ExplicitTemplateArgs)
continue;
AddOverloadCandidate(FD, F.getPair(), FunctionArgs, CandidateSet);
if (CandidateSet.getRewriteInfo().shouldAddReversed(*this, Args, FD))
- AddOverloadCandidate(FD, F.getPair(),
- {FunctionArgs[1], FunctionArgs[0]}, CandidateSet,
+ AddOverloadCandidate(FD, F.getPair(), ReversedArgs(), CandidateSet,
false, false, true, false, ADLCallKind::NotADL, {},
OverloadCandidateParamOrder::Reversed);
}
@@ -10199,6 +10271,12 @@ Sema::AddArgumentDependentLookupCandidates(DeclarationName Name,
// FIXME: Pass in the explicit template arguments?
ArgumentDependentLookup(Name, Loc, Args, Fns);
+ auto ReversedArgs = [&, Arr = ArrayRef<Expr *>{}]() mutable {
+ if (Arr.empty())
+ Arr = CandidateSet.getPersistentArgsArray(Args[1], Args[0]);
+ return Arr;
+ };
+
// Erase all of the candidates we already knew about.
for (OverloadCandidateSet::iterator Cand = CandidateSet.begin(),
CandEnd = CandidateSet.end();
@@ -10225,7 +10303,7 @@ Sema::AddArgumentDependentLookupCandidates(DeclarationName Name,
/*AllowExplicitConversion=*/false, ADLCallKind::UsesADL);
if (CandidateSet.getRewriteInfo().shouldAddReversed(*this, Args, FD)) {
AddOverloadCandidate(
- FD, FoundDecl, {Args[1], Args[0]}, CandidateSet,
+ FD, FoundDecl, ReversedArgs(), CandidateSet,
/*SuppressUserConversions=*/false, PartialOverloading,
/*AllowExplicit=*/true, /*AllowExplicitConversion=*/false,
ADLCallKind::UsesADL, {}, OverloadCandidateParamOrder::Reversed);
@@ -10239,8 +10317,8 @@ Sema::AddArgumentDependentLookupCandidates(DeclarationName Name,
if (CandidateSet.getRewriteInfo().shouldAddReversed(
*this, Args, FTD->getTemplatedDecl())) {
AddTemplateOverloadCandidate(
- FTD, FoundDecl, ExplicitTemplateArgs, {Args[1], Args[0]},
- CandidateSet, /*SuppressUserConversions=*/false, PartialOverloading,
+ FTD, FoundDecl, ExplicitTemplateArgs, ReversedArgs(), CandidateSet,
+ /*SuppressUserConversions=*/false, PartialOverloading,
/*AllowExplicit=*/true, ADLCallKind::UsesADL,
OverloadCandidateParamOrder::Reversed);
}
@@ -10913,6 +10991,93 @@ bool OverloadCandidate::NotValidBecauseConstraintExprHasError() const {
->Satisfaction.ContainsErrors;
}
+void OverloadCandidateSet::AddNonDeducedTemplateCandidate(
+ FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
+ ArrayRef<Expr *> Args, bool SuppressUserConversions,
+ bool PartialOverloading, bool AllowExplicit,
+ CallExpr::ADLCallKind IsADLCandidate, OverloadCandidateParamOrder PO,
+ bool AggregateCandidateDeduction) {
+ NonDeducedFunctionTemplateOverloadCandidate C{FunctionTemplate,
+ FoundDecl,
+ Args,
+ IsADLCandidate,
+ PO,
+ SuppressUserConversions,
+ PartialOverloading,
+ AllowExplicit,
+ AggregateCandidateDeduction};
+ NonDeducedCandidates.emplace_back(std::move(C));
+}
+
+void OverloadCandidateSet::AddNonDeducedMethodTemplateCandidate(
+ FunctionTemplateDecl *MethodTmpl, DeclAccessPair FoundDecl,
+ CXXRecordDecl *ActingContext, QualType ObjectType,
+ Expr::Classification ObjectClassification, ArrayRef<Expr *> Args,
+ bool SuppressUserConversions, bool PartialOverloading,
+ OverloadCandidateParamOrder PO) {
+ NonDeducedMethodTemplateOverloadCandidate C{
+ MethodTmpl, FoundDecl, Args, ActingContext,
+ ObjectClassification, ObjectType, PO, SuppressUserConversions,
+ PartialOverloading};
+ NonDeducedCandidates.emplace_back(std::move(C));
+}
+
+void OverloadCandidateSet::AddNonDeducedConversionTemplateCandidate(
+ FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
+ CXXRecordDecl *ActingContext, Expr *From, QualType ToType,
+ bool AllowObjCConversionOnExplicit, bool AllowExplicit,
+ bool AllowResultConversion) {
+
+ NonDeducedConversionTemplateOverloadCandidate C{
+ FunctionTemplate, FoundDecl,
+ ActingContext, From,
+ ToType, AllowObjCConversionOnExplicit,
+ AllowExplicit, AllowResultConversion};
+
+ NonDeducedCandidates.emplace_back(std::move(C));
+}
+
+static void
+AddTemplateOverloadCandidate(Sema &S, OverloadCandidateSet &CandidateSet,
+ NonDeducedMethodTemplateOverloadCandidate &&C) {
+
+ S.AddMethodTemplateCandidateImmediately(
+ CandidateSet, C.FunctionTemplate, C.FoundDecl, C.ActingContext,
+ /*ExplicitTemplateArgs=*/nullptr, C.ObjectType, C.ObjectClassification,
+ C.Args, C.SuppressUserConversions, C.PartialOverloading, C.PO);
+}
+
+static void
+AddTemplateOverloadCandidate(Sema &S, OverloadCandidateSet &CandidateSet,
+ NonDeducedFunctionTemplateOverloadCandidate &&C) {
+ S.AddTemplateOverloadCandidateImmediately(
+ CandidateSet, C.FunctionTemplate, C.FoundDecl,
+ /*ExplicitTemplateArgs=*/nullptr, C.Args, C.SuppressUserConversions,
+ C.PartialOverloading, C.AllowExplicit, C.IsADLCandidate, C.PO,
+ C.AggregateCandidateDeduction);
+}
+
+static void AddTemplateOverloadCandidate(
+ Sema &S, OverloadCandidateSet &CandidateSet,
+ NonDeducedConversionTemplateOverloadCandidate &&C) {
+ return S.AddTemplateConversionCandidateImmediately(
+ CandidateSet, C.FunctionTemplate, C.FoundDecl, C.ActingContext, C.From,
+ C.ToType, C.AllowObjCConversionOnExplicit, C.AllowExplicit,
+ C.AllowResultConversion);
+}
+
+void OverloadCandidateSet::InjectNonDeducedTemplateCandidates(Sema &S) {
+ Candidates.reserve(Candidates.size() + NonDeducedCandidates.size());
+ for (auto &&Elem : NonDeducedCandidates) {
+ std::visit(
+ [&](auto &&Cand) {
+ AddTemplateOverloadCandidate(S, *this, std::move(Cand));
+ },
+ Elem);
+ }
+ NonDeducedCandidates.clear();
+}
+
/// Computes the best viable function (C++ 13.3.3)
/// within an overload candidate set.
///
@@ -10926,7 +11091,44 @@ bool OverloadCandidate::NotValidBecauseConstraintExprHasError() const {
OverloadingResult
OverloadCandidateSet::BestViableFunction(Sema &S, SourceLocation Loc,
iterator &Best) {
+
+ bool TwoPhaseResolution =
+ !NonDeducedCandidates.empty() && Kind != CSK_CodeCompletion &&
+ Kind != CSK_InitByUserDefinedConversion && Kind != CSK_InitByConstructor;
+
+ if (TwoPhaseResolution) {
+ Best = end();
+ for (auto It = begin(); It != end(); ++It) {
+ if (It->isPerfectMatch(S.getASTContext())) {
+ if (Best == end()) {
+ Best = It;
+ } else {
+ Best = end();
+ break;
+ }
+ }
+ }
+ if (Best != end()) {
+ Best->Best = true;
+ if (Best->Function && Best->Function->isDeleted())
+ return OR_Deleted;
+ if (auto *M = dyn_cast_or_null<CXXMethodDecl>(Best->Function);
+ Kind == CSK_AddressOfOverloadSet && M &&
+ M->isImplicitObjectMemberFunction()) {
+ return OR_No_Viable_Function;
+ }
+ return OR_Success;
+ }
+ }
+ InjectNonDeducedTemplateCandidates(S);
+ return BestViableFunctionImpl(S, Loc, Best);
+}
+
+OverloadingResult OverloadCandidateSet::BestViableFunctionImpl(
+ Sema &S, SourceLocation Loc, OverloadCandidateSet::iterator &Best) {
+
llvm::SmallVector<OverloadCandidate *, 16> Candidates;
+ Candidates.reserve(this->Candidates.size());
std::transform(begin(), end(), std::back_inserter(Candidates),
[](OverloadCandidate &Cand) { return &Cand; });
@@ -10961,7 +11163,6 @@ OverloadCandidateSet::BestViableFunction(Sema &S, SourceLocation Loc,
}
}
- // Find the best viable function.
Best = end();
for (auto *Cand : Candidates) {
Cand->Best = false;
@@ -10983,9 +11184,8 @@ OverloadCandidateSet::BestViableFunction(Sema &S, SourceLocation Loc,
if (Best == end())
return OR_No_Viable_Function;
+ llvm::SmallVector<OverloadCandidate *, 4> PendingBest;
llvm::SmallVector<const NamedDecl *, 4> EquivalentCands;
-
- llvm::SmallVector<OverloadCandidate*, 4> PendingBest;
PendingBest.push_back(&*Best);
Best->Best = true;
@@ -11007,8 +11207,6 @@ OverloadCandidateSet::BestViableFunction(Sema &S, SourceLocation Loc,
}
}
}
-
- // If we found more than one best candidate, this is ambiguous.
if (Best == end())
return OR_Ambiguous;
@@ -11022,10 +11220,9 @@ OverloadCandidateSet::BestViableFunction(Sema &S, SourceLocation Loc,
return OR_No_Viable_Function;
}
- if (!EquivalentCands.empty())
+ if (NonDeducedCandidates.empty() && !EquivalentCands.empty())
S.diagnoseEquivalentInternalLinkageDeclarations(Loc, Best->Function,
EquivalentCands);
-
return OR_Success;
}
@@ -12733,6 +12930,9 @@ SmallVector<OverloadCandidate *, 32> OverloadCandidateSet::CompleteCandidates(
Sema &S, OverloadCandidateDisplayKind OCD, ArrayRef<Expr *> Args,
SourceLocation OpLoc,
llvm::function_ref<bool(OverloadCandidate &)> Filter) {
+
+ InjectNonDeducedTemplateCandidates(S);
+
// Sort the candidates by viability and position. Sorting directly would
// be prohibitive, so we make a set of pointers and sort those.
SmallVector<OverloadCandidate*, 32> Cands;
@@ -14708,18 +14908,23 @@ void Sema::LookupOverloadedBinOp(OverloadCandidateSet &CandidateSet,
// rewritten candidates using these functions if necessary.
AddNonMemberOperatorCandidates(Fns, Args, CandidateSet);
+ auto ReversedArgs = [&, Arr = ArrayRef<Expr *>{}]() mutable {
+ if (Arr.empty())
+ Arr = CandidateSet.getPersistentArgsArray(Args[1], Args[0]);
+ return Arr;
+ };
+
// Add operator candidates that are member functions.
AddMemberOperatorCandidates(Op, OpLoc, Args, CandidateSet);
if (CandidateSet.getRewriteInfo().allowsReversed(Op))
- AddMemberOperatorCandidates(Op, OpLoc, {Args[1], Args[0]}, CandidateSet,
+ AddMemberOperatorCandidates(Op, OpLoc, ReversedArgs(), CandidateSet,
OverloadCandidateParamOrder::Reversed);
// In C++20, also add any rewritten member candidates.
if (ExtraOp) {
AddMemberOperatorCandidates(ExtraOp, OpLoc, Args, CandidateSet);
if (CandidateSet.getRewriteInfo().allowsReversed(ExtraOp))
- AddMemberOperatorCandidates(ExtraOp, OpLoc, {Args[1], Args[0]},
- CandidateSet,
+ AddMemberOperatorCandidates(ExtraOp, OpLoc, ReversedArgs(), CandidateSet,
OverloadCandidateParamOrder::Reversed);
}
>From f1f96a6592cfa6aeb2efc3ca4899d3e456bc6f50 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Sat, 29 Mar 2025 12:34:55 +0100
Subject: [PATCH 02/32] avoid comparing types
---
clang/include/clang/Sema/Overload.h | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index 2cc7e1809e26c..4fb6bfe0aa55b 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -745,8 +745,9 @@ class Sema;
}
bool isPerfect(const ASTContext &C) const {
- return (isStandard() && Standard.isIdentityConversion() &&
- C.hasSameType(Standard.getFromType(), Standard.getToType(2))) ||
+ return (isStandard() && Standard.isIdentityConversion()
+ && !Standard.DirectBinding
+ ) ||
getKind() == StaticObjectArgumentConversion;
}
@@ -989,7 +990,7 @@ class Sema;
bool isPerfectMatch(const ASTContext &Ctx) const {
if (!Viable)
return false;
- for (auto &C : Conversions) {
+ for (const auto &C : Conversions) {
if (!C.isInitialized())
return false;
if (!C.isPerfect(Ctx))
>From 1144ae47669c9d6a5297d14e8512dc6f5c281223 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Sat, 29 Mar 2025 13:57:47 +0100
Subject: [PATCH 03/32] Fix logic
---
clang/include/clang/Sema/Overload.h | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index 4fb6bfe0aa55b..4bd94ce7bff94 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -745,10 +745,9 @@ class Sema;
}
bool isPerfect(const ASTContext &C) const {
- return (isStandard() && Standard.isIdentityConversion()
- && !Standard.DirectBinding
- ) ||
- getKind() == StaticObjectArgumentConversion;
+ return isStandard() && Standard.isIdentityConversion() &&
+ (!Standard.ReferenceBinding || C.hasSameType(Standard.getFromType(), Standard.getToType(2)))
+ ;
}
// True iff this is a conversion sequence from an initializer list to an
>From b35a6c4cc4d688453492599f79dc924c652da86d Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Sat, 29 Mar 2025 15:19:03 +0100
Subject: [PATCH 04/32] optimize
---
clang/lib/Sema/SemaOverload.cpp | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 337a7033bbfcc..30b2b76eaa213 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -11120,7 +11120,10 @@ OverloadCandidateSet::BestViableFunction(Sema &S, SourceLocation Loc,
return OR_Success;
}
}
- InjectNonDeducedTemplateCandidates(S);
+
+ if(!NonDeducedCandidates.empty())
+ InjectNonDeducedTemplateCandidates(S);
+
return BestViableFunctionImpl(S, Loc, Best);
}
>From 401772db790ae8141d144d05b431954dfbad294a Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Sat, 29 Mar 2025 15:26:52 +0100
Subject: [PATCH 05/32] optimize again
---
clang/lib/Sema/SemaOverload.cpp | 14 +++++---------
1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 30b2b76eaa213..c9c8dc00ec0c3 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -8514,13 +8514,6 @@ void Sema::AddNonMemberOperatorCandidates(
NamedDecl *D = F.getDecl()->getUnderlyingDecl();
ArrayRef<Expr *> FunctionArgs = Args;
- auto ReversedArgs = [&, Arr = ArrayRef<Expr *>{}]() mutable {
- if (Arr.empty())
- Arr = CandidateSet.getPersistentArgsArray(FunctionArgs[1],
- FunctionArgs[0]);
- return Arr;
- };
-
FunctionTemplateDecl *FunTmpl = dyn_cast<FunctionTemplateDecl>(D);
FunctionDecl *FD =
FunTmpl ? FunTmpl->getTemplatedDecl() : cast<FunctionDecl>(D);
@@ -8536,8 +8529,10 @@ void Sema::AddNonMemberOperatorCandidates(
AddTemplateOverloadCandidate(FunTmpl, F.getPair(), ExplicitTemplateArgs,
FunctionArgs, CandidateSet);
if (CandidateSet.getRewriteInfo().shouldAddReversed(*this, Args, FD)) {
+ ArrayRef<Expr *> Reversed = CandidateSet.getPersistentArgsArray(FunctionArgs[1],
+ FunctionArgs[0]);
AddTemplateOverloadCandidate(FunTmpl, F.getPair(), ExplicitTemplateArgs,
- ReversedArgs(), CandidateSet, false, false,
+ Reversed, CandidateSet, false, false,
true, ADLCallKind::NotADL,
OverloadCandidateParamOrder::Reversed);
}
@@ -8546,7 +8541,8 @@ void Sema::AddNonMemberOperatorCandidates(
continue;
AddOverloadCandidate(FD, F.getPair(), FunctionArgs, CandidateSet);
if (CandidateSet.getRewriteInfo().shouldAddReversed(*this, Args, FD))
- AddOverloadCandidate(FD, F.getPair(), ReversedArgs(), CandidateSet,
+ AddOverloadCandidate(FD, F.getPair(), {FunctionArgs[1],
+ FunctionArgs[0]}, CandidateSet,
false, false, true, false, ADLCallKind::NotADL, {},
OverloadCandidateParamOrder::Reversed);
}
>From a5acddee7e0fcaa00262162e3275ee9a6379bef5 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Mon, 31 Mar 2025 16:34:02 +0200
Subject: [PATCH 06/32] Cleanups, fix tests
---
clang/include/clang/Sema/Overload.h | 67 ++-
clang/include/clang/Sema/Sema.h | 23 -
clang/lib/Sema/SemaInit.cpp | 4 +
clang/lib/Sema/SemaOverload.cpp | 475 ++++++++++--------
.../constrant-satisfaction-conversions.cpp | 8 +-
.../SemaCXX/implicit-member-functions.cpp | 21 +-
.../instantiate-function-params.cpp | 7 +-
.../Templight/templight-empty-entries-fix.cpp | 34 +-
8 files changed, 336 insertions(+), 303 deletions(-)
diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index 4bd94ce7bff94..63339a917c3a4 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -408,6 +408,11 @@ class Sema;
Third == ICK_Identity;
}
+ bool isPerfect(const ASTContext &C) const {
+ return isIdentityConversion() &&
+ (!ReferenceBinding || C.hasSameType(getFromType(), getToType(2)));
+ }
+
ImplicitConversionRank getRank() const;
NarrowingKind
getNarrowingKind(ASTContext &Context, const Expr *Converted,
@@ -745,9 +750,7 @@ class Sema;
}
bool isPerfect(const ASTContext &C) const {
- return isStandard() && Standard.isIdentityConversion() &&
- (!Standard.ReferenceBinding || C.hasSameType(Standard.getFromType(), Standard.getToType(2)))
- ;
+ return isStandard() && Standard.isPerfect(C);
}
// True iff this is a conversion sequence from an initializer list to an
@@ -995,6 +998,8 @@ class Sema;
if (!C.isPerfect(Ctx))
return false;
}
+ if (isa_and_nonnull<CXXConversionDecl>(Function))
+ return FinalConversion.isPerfect(Ctx);
return true;
}
@@ -1034,7 +1039,7 @@ class Sema;
RewriteKind(CRK_None) {}
};
- struct NonDeducedConversionTemplateOverloadCandidate {
+ struct DeferredConversionTemplateOverloadCandidate {
FunctionTemplateDecl *FunctionTemplate;
DeclAccessPair FoundDecl;
CXXRecordDecl *ActingContext;
@@ -1049,7 +1054,7 @@ class Sema;
unsigned AllowResultConversion : 1;
};
- struct NonDeducedMethodTemplateOverloadCandidate {
+ struct DeferredMethodTemplateOverloadCandidate {
FunctionTemplateDecl *FunctionTemplate;
DeclAccessPair FoundDecl;
ArrayRef<Expr *> Args;
@@ -1064,7 +1069,7 @@ class Sema;
unsigned PartialOverloading : 1;
};
- struct NonDeducedFunctionTemplateOverloadCandidate {
+ struct DeferredFunctionTemplateOverloadCandidate {
FunctionTemplateDecl *FunctionTemplate;
DeclAccessPair FoundDecl;
ArrayRef<Expr *> Args;
@@ -1081,13 +1086,13 @@ class Sema;
unsigned AggregateCandidateDeduction : 1;
};
- using NonDeducedTemplateOverloadCandidate =
- std::variant<NonDeducedConversionTemplateOverloadCandidate,
- NonDeducedMethodTemplateOverloadCandidate,
- NonDeducedFunctionTemplateOverloadCandidate>;
+ using DeferredTemplateOverloadCandidate =
+ std::variant<DeferredConversionTemplateOverloadCandidate,
+ DeferredMethodTemplateOverloadCandidate,
+ DeferredFunctionTemplateOverloadCandidate>;
static_assert(
- std::is_trivially_destructible_v<NonDeducedTemplateOverloadCandidate>);
+ std::is_trivially_destructible_v<DeferredTemplateOverloadCandidate>);
/// OverloadCandidateSet - A set of overload candidates, used in C++
/// overload resolution (C++ 13.3).
@@ -1192,7 +1197,7 @@ class Sema;
private:
SmallVector<OverloadCandidate, 16> Candidates;
llvm::SmallPtrSet<uintptr_t, 16> Functions;
- SmallVector<NonDeducedTemplateOverloadCandidate, 8> NonDeducedCandidates;
+ SmallVector<DeferredTemplateOverloadCandidate, 8> DeferredCandidates;
// Allocator for ConversionSequenceLists. We store the first few of these
// inline to avoid allocation for small sets.
@@ -1204,6 +1209,7 @@ class Sema;
constexpr static unsigned NumInlineBytes =
32 * sizeof(ImplicitConversionSequence);
+
unsigned NumInlineBytesUsed = 0;
alignas(void *) char InlineSpace[NumInlineBytes];
@@ -1214,8 +1220,6 @@ class Sema;
/// from the slab allocator.
/// FIXME: It would probably be nice to have a SmallBumpPtrAllocator
/// instead.
- /// FIXME: Now that this only allocates ImplicitConversionSequences, do we
- /// want to un-generalize this?
template <typename T>
T *slabAllocate(unsigned N) {
// It's simpler if this doesn't need to consider alignment.
@@ -1253,6 +1257,9 @@ class Sema;
/// Whether diagnostics should be deferred.
bool shouldDeferDiags(Sema &S, ArrayRef<Expr *> Args, SourceLocation OpLoc);
+ // Whether the resolution of template candidates should be defered
+ bool shouldDeferTemplateArgumentDeduction(const LangOptions &Opts) const;
+
/// Determine when this overload candidate will be new to the
/// overload set.
bool isNewCandidate(Decl *F, OverloadCandidateParamOrder PO =
@@ -1277,10 +1284,10 @@ class Sema;
iterator end() { return Candidates.end(); }
size_t size() const {
- return Candidates.size() + NonDeducedCandidates.size();
+ return Candidates.size() + DeferredCandidates.size();
}
bool empty() const {
- return Candidates.empty() && NonDeducedCandidates.empty();
+ return Candidates.empty() && DeferredCandidates.empty();
}
/// Allocate storage for conversion sequences for NumConversions
@@ -1325,21 +1332,21 @@ class Sema;
return C;
}
- void AddNonDeducedTemplateCandidate(
+ void AddDeferredTemplateCandidate(
FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
ArrayRef<Expr *> Args, bool SuppressUserConversions,
bool PartialOverloading, bool AllowExplicit,
CallExpr::ADLCallKind IsADLCandidate, OverloadCandidateParamOrder PO,
bool AggregateCandidateDeduction);
- void AddNonDeducedMethodTemplateCandidate(
+ void AddDeferredMethodTemplateCandidate(
FunctionTemplateDecl *MethodTmpl, DeclAccessPair FoundDecl,
CXXRecordDecl *ActingContext, QualType ObjectType,
Expr::Classification ObjectClassification, ArrayRef<Expr *> Args,
bool SuppressUserConversions, bool PartialOverloading,
OverloadCandidateParamOrder PO);
- void AddNonDeducedConversionTemplateCandidate(
+ void AddDeferredConversionTemplateCandidate(
FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
CXXRecordDecl *ActingContext, Expr *From, QualType ToType,
bool AllowObjCConversionOnExplicit, bool AllowExplicit,
@@ -1351,10 +1358,6 @@ class Sema;
OverloadingResult BestViableFunction(Sema &S, SourceLocation Loc,
OverloadCandidateSet::iterator& Best);
- OverloadingResult
- BestViableFunctionImpl(Sema &S, SourceLocation Loc,
- OverloadCandidateSet::iterator &Best);
-
SmallVector<OverloadCandidate *, 32> CompleteCandidates(
Sema &S, OverloadCandidateDisplayKind OCD, ArrayRef<Expr *> Args,
SourceLocation OpLoc = SourceLocation(),
@@ -1383,6 +1386,14 @@ class Sema;
DestAS = AS;
}
+ private:
+ OverloadingResult ResultForBestCandidate(const iterator &Best);
+ void CudaExcludeWrongSideCandidates(Sema &S);
+ OverloadingResult
+ BestViableFunctionImpl(Sema &S, SourceLocation Loc,
+ OverloadCandidateSet::iterator &Best);
+ void PerfectViableFunction(Sema &S, SourceLocation Loc,
+ OverloadCandidateSet::iterator &Best);
};
bool isBetterOverloadCandidate(Sema &S, const OverloadCandidate &Cand1,
@@ -1431,6 +1442,16 @@ class Sema;
// parameter.
bool shouldEnforceArgLimit(bool PartialOverloading, FunctionDecl *Function);
+ inline bool OverloadCandidateSet::shouldDeferTemplateArgumentDeduction(
+ const LangOptions &Opts) const {
+ return
+ // For user defined conversion we need to check against different
+ // combination of CV qualifiers and look at any expicit specifier, so
+ // always deduce template candidate.
+ Kind != CSK_InitByUserDefinedConversion && Kind != CSK_CodeCompletion &&
+ Opts.CPlusPlus && (!Opts.CUDA || Opts.GPUExcludeWrongSideOverloads);
+ }
+
} // namespace clang
#endif // LLVM_CLANG_SEMA_OVERLOAD_H
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 34beea87e111a..82c3fe024b28b 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -10390,26 +10390,10 @@ class Sema final : public SemaBase {
OverloadCandidateSet &CandidateSet, bool SuppressUserConversions = false,
bool PartialOverloading = false, OverloadCandidateParamOrder PO = {});
- void AddMethodTemplateCandidateImmediately(
- OverloadCandidateSet &CandidateSet, FunctionTemplateDecl *MethodTmpl,
- DeclAccessPair FoundDecl, CXXRecordDecl *ActingContext,
- TemplateArgumentListInfo *ExplicitTemplateArgs, QualType ObjectType,
- Expr::Classification ObjectClassification, ArrayRef<Expr *> Args,
- bool SuppressUserConversions, bool PartialOverloading,
- OverloadCandidateParamOrder PO);
-
/// Add a C++ function template specialization as a candidate
/// in the candidate set, using template argument deduction to produce
/// an appropriate function template specialization.
- void AddTemplateOverloadCandidateImmediately(
- OverloadCandidateSet &CandidateSet,
- FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
- TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *> Args,
- bool SuppressUserConversions, bool PartialOverloading, bool AllowExplicit,
- ADLCallKind IsADLCandidate, OverloadCandidateParamOrder PO,
- bool AggregateCandidateDeduction);
-
void AddTemplateOverloadCandidate(
FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *> Args,
@@ -10454,13 +10438,6 @@ class Sema final : public SemaBase {
OverloadCandidateSet &CandidateSet, bool AllowObjCConversionOnExplicit,
bool AllowExplicit, bool AllowResultConversion = true);
- void AddTemplateConversionCandidateImmediately(
- OverloadCandidateSet &CandidateSet,
- FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
- CXXRecordDecl *ActingContext, Expr *From, QualType ToType,
- bool AllowObjCConversionOnExplicit, bool AllowExplicit,
- bool AllowResultConversion);
-
/// AddSurrogateCandidate - Adds a "surrogate" candidate function that
/// converts the given @c Object to a function pointer via the
/// conversion function @c Conversion, and then attempts to call it
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index e8a1e8fff3314..460bf64543821 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -10029,8 +10029,12 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
// When [...] the constructor [...] is a candidate by
// - [over.match.copy] (in all cases)
if (TD) {
+
+ // As template candidates are not deduced immediately,
+ // persist the arry in the overload set.
MutableArrayRef<Expr *> TmpInits =
Candidates.getPersistentArgsArray(Inits.size());
+
for (auto [I, E] : llvm::enumerate(Inits)) {
if (auto *DI = dyn_cast<DesignatedInitExpr>(E))
TmpInits[I] = DI->getInit();
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index c9c8dc00ec0c3..981b65237acb8 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -7797,35 +7797,12 @@ void Sema::AddMethodCandidate(
}
}
-void Sema::AddMethodTemplateCandidate(
+static void AddMethodTemplateCandidateImmediately(
+ Sema &S, OverloadCandidateSet &CandidateSet,
FunctionTemplateDecl *MethodTmpl, DeclAccessPair FoundDecl,
CXXRecordDecl *ActingContext,
TemplateArgumentListInfo *ExplicitTemplateArgs, QualType ObjectType,
Expr::Classification ObjectClassification, ArrayRef<Expr *> Args,
- OverloadCandidateSet &CandidateSet, bool SuppressUserConversions,
- bool PartialOverloading, OverloadCandidateParamOrder PO) {
- if (!CandidateSet.isNewCandidate(MethodTmpl, PO))
- return;
-
- if (CandidateSet.getKind() == OverloadCandidateSet::CSK_CodeCompletion ||
- ExplicitTemplateArgs) {
- AddMethodTemplateCandidateImmediately(
- CandidateSet, MethodTmpl, FoundDecl, ActingContext,
- ExplicitTemplateArgs, ObjectType, ObjectClassification, Args,
- SuppressUserConversions, PartialOverloading, PO);
- return;
- }
-
- CandidateSet.AddNonDeducedMethodTemplateCandidate(
- MethodTmpl, FoundDecl, ActingContext, ObjectType, ObjectClassification,
- Args, SuppressUserConversions, PartialOverloading, PO);
-}
-
-void Sema::AddMethodTemplateCandidateImmediately(
- OverloadCandidateSet &CandidateSet, FunctionTemplateDecl *MethodTmpl,
- DeclAccessPair FoundDecl, CXXRecordDecl *ActingContext,
- TemplateArgumentListInfo *ExplicitTemplateArgs, QualType ObjectType,
- Expr::Classification ObjectClassification, ArrayRef<Expr *> Args,
bool SuppressUserConversions, bool PartialOverloading,
OverloadCandidateParamOrder PO) {
@@ -7841,12 +7818,12 @@ void Sema::AddMethodTemplateCandidateImmediately(
TemplateDeductionInfo Info(CandidateSet.getLocation());
FunctionDecl *Specialization = nullptr;
ConversionSequenceList Conversions;
- if (TemplateDeductionResult Result = DeduceTemplateArguments(
+ if (TemplateDeductionResult Result = S.DeduceTemplateArguments(
MethodTmpl, ExplicitTemplateArgs, Args, Specialization, Info,
PartialOverloading, /*AggregateDeductionCandidate=*/false,
/*PartialOrdering=*/false, ObjectType, ObjectClassification,
[&](ArrayRef<QualType> ParamTypes) {
- return CheckNonDependentConversions(
+ return S.CheckNonDependentConversions(
MethodTmpl, ParamTypes, Args, CandidateSet, Conversions,
SuppressUserConversions, ActingContext, ObjectType,
ObjectClassification, PO);
@@ -7869,7 +7846,7 @@ void Sema::AddMethodTemplateCandidateImmediately(
else {
Candidate.FailureKind = ovl_fail_bad_deduction;
Candidate.DeductionFailure =
- MakeDeductionFailureInfo(Context, Result, Info);
+ MakeDeductionFailureInfo(S.Context, Result, Info);
}
return;
}
@@ -7879,48 +7856,49 @@ void Sema::AddMethodTemplateCandidateImmediately(
assert(Specialization && "Missing member function template specialization?");
assert(isa<CXXMethodDecl>(Specialization) &&
"Specialization is not a member function?");
- AddMethodCandidate(cast<CXXMethodDecl>(Specialization), FoundDecl,
- ActingContext, ObjectType, ObjectClassification, Args,
- CandidateSet, SuppressUserConversions, PartialOverloading,
- Conversions, PO, Info.hasStrictPackMatch());
+ S.AddMethodCandidate(
+ cast<CXXMethodDecl>(Specialization), FoundDecl, ActingContext, ObjectType,
+ ObjectClassification, Args, CandidateSet, SuppressUserConversions,
+ PartialOverloading, Conversions, PO, Info.hasStrictPackMatch());
}
-/// Determine whether a given function template has a simple explicit specifier
-/// or a non-value-dependent explicit-specification that evaluates to true.
-static bool isNonDependentlyExplicit(FunctionTemplateDecl *FTD) {
- return ExplicitSpecifier::getFromDecl(FTD->getTemplatedDecl()).isExplicit();
-}
-
-void Sema::AddTemplateOverloadCandidate(
- FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
- TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *> Args,
+void Sema::AddMethodTemplateCandidate(
+ FunctionTemplateDecl *MethodTmpl, DeclAccessPair FoundDecl,
+ CXXRecordDecl *ActingContext,
+ TemplateArgumentListInfo *ExplicitTemplateArgs, QualType ObjectType,
+ Expr::Classification ObjectClassification, ArrayRef<Expr *> Args,
OverloadCandidateSet &CandidateSet, bool SuppressUserConversions,
- bool PartialOverloading, bool AllowExplicit, ADLCallKind IsADLCandidate,
- OverloadCandidateParamOrder PO, bool AggregateCandidateDeduction) {
- if (!CandidateSet.isNewCandidate(FunctionTemplate, PO))
+ bool PartialOverloading, OverloadCandidateParamOrder PO) {
+ if (!CandidateSet.isNewCandidate(MethodTmpl, PO))
return;
- if (CandidateSet.getKind() == OverloadCandidateSet::CSK_CodeCompletion ||
- ExplicitTemplateArgs) {
- AddTemplateOverloadCandidateImmediately(
- CandidateSet, FunctionTemplate, FoundDecl, ExplicitTemplateArgs, Args,
- SuppressUserConversions, PartialOverloading, AllowExplicit,
- IsADLCandidate, PO, AggregateCandidateDeduction);
+ if (ExplicitTemplateArgs ||
+ !CandidateSet.shouldDeferTemplateArgumentDeduction(getLangOpts())) {
+ AddMethodTemplateCandidateImmediately(
+ *this, CandidateSet, MethodTmpl, FoundDecl, ActingContext,
+ ExplicitTemplateArgs, ObjectType, ObjectClassification, Args,
+ SuppressUserConversions, PartialOverloading, PO);
return;
}
- CandidateSet.AddNonDeducedTemplateCandidate(
- FunctionTemplate, FoundDecl, Args, SuppressUserConversions,
- PartialOverloading, AllowExplicit, IsADLCandidate, PO,
- AggregateCandidateDeduction);
+ CandidateSet.AddDeferredMethodTemplateCandidate(
+ MethodTmpl, FoundDecl, ActingContext, ObjectType, ObjectClassification,
+ Args, SuppressUserConversions, PartialOverloading, PO);
}
-void Sema::AddTemplateOverloadCandidateImmediately(
- OverloadCandidateSet &CandidateSet, FunctionTemplateDecl *FunctionTemplate,
- DeclAccessPair FoundDecl, TemplateArgumentListInfo *ExplicitTemplateArgs,
- ArrayRef<Expr *> Args, bool SuppressUserConversions,
- bool PartialOverloading, bool AllowExplicit, ADLCallKind IsADLCandidate,
- OverloadCandidateParamOrder PO, bool AggregateCandidateDeduction) {
+/// Determine whether a given function template has a simple explicit specifier
+/// or a non-value-dependent explicit-specification that evaluates to true.
+static bool isNonDependentlyExplicit(FunctionTemplateDecl *FTD) {
+ return ExplicitSpecifier::getFromDecl(FTD->getTemplatedDecl()).isExplicit();
+}
+
+static void AddTemplateOverloadCandidateImmediately(
+ Sema &S, OverloadCandidateSet &CandidateSet,
+ FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
+ TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *> Args,
+ bool SuppressUserConversions, bool PartialOverloading, bool AllowExplicit,
+ Sema::ADLCallKind IsADLCandidate, OverloadCandidateParamOrder PO,
+ bool AggregateCandidateDeduction) {
// If the function template has a non-dependent explicit specification,
// exclude it now if appropriate; we are not permitted to perform deduction
@@ -7947,14 +7925,14 @@ void Sema::AddTemplateOverloadCandidateImmediately(
FunctionTemplate->getTemplateDepth());
FunctionDecl *Specialization = nullptr;
ConversionSequenceList Conversions;
- if (TemplateDeductionResult Result = DeduceTemplateArguments(
+ if (TemplateDeductionResult Result = S.DeduceTemplateArguments(
FunctionTemplate, ExplicitTemplateArgs, Args, Specialization, Info,
PartialOverloading, AggregateCandidateDeduction,
/*PartialOrdering=*/false,
/*ObjectType=*/QualType(),
/*ObjectClassification=*/Expr::Classification(),
[&](ArrayRef<QualType> ParamTypes) {
- return CheckNonDependentConversions(
+ return S.CheckNonDependentConversions(
FunctionTemplate, ParamTypes, Args, CandidateSet, Conversions,
SuppressUserConversions, nullptr, QualType(), {}, PO);
});
@@ -7979,7 +7957,7 @@ void Sema::AddTemplateOverloadCandidateImmediately(
else {
Candidate.FailureKind = ovl_fail_bad_deduction;
Candidate.DeductionFailure =
- MakeDeductionFailureInfo(Context, Result, Info);
+ MakeDeductionFailureInfo(S.Context, Result, Info);
}
return;
}
@@ -7987,7 +7965,7 @@ void Sema::AddTemplateOverloadCandidateImmediately(
// Add the function template specialization produced by template argument
// deduction as a candidate.
assert(Specialization && "Missing function template specialization?");
- AddOverloadCandidate(
+ S.AddOverloadCandidate(
Specialization, FoundDecl, Args, CandidateSet, SuppressUserConversions,
PartialOverloading, AllowExplicit,
/*AllowExplicitConversions=*/false, IsADLCandidate, Conversions, PO,
@@ -7995,6 +7973,30 @@ void Sema::AddTemplateOverloadCandidateImmediately(
Info.hasStrictPackMatch());
}
+void Sema::AddTemplateOverloadCandidate(
+ FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
+ TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *> Args,
+ OverloadCandidateSet &CandidateSet, bool SuppressUserConversions,
+ bool PartialOverloading, bool AllowExplicit, ADLCallKind IsADLCandidate,
+ OverloadCandidateParamOrder PO, bool AggregateCandidateDeduction) {
+ if (!CandidateSet.isNewCandidate(FunctionTemplate, PO))
+ return;
+
+ if (ExplicitTemplateArgs ||
+ !CandidateSet.shouldDeferTemplateArgumentDeduction(getLangOpts())) {
+ AddTemplateOverloadCandidateImmediately(
+ *this, CandidateSet, FunctionTemplate, FoundDecl, ExplicitTemplateArgs,
+ Args, SuppressUserConversions, PartialOverloading, AllowExplicit,
+ IsADLCandidate, PO, AggregateCandidateDeduction);
+ return;
+ }
+
+ CandidateSet.AddDeferredTemplateCandidate(
+ FunctionTemplate, FoundDecl, Args, SuppressUserConversions,
+ PartialOverloading, AllowExplicit, IsADLCandidate, PO,
+ AggregateCandidateDeduction);
+}
+
bool Sema::CheckNonDependentConversions(
FunctionTemplateDecl *FunctionTemplate, ArrayRef<QualType> ParamTypes,
ArrayRef<Expr *> Args, OverloadCandidateSet &CandidateSet,
@@ -8310,34 +8312,11 @@ void Sema::AddConversionCandidate(
}
}
-void Sema::AddTemplateConversionCandidate(
+static void AddTemplateConversionCandidateImmediately(
+ Sema &S, OverloadCandidateSet &CandidateSet,
FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
- CXXRecordDecl *ActingDC, Expr *From, QualType ToType,
- OverloadCandidateSet &CandidateSet, bool AllowObjCConversionOnExplicit,
- bool AllowExplicit, bool AllowResultConversion) {
- assert(isa<CXXConversionDecl>(FunctionTemplate->getTemplatedDecl()) &&
- "Only conversion function templates permitted here");
-
- if (!CandidateSet.isNewCandidate(FunctionTemplate))
- return;
-
- if (CandidateSet.getKind() == OverloadCandidateSet::CSK_CodeCompletion) {
- AddTemplateConversionCandidateImmediately(
- CandidateSet, FunctionTemplate, FoundDecl, ActingDC, From, ToType,
- AllowObjCConversionOnExplicit, AllowExplicit, AllowResultConversion);
-
- return;
- }
-
- CandidateSet.AddNonDeducedConversionTemplateCandidate(
- FunctionTemplate, FoundDecl, ActingDC, From, ToType,
- AllowObjCConversionOnExplicit, AllowExplicit, AllowResultConversion);
-}
-
-void Sema::AddTemplateConversionCandidateImmediately(
- OverloadCandidateSet &CandidateSet, FunctionTemplateDecl *FunctionTemplate,
- DeclAccessPair FoundDecl, CXXRecordDecl *ActingContext, Expr *From,
- QualType ToType, bool AllowObjCConversionOnExplicit, bool AllowExplicit,
+ CXXRecordDecl *ActingContext, Expr *From, QualType ToType,
+ bool AllowObjCConversionOnExplicit, bool AllowExplicit,
bool AllowResultConversion) {
// If the function template has a non-dependent explicit specification,
@@ -8353,11 +8332,11 @@ void Sema::AddTemplateConversionCandidateImmediately(
}
QualType ObjectType = From->getType();
- Expr::Classification ObjectClassification = From->Classify(getASTContext());
+ Expr::Classification ObjectClassification = From->Classify(S.Context);
TemplateDeductionInfo Info(CandidateSet.getLocation());
CXXConversionDecl *Specialization = nullptr;
- if (TemplateDeductionResult Result = DeduceTemplateArguments(
+ if (TemplateDeductionResult Result = S.DeduceTemplateArguments(
FunctionTemplate, ObjectType, ObjectClassification, ToType,
Specialization, Info);
Result != TemplateDeductionResult::Success) {
@@ -8368,17 +8347,42 @@ void Sema::AddTemplateConversionCandidateImmediately(
Candidate.FailureKind = ovl_fail_bad_deduction;
Candidate.ExplicitCallArguments = 1;
Candidate.DeductionFailure =
- MakeDeductionFailureInfo(Context, Result, Info);
+ MakeDeductionFailureInfo(S.Context, Result, Info);
return;
}
// Add the conversion function template specialization produced by
// template argument deduction as a candidate.
assert(Specialization && "Missing function template specialization?");
- AddConversionCandidate(Specialization, FoundDecl, ActingContext, From, ToType,
- CandidateSet, AllowObjCConversionOnExplicit,
- AllowExplicit, AllowResultConversion,
- Info.hasStrictPackMatch());
+ S.AddConversionCandidate(Specialization, FoundDecl, ActingContext, From,
+ ToType, CandidateSet, AllowObjCConversionOnExplicit,
+ AllowExplicit, AllowResultConversion,
+ Info.hasStrictPackMatch());
+}
+
+void Sema::AddTemplateConversionCandidate(
+ FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
+ CXXRecordDecl *ActingDC, Expr *From, QualType ToType,
+ OverloadCandidateSet &CandidateSet, bool AllowObjCConversionOnExplicit,
+ bool AllowExplicit, bool AllowResultConversion) {
+ assert(isa<CXXConversionDecl>(FunctionTemplate->getTemplatedDecl()) &&
+ "Only conversion function templates permitted here");
+
+ if (!CandidateSet.isNewCandidate(FunctionTemplate))
+ return;
+
+ if (!CandidateSet.shouldDeferTemplateArgumentDeduction(getLangOpts())) {
+ AddTemplateConversionCandidateImmediately(
+ *this, CandidateSet, FunctionTemplate, FoundDecl, ActingDC, From,
+ ToType, AllowObjCConversionOnExplicit, AllowExplicit,
+ AllowResultConversion);
+
+ return;
+ }
+
+ CandidateSet.AddDeferredConversionTemplateCandidate(
+ FunctionTemplate, FoundDecl, ActingDC, From, ToType,
+ AllowObjCConversionOnExplicit, AllowExplicit, AllowResultConversion);
}
void Sema::AddSurrogateCandidate(CXXConversionDecl *Conversion,
@@ -8529,11 +8533,14 @@ void Sema::AddNonMemberOperatorCandidates(
AddTemplateOverloadCandidate(FunTmpl, F.getPair(), ExplicitTemplateArgs,
FunctionArgs, CandidateSet);
if (CandidateSet.getRewriteInfo().shouldAddReversed(*this, Args, FD)) {
- ArrayRef<Expr *> Reversed = CandidateSet.getPersistentArgsArray(FunctionArgs[1],
- FunctionArgs[0]);
+
+ // As template candidates are not deduced immediately,
+ // persist the arry in the overload set.
+ ArrayRef<Expr *> Reversed = CandidateSet.getPersistentArgsArray(
+ FunctionArgs[1], FunctionArgs[0]);
AddTemplateOverloadCandidate(FunTmpl, F.getPair(), ExplicitTemplateArgs,
- Reversed, CandidateSet, false, false,
- true, ADLCallKind::NotADL,
+ Reversed, CandidateSet, false, false, true,
+ ADLCallKind::NotADL,
OverloadCandidateParamOrder::Reversed);
}
} else {
@@ -8541,8 +8548,8 @@ void Sema::AddNonMemberOperatorCandidates(
continue;
AddOverloadCandidate(FD, F.getPair(), FunctionArgs, CandidateSet);
if (CandidateSet.getRewriteInfo().shouldAddReversed(*this, Args, FD))
- AddOverloadCandidate(FD, F.getPair(), {FunctionArgs[1],
- FunctionArgs[0]}, CandidateSet,
+ AddOverloadCandidate(FD, F.getPair(),
+ {FunctionArgs[1], FunctionArgs[0]}, CandidateSet,
false, false, true, false, ADLCallKind::NotADL, {},
OverloadCandidateParamOrder::Reversed);
}
@@ -10267,11 +10274,7 @@ Sema::AddArgumentDependentLookupCandidates(DeclarationName Name,
// FIXME: Pass in the explicit template arguments?
ArgumentDependentLookup(Name, Loc, Args, Fns);
- auto ReversedArgs = [&, Arr = ArrayRef<Expr *>{}]() mutable {
- if (Arr.empty())
- Arr = CandidateSet.getPersistentArgsArray(Args[1], Args[0]);
- return Arr;
- };
+ ArrayRef<Expr *> ReversedArgs;
// Erase all of the candidates we already knew about.
for (OverloadCandidateSet::iterator Cand = CandidateSet.begin(),
@@ -10299,7 +10302,7 @@ Sema::AddArgumentDependentLookupCandidates(DeclarationName Name,
/*AllowExplicitConversion=*/false, ADLCallKind::UsesADL);
if (CandidateSet.getRewriteInfo().shouldAddReversed(*this, Args, FD)) {
AddOverloadCandidate(
- FD, FoundDecl, ReversedArgs(), CandidateSet,
+ FD, FoundDecl, {Args[1], Args[0]}, CandidateSet,
/*SuppressUserConversions=*/false, PartialOverloading,
/*AllowExplicit=*/true, /*AllowExplicitConversion=*/false,
ADLCallKind::UsesADL, {}, OverloadCandidateParamOrder::Reversed);
@@ -10312,8 +10315,14 @@ Sema::AddArgumentDependentLookupCandidates(DeclarationName Name,
/*AllowExplicit=*/true, ADLCallKind::UsesADL);
if (CandidateSet.getRewriteInfo().shouldAddReversed(
*this, Args, FTD->getTemplatedDecl())) {
+
+ // As template candidates are not deduced immediately,
+ // persist the arry in the overload set.
+ if (ReversedArgs.empty())
+ ReversedArgs = CandidateSet.getPersistentArgsArray(Args[1], Args[0]);
+
AddTemplateOverloadCandidate(
- FTD, FoundDecl, ExplicitTemplateArgs, ReversedArgs(), CandidateSet,
+ FTD, FoundDecl, ExplicitTemplateArgs, ReversedArgs, CandidateSet,
/*SuppressUserConversions=*/false, PartialOverloading,
/*AllowExplicit=*/true, ADLCallKind::UsesADL,
OverloadCandidateParamOrder::Reversed);
@@ -10987,91 +10996,143 @@ bool OverloadCandidate::NotValidBecauseConstraintExprHasError() const {
->Satisfaction.ContainsErrors;
}
-void OverloadCandidateSet::AddNonDeducedTemplateCandidate(
+void OverloadCandidateSet::AddDeferredTemplateCandidate(
FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
ArrayRef<Expr *> Args, bool SuppressUserConversions,
bool PartialOverloading, bool AllowExplicit,
CallExpr::ADLCallKind IsADLCandidate, OverloadCandidateParamOrder PO,
bool AggregateCandidateDeduction) {
- NonDeducedFunctionTemplateOverloadCandidate C{FunctionTemplate,
- FoundDecl,
- Args,
- IsADLCandidate,
- PO,
- SuppressUserConversions,
- PartialOverloading,
- AllowExplicit,
- AggregateCandidateDeduction};
- NonDeducedCandidates.emplace_back(std::move(C));
+ DeferredFunctionTemplateOverloadCandidate C{FunctionTemplate,
+ FoundDecl,
+ Args,
+ IsADLCandidate,
+ PO,
+ SuppressUserConversions,
+ PartialOverloading,
+ AllowExplicit,
+ AggregateCandidateDeduction};
+ DeferredCandidates.emplace_back(std::move(C));
}
-void OverloadCandidateSet::AddNonDeducedMethodTemplateCandidate(
+void OverloadCandidateSet::AddDeferredMethodTemplateCandidate(
FunctionTemplateDecl *MethodTmpl, DeclAccessPair FoundDecl,
CXXRecordDecl *ActingContext, QualType ObjectType,
Expr::Classification ObjectClassification, ArrayRef<Expr *> Args,
bool SuppressUserConversions, bool PartialOverloading,
OverloadCandidateParamOrder PO) {
- NonDeducedMethodTemplateOverloadCandidate C{
+ DeferredMethodTemplateOverloadCandidate C{
MethodTmpl, FoundDecl, Args, ActingContext,
ObjectClassification, ObjectType, PO, SuppressUserConversions,
PartialOverloading};
- NonDeducedCandidates.emplace_back(std::move(C));
+ DeferredCandidates.emplace_back(std::move(C));
}
-void OverloadCandidateSet::AddNonDeducedConversionTemplateCandidate(
+void OverloadCandidateSet::AddDeferredConversionTemplateCandidate(
FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
CXXRecordDecl *ActingContext, Expr *From, QualType ToType,
bool AllowObjCConversionOnExplicit, bool AllowExplicit,
bool AllowResultConversion) {
- NonDeducedConversionTemplateOverloadCandidate C{
+ DeferredConversionTemplateOverloadCandidate C{
FunctionTemplate, FoundDecl,
ActingContext, From,
ToType, AllowObjCConversionOnExplicit,
AllowExplicit, AllowResultConversion};
- NonDeducedCandidates.emplace_back(std::move(C));
+ DeferredCandidates.emplace_back(std::move(C));
}
static void
AddTemplateOverloadCandidate(Sema &S, OverloadCandidateSet &CandidateSet,
- NonDeducedMethodTemplateOverloadCandidate &&C) {
+ DeferredMethodTemplateOverloadCandidate &&C) {
- S.AddMethodTemplateCandidateImmediately(
- CandidateSet, C.FunctionTemplate, C.FoundDecl, C.ActingContext,
+ AddMethodTemplateCandidateImmediately(
+ S, CandidateSet, C.FunctionTemplate, C.FoundDecl, C.ActingContext,
/*ExplicitTemplateArgs=*/nullptr, C.ObjectType, C.ObjectClassification,
C.Args, C.SuppressUserConversions, C.PartialOverloading, C.PO);
}
static void
AddTemplateOverloadCandidate(Sema &S, OverloadCandidateSet &CandidateSet,
- NonDeducedFunctionTemplateOverloadCandidate &&C) {
- S.AddTemplateOverloadCandidateImmediately(
- CandidateSet, C.FunctionTemplate, C.FoundDecl,
+ DeferredFunctionTemplateOverloadCandidate &&C) {
+ AddTemplateOverloadCandidateImmediately(
+ S, CandidateSet, C.FunctionTemplate, C.FoundDecl,
/*ExplicitTemplateArgs=*/nullptr, C.Args, C.SuppressUserConversions,
C.PartialOverloading, C.AllowExplicit, C.IsADLCandidate, C.PO,
C.AggregateCandidateDeduction);
}
-static void AddTemplateOverloadCandidate(
- Sema &S, OverloadCandidateSet &CandidateSet,
- NonDeducedConversionTemplateOverloadCandidate &&C) {
- return S.AddTemplateConversionCandidateImmediately(
- CandidateSet, C.FunctionTemplate, C.FoundDecl, C.ActingContext, C.From,
+static void
+AddTemplateOverloadCandidate(Sema &S, OverloadCandidateSet &CandidateSet,
+ DeferredConversionTemplateOverloadCandidate &&C) {
+ return AddTemplateConversionCandidateImmediately(
+ S, CandidateSet, C.FunctionTemplate, C.FoundDecl, C.ActingContext, C.From,
C.ToType, C.AllowObjCConversionOnExplicit, C.AllowExplicit,
C.AllowResultConversion);
}
void OverloadCandidateSet::InjectNonDeducedTemplateCandidates(Sema &S) {
- Candidates.reserve(Candidates.size() + NonDeducedCandidates.size());
- for (auto &&Elem : NonDeducedCandidates) {
+ Candidates.reserve(Candidates.size() + DeferredCandidates.size());
+ for (auto &&Elem : DeferredCandidates) {
std::visit(
[&](auto &&Cand) {
AddTemplateOverloadCandidate(S, *this, std::move(Cand));
},
Elem);
}
- NonDeducedCandidates.clear();
+ DeferredCandidates.clear();
+}
+
+OverloadingResult
+OverloadCandidateSet::ResultForBestCandidate(const iterator &Best) {
+ Best->Best = true;
+ if (Best->Function && Best->Function->isDeleted())
+ return OR_Deleted;
+ if (auto *M = dyn_cast_or_null<CXXMethodDecl>(Best->Function);
+ Kind == CSK_AddressOfOverloadSet && M &&
+ M->isImplicitObjectMemberFunction()) {
+ return OR_No_Viable_Function;
+ }
+ return OR_Success;
+}
+
+void OverloadCandidateSet::CudaExcludeWrongSideCandidates(Sema &S) {
+ // [CUDA] HD->H or HD->D calls are technically not allowed by CUDA but
+ // are accepted by both clang and NVCC. However, during a particular
+ // compilation mode only one call variant is viable. We need to
+ // exclude non-viable overload candidates from consideration based
+ // only on their host/device attributes. Specifically, if one
+ // candidate call is WrongSide and the other is SameSide, we ignore
+ // the WrongSide candidate.
+ // We only need to remove wrong-sided candidates here if
+ // -fgpu-exclude-wrong-side-overloads is off. When
+ // -fgpu-exclude-wrong-side-overloads is on, all candidates are compared
+ // uniformly in isBetterOverloadCandidate.
+ if (!S.getLangOpts().CUDA || S.getLangOpts().GPUExcludeWrongSideOverloads)
+ return;
+ const FunctionDecl *Caller = S.getCurFunctionDecl(/*AllowLambda=*/true);
+
+ bool ContainsSameSideCandidate =
+ llvm::any_of(Candidates, [&](const OverloadCandidate &Cand) {
+ // Check viable function only.
+ return Cand.Viable && Cand.Function &&
+ S.CUDA().IdentifyPreference(Caller, Cand.Function) ==
+ SemaCUDA::CFP_SameSide;
+ });
+ if (!ContainsSameSideCandidate)
+ return;
+
+ auto IsWrongSideCandidate = [&](const OverloadCandidate &Cand) {
+ // Check viable function only to avoid unnecessary data copying/moving.
+ return Cand.Viable && Cand.Function &&
+ S.CUDA().IdentifyPreference(Caller, Cand.Function) ==
+ SemaCUDA::CFP_WrongSide;
+ };
+
+ for (auto &Cand : Candidates) {
+ if (IsWrongSideCandidate(Cand))
+ Cand.Viable = false;
+ }
}
/// Computes the best viable function (C++ 13.3.3)
@@ -11088,41 +11149,56 @@ OverloadingResult
OverloadCandidateSet::BestViableFunction(Sema &S, SourceLocation Loc,
iterator &Best) {
- bool TwoPhaseResolution =
- !NonDeducedCandidates.empty() && Kind != CSK_CodeCompletion &&
- Kind != CSK_InitByUserDefinedConversion && Kind != CSK_InitByConstructor;
+ assert(shouldDeferTemplateArgumentDeduction(S.getLangOpts()) ||
+ DeferredCandidates.empty() &&
+ "Unexpected deferred template candidate");
+
+ if (S.getLangOpts().CUDA)
+ CudaExcludeWrongSideCandidates(S);
+
+ bool TwoPhaseResolution = !DeferredCandidates.empty();
if (TwoPhaseResolution) {
- Best = end();
- for (auto It = begin(); It != end(); ++It) {
- if (It->isPerfectMatch(S.getASTContext())) {
- if (Best == end()) {
- Best = It;
- } else {
- Best = end();
- break;
- }
- }
- }
- if (Best != end()) {
- Best->Best = true;
- if (Best->Function && Best->Function->isDeleted())
- return OR_Deleted;
- if (auto *M = dyn_cast_or_null<CXXMethodDecl>(Best->Function);
- Kind == CSK_AddressOfOverloadSet && M &&
- M->isImplicitObjectMemberFunction()) {
- return OR_No_Viable_Function;
- }
- return OR_Success;
- }
- }
- if(!NonDeducedCandidates.empty())
+ PerfectViableFunction(S, Loc, Best);
+ if (Best != end())
+ return ResultForBestCandidate(Best);
+
InjectNonDeducedTemplateCandidates(S);
+ if (S.getLangOpts().CUDA)
+ CudaExcludeWrongSideCandidates(S);
+ }
+
return BestViableFunctionImpl(S, Loc, Best);
}
+void OverloadCandidateSet::PerfectViableFunction(
+ Sema &S, SourceLocation Loc, OverloadCandidateSet::iterator &Best) {
+ Best = end();
+ for (auto It = begin(); It != end(); ++It) {
+ if (It->isPerfectMatch(S.getASTContext())) {
+ if (Best == end()) {
+ Best = It;
+ } else {
+ if (Best->Function && It->Function) {
+ FunctionDecl *D =
+ S.getMoreConstrainedFunction(Best->Function, It->Function);
+ if (D == nullptr) {
+ Best = end();
+ break;
+ }
+ if (D == It->Function)
+ Best = It;
+ continue;
+ }
+ Best = end();
+ break;
+ }
+ }
+ }
+}
+
OverloadingResult OverloadCandidateSet::BestViableFunctionImpl(
Sema &S, SourceLocation Loc, OverloadCandidateSet::iterator &Best) {
@@ -11131,37 +11207,6 @@ OverloadingResult OverloadCandidateSet::BestViableFunctionImpl(
std::transform(begin(), end(), std::back_inserter(Candidates),
[](OverloadCandidate &Cand) { return &Cand; });
- // [CUDA] HD->H or HD->D calls are technically not allowed by CUDA but
- // are accepted by both clang and NVCC. However, during a particular
- // compilation mode only one call variant is viable. We need to
- // exclude non-viable overload candidates from consideration based
- // only on their host/device attributes. Specifically, if one
- // candidate call is WrongSide and the other is SameSide, we ignore
- // the WrongSide candidate.
- // We only need to remove wrong-sided candidates here if
- // -fgpu-exclude-wrong-side-overloads is off. When
- // -fgpu-exclude-wrong-side-overloads is on, all candidates are compared
- // uniformly in isBetterOverloadCandidate.
- if (S.getLangOpts().CUDA && !S.getLangOpts().GPUExcludeWrongSideOverloads) {
- const FunctionDecl *Caller = S.getCurFunctionDecl(/*AllowLambda=*/true);
- bool ContainsSameSideCandidate =
- llvm::any_of(Candidates, [&](OverloadCandidate *Cand) {
- // Check viable function only.
- return Cand->Viable && Cand->Function &&
- S.CUDA().IdentifyPreference(Caller, Cand->Function) ==
- SemaCUDA::CFP_SameSide;
- });
- if (ContainsSameSideCandidate) {
- auto IsWrongSideCandidate = [&](OverloadCandidate *Cand) {
- // Check viable function only to avoid unnecessary data copying/moving.
- return Cand->Viable && Cand->Function &&
- S.CUDA().IdentifyPreference(Caller, Cand->Function) ==
- SemaCUDA::CFP_WrongSide;
- };
- llvm::erase_if(Candidates, IsWrongSideCandidate);
- }
- }
-
Best = end();
for (auto *Cand : Candidates) {
Cand->Best = false;
@@ -11206,23 +11251,16 @@ OverloadingResult OverloadCandidateSet::BestViableFunctionImpl(
}
}
}
+
if (Best == end())
return OR_Ambiguous;
- // Best is the best viable function.
- if (Best->Function && Best->Function->isDeleted())
- return OR_Deleted;
+ OverloadingResult R = ResultForBestCandidate(Best);
- if (auto *M = dyn_cast_or_null<CXXMethodDecl>(Best->Function);
- Kind == CSK_AddressOfOverloadSet && M &&
- M->isImplicitObjectMemberFunction()) {
- return OR_No_Viable_Function;
- }
-
- if (NonDeducedCandidates.empty() && !EquivalentCands.empty())
+ if (DeferredCandidates.empty() && !EquivalentCands.empty())
S.diagnoseEquivalentInternalLinkageDeclarations(Loc, Best->Function,
EquivalentCands);
- return OR_Success;
+ return R;
}
namespace {
@@ -14907,23 +14945,24 @@ void Sema::LookupOverloadedBinOp(OverloadCandidateSet &CandidateSet,
// rewritten candidates using these functions if necessary.
AddNonMemberOperatorCandidates(Fns, Args, CandidateSet);
- auto ReversedArgs = [&, Arr = ArrayRef<Expr *>{}]() mutable {
- if (Arr.empty())
- Arr = CandidateSet.getPersistentArgsArray(Args[1], Args[0]);
- return Arr;
- };
+ // As template candidates are not deduced immediately,
+ // persist the arry in the overload set.
+ ArrayRef<Expr *> ReversedArgs;
+ if (CandidateSet.getRewriteInfo().allowsReversed(Op) ||
+ CandidateSet.getRewriteInfo().allowsReversed(ExtraOp))
+ ReversedArgs = CandidateSet.getPersistentArgsArray(Args[1], Args[0]);
// Add operator candidates that are member functions.
AddMemberOperatorCandidates(Op, OpLoc, Args, CandidateSet);
if (CandidateSet.getRewriteInfo().allowsReversed(Op))
- AddMemberOperatorCandidates(Op, OpLoc, ReversedArgs(), CandidateSet,
+ AddMemberOperatorCandidates(Op, OpLoc, ReversedArgs, CandidateSet,
OverloadCandidateParamOrder::Reversed);
// In C++20, also add any rewritten member candidates.
if (ExtraOp) {
AddMemberOperatorCandidates(ExtraOp, OpLoc, Args, CandidateSet);
if (CandidateSet.getRewriteInfo().allowsReversed(ExtraOp))
- AddMemberOperatorCandidates(ExtraOp, OpLoc, ReversedArgs(), CandidateSet,
+ AddMemberOperatorCandidates(ExtraOp, OpLoc, ReversedArgs, CandidateSet,
OverloadCandidateParamOrder::Reversed);
}
diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp
index ba8e2dc372e98..083e743818121 100644
--- a/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp
+++ b/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp
@@ -14,7 +14,7 @@ template<typename T> struct S {
// expected-note@#FINST{{in instantiation of function template specialization}}
template<typename T> requires (S<T>{})
void f(T);
-void f(int);
+void f(long);
// Ensure this applies to operator && as well.
// expected-error at +3{{atomic constraint must be of type 'bool' (found 'S<int>')}}
@@ -22,7 +22,7 @@ void f(int);
// expected-note@#F2INST{{in instantiation of function template specialization}}
template<typename T> requires (S<T>{} && true)
void f2(T);
-void f2(int);
+void f2(long);
template<typename T> requires requires {
requires S<T>{};
@@ -36,12 +36,12 @@ template<typename T> requires requires {
//
}
void f3(T);
-void f3(int);
+void f3(long);
// Doesn't diagnose, since this is no longer a compound requirement.
template<typename T> requires (bool(1 && 2))
void f4(T);
-void f4(int);
+void f4(long);
void g() {
f(0); // #FINST
diff --git a/clang/test/SemaCXX/implicit-member-functions.cpp b/clang/test/SemaCXX/implicit-member-functions.cpp
index 1554b1af5d59a..8350eac5b88a0 100644
--- a/clang/test/SemaCXX/implicit-member-functions.cpp
+++ b/clang/test/SemaCXX/implicit-member-functions.cpp
@@ -54,31 +54,24 @@ namespace PR7594 {
namespace Recursion {
template<typename T> struct InvokeCopyConstructor {
static const T &get();
- typedef decltype(T(get())) type; // expected-error {{no matching conver}}
+ typedef decltype(T(get())) type;
};
struct B;
struct A {
- // expected-note at -1 {{while substituting deduced template arguments}}
typedef B type;
template<typename T,
typename = typename InvokeCopyConstructor<typename T::type>::type>
- // expected-note at -1 {{in instantiation of template class}}
A(const T &);
- // expected-note at -1 {{in instantiation of default argument}}
};
- struct B { // expected-note {{while declaring the implicit copy constructor for 'B'}}
- // expected-note at -1 {{candidate constructor (the implicit move }}
- B(); // expected-note {{candidate constructor not viable}}
+ struct B {
+ B();
A a;
};
// Triggering the declaration of B's copy constructor causes overload
- // resolution to occur for A's copying constructor, which instantiates
- // InvokeCopyConstructor<B>, which triggers the declaration of B's copy
- // constructor. Notionally, this happens when we get to the end of the
- // definition of 'struct B', so there is no declared copy constructor yet.
- //
- // This behavior is g++-compatible, but isn't exactly right; the class is
- // supposed to be incomplete when we implicitly declare its special members.
+ // resolution to occur for A's copying constructor, which picks
+ // the implicit copy constructor of A.
+ // Because that copy constructor is always a perfect match the template
+ // candidate is not instantiated.
B b = B();
diff --git a/clang/test/SemaTemplate/instantiate-function-params.cpp b/clang/test/SemaTemplate/instantiate-function-params.cpp
index 7dd5595de58a3..eb2a7c5d4e8d6 100644
--- a/clang/test/SemaTemplate/instantiate-function-params.cpp
+++ b/clang/test/SemaTemplate/instantiate-function-params.cpp
@@ -6,13 +6,12 @@ template<typename T1> struct if_ {
typedef if_c< static_cast<bool>(T1::value)> almost_type_; // expected-note 7{{in instantiation}}
};
template <class Model, void (Model::*)()> struct wrap_constraints { };
-template <class Model>
+template <class Model>
inline char has_constraints_(Model* , // expected-note 3{{candidate template ignored}}
- wrap_constraints<Model,&Model::constraints>* = 0); // expected-note 4{{in instantiation}}
-
+ wrap_constraints<Model,&Model::constraints>* = 0);
template <class Model> struct not_satisfied {
static const bool value = sizeof( has_constraints_((Model*)0) == 1); // expected-error 3{{no matching function}} \
- // expected-note 4{{while substituting deduced template arguments into function template 'has_constraints_' [with }}
+ // expected-note 4{{in instantiation}}
};
template <class ModelFn> struct requirement_;
template <void(*)()> struct instantiate {
diff --git a/clang/test/Templight/templight-empty-entries-fix.cpp b/clang/test/Templight/templight-empty-entries-fix.cpp
index d13b748068efe..9f4590cec2333 100644
--- a/clang/test/Templight/templight-empty-entries-fix.cpp
+++ b/clang/test/Templight/templight-empty-entries-fix.cpp
@@ -1,6 +1,6 @@
// RUN: %clang_cc1 -templight-dump -Wno-unused-value %s 2>&1 | FileCheck %s
-void a() {
+void a(long) {
[] {};
}
@@ -17,14 +17,14 @@ void a() {
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:4:3'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:4:3'$}}
-template <int = 0> void a() { a(); }
+template <int = 0> void a(long) { a(0); }
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+a$}}
// CHECK: {{^kind:[ ]+DeducedTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+Begin$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:20:25'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:20:31'$}}
+// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:20:35'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+unnamed template non-type parameter 0 of a$}}
// CHECK: {{^kind:[ ]+DefaultTemplateArgumentInstantiation$}}
@@ -42,29 +42,29 @@ template <int = 0> void a() { a(); }
// CHECK: {{^kind:[ ]+DeducedTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+End$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:20:25'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:20:31'$}}
+// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:20:35'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+'a<0>'$}}
// CHECK: {{^kind:[ ]+TemplateInstantiation$}}
// CHECK: {{^event:[ ]+Begin$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:20:25'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:20:31'$}}
+// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:20:35'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+'a<0>'$}}
// CHECK: {{^kind:[ ]+TemplateInstantiation$}}
// CHECK: {{^event:[ ]+End$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:20:25'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:20:31'$}}
+// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:20:35'$}}
template <int> struct b { typedef int c; };
-template <bool d = true, class = typename b<d>::c> void a() { a(); }
+template <bool d = true, class = typename b<d>::c> void a(long) { a(0); }
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+a$}}
// CHECK: {{^kind:[ ]+DeducedTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+Begin$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:60:57'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:60:63'$}}
+// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:60:67'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+d$}}
// CHECK: {{^kind:[ ]+DefaultTemplateArgumentInstantiation$}}
@@ -130,25 +130,25 @@ template <bool d = true, class = typename b<d>::c> void a() { a(); }
// CHECK: {{^kind:[ ]+DeducedTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+End$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:60:57'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:60:63'$}}
+// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:60:67'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+'a<true, int>'$}}
// CHECK: {{^kind:[ ]+TemplateInstantiation$}}
// CHECK: {{^event:[ ]+Begin$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:60:57'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:60:63'$}}
+// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:60:67'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+'a<true, int>'$}}
// CHECK: {{^kind:[ ]+TemplateInstantiation$}}
// CHECK: {{^event:[ ]+End$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:60:57'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:60:63'$}}
+// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:60:67'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+a$}}
// CHECK: {{^kind:[ ]+DeducedTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+Begin$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:20:25'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:60:63'$}}
+// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:60:67'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+unnamed template non-type parameter 0 of a$}}
// CHECK: {{^kind:[ ]+DefaultTemplateArgumentInstantiation$}}
@@ -166,7 +166,7 @@ template <bool d = true, class = typename b<d>::c> void a() { a(); }
// CHECK: {{^kind:[ ]+DeducedTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+End$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:20:25'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:60:63'$}}
+// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:60:67'$}}
template <bool = true> void d(int = 0) { d(); }
@@ -175,25 +175,25 @@ template <bool = true> void d(int = 0) { d(); }
// CHECK: {{^kind:[ ]+DeducedTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+Begin$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:60:57'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:60:63'$}}
+// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:60:67'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+a$}}
// CHECK: {{^kind:[ ]+DeducedTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+End$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:60:57'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:60:63'$}}
+// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:60:67'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+a$}}
// CHECK: {{^kind:[ ]+DeducedTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+Begin$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:20:25'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:60:63'$}}
+// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:60:67'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+a$}}
// CHECK: {{^kind:[ ]+DeducedTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+End$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:20:25'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:60:63'$}}
+// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:60:67'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+d$}}
// CHECK: {{^kind:[ ]+DeducedTemplateArgumentSubstitution$}}
>From a415862791ffb7915fa25febe8441a01240a864c Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Mon, 31 Mar 2025 17:06:02 +0200
Subject: [PATCH 07/32] Fix templight test
---
.../Templight/templight-empty-entries-fix.cpp | 100 +++++++-----------
1 file changed, 38 insertions(+), 62 deletions(-)
diff --git a/clang/test/Templight/templight-empty-entries-fix.cpp b/clang/test/Templight/templight-empty-entries-fix.cpp
index 9f4590cec2333..7f34b10134929 100644
--- a/clang/test/Templight/templight-empty-entries-fix.cpp
+++ b/clang/test/Templight/templight-empty-entries-fix.cpp
@@ -170,30 +170,6 @@ template <bool d = true, class = typename b<d>::c> void a(long) { a(0); }
template <bool = true> void d(int = 0) { d(); }
-// CHECK-LABEL: {{^---$}}
-// CHECK: {{^name:[ ]+a$}}
-// CHECK: {{^kind:[ ]+DeducedTemplateArgumentSubstitution$}}
-// CHECK: {{^event:[ ]+Begin$}}
-// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:60:57'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:60:67'$}}
-// CHECK-LABEL: {{^---$}}
-// CHECK: {{^name:[ ]+a$}}
-// CHECK: {{^kind:[ ]+DeducedTemplateArgumentSubstitution$}}
-// CHECK: {{^event:[ ]+End$}}
-// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:60:57'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:60:67'$}}
-// CHECK-LABEL: {{^---$}}
-// CHECK: {{^name:[ ]+a$}}
-// CHECK: {{^kind:[ ]+DeducedTemplateArgumentSubstitution$}}
-// CHECK: {{^event:[ ]+Begin$}}
-// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:20:25'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:60:67'$}}
-// CHECK-LABEL: {{^---$}}
-// CHECK: {{^name:[ ]+a$}}
-// CHECK: {{^kind:[ ]+DeducedTemplateArgumentSubstitution$}}
-// CHECK: {{^event:[ ]+End$}}
-// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:20:25'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:60:67'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+d$}}
// CHECK: {{^kind:[ ]+DeducedTemplateArgumentSubstitution$}}
@@ -249,41 +225,41 @@ void e() {
}
// CHECK-LABEL: {{^---$}}
-// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:247:3\)'$}}
+// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:223:3\)'$}}
// CHECK: {{^kind:[ ]+Memoization$}}
// CHECK: {{^event:[ ]+Begin$}}
-// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:247:3'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:248:5'$}}
+// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:223:3'$}}
+// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:224:5'$}}
// CHECK-LABEL: {{^---$}}
-// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:247:3\)'$}}
+// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:223:3\)'$}}
// CHECK: {{^kind:[ ]+Memoization$}}
// CHECK: {{^event:[ ]+End$}}
-// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:247:3'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:248:5'$}}
+// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:223:3'$}}
+// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:224:5'$}}
// CHECK-LABEL: {{^---$}}
-// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:247:3\)'$}}
+// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:223:3\)'$}}
// CHECK: {{^kind:[ ]+Memoization$}}
// CHECK: {{^event:[ ]+Begin$}}
-// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:247:3'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:248:5'$}}
+// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:223:3'$}}
+// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:224:5'$}}
// CHECK-LABEL: {{^---$}}
-// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:247:3\)'$}}
+// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:223:3\)'$}}
// CHECK: {{^kind:[ ]+Memoization$}}
// CHECK: {{^event:[ ]+End$}}
-// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:247:3'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:248:5'$}}
+// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:223:3'$}}
+// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:224:5'$}}
// CHECK-LABEL: {{^---$}}
-// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:247:3\)'$}}
+// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:223:3\)'$}}
// CHECK: {{^kind:[ ]+Memoization$}}
// CHECK: {{^event:[ ]+Begin$}}
-// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:247:3'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:247:3'$}}
+// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:223:3'$}}
+// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:223:3'$}}
// CHECK-LABEL: {{^---$}}
-// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:247:3\)'$}}
+// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:223:3\)'$}}
// CHECK: {{^kind:[ ]+Memoization$}}
// CHECK: {{^event:[ ]+End$}}
-// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:247:3'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:247:3'$}}
+// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:223:3'$}}
+// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:223:3'$}}
template <template<typename> class>
@@ -299,71 +275,71 @@ void foo() {
// CHECK: {{^name:[ ]+d$}}
// CHECK: {{^kind:[ ]+ExplicitTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+Begin$}}
-// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:290:6'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:295:3'$}}
+// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:266:6'$}}
+// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:271:3'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+unnamed template template parameter 0 of d$}}
// CHECK: {{^kind:[ ]+PriorTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+Begin$}}
-// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:289:35'$}}
+// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:265:35'$}}
// CHECK: {{^poi:[ ]+''$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+unnamed template template parameter 0 of d$}}
// CHECK: {{^kind:[ ]+PriorTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+End$}}
-// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:289:35'$}}
+// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:265:35'$}}
// CHECK: {{^poi:[ ]+''$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+unnamed template template parameter 0 of d$}}
// CHECK: {{^kind:[ ]+PartialOrderingTTP$}}
// CHECK: {{^event:[ ]+Begin$}}
-// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:289:35'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:295:5'$}}
+// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:265:35'$}}
+// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:271:5'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+unnamed template template parameter 0 of d$}}
// CHECK: {{^kind:[ ]+PartialOrderingTTP$}}
// CHECK: {{^event:[ ]+End$}}
-// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:289:35'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:295:5'$}}
+// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:265:35'$}}
+// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:271:5'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+d$}}
// CHECK: {{^kind:[ ]+ExplicitTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+End$}}
-// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:290:6'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:295:3'$}}
+// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:266:6'$}}
+// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:271:3'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+d$}}
// CHECK: {{^kind:[ ]+DeducedTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+Begin$}}
-// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:290:6'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:295:3'$}}
+// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:266:6'$}}
+// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:271:3'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+d$}}
// CHECK: {{^kind:[ ]+DeducedTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+End$}}
-// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:290:6'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:295:3'$}}
+// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:266:6'$}}
+// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:271:3'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+'d<C>'$}}
// CHECK: {{^kind:[ ]+TemplateInstantiation$}}
// CHECK: {{^event:[ ]+Begin$}}
-// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:290:6'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:295:3'$}}
+// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:266:6'$}}
+// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:271:3'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+'d<C>'$}}
// CHECK: {{^kind:[ ]+TemplateInstantiation$}}
// CHECK: {{^event:[ ]+End$}}
-// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:290:6'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:295:3'$}}
+// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:266:6'$}}
+// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:271:3'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+d$}}
// CHECK: {{^kind:[ ]+ExplicitTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+Begin$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:171:29'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:295:3'$}}
+// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:271:3'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+d$}}
// CHECK: {{^kind:[ ]+ExplicitTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+End$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:171:29'$}}
-// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:295:3'$}}
+// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:271:3'$}}
>From e75040e26821bfd9d6a319cd76c45297f2827969 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Mon, 31 Mar 2025 17:09:41 +0200
Subject: [PATCH 08/32] format
---
clang/lib/Sema/SemaOverload.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 981b65237acb8..968efcbdad114 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -11145,9 +11145,9 @@ void OverloadCandidateSet::CudaExcludeWrongSideCandidates(Sema &S) {
/// function, \p Best points to the candidate function found.
///
/// \returns The result of overload resolution.
-OverloadingResult
-OverloadCandidateSet::BestViableFunction(Sema &S, SourceLocation Loc,
- iterator &Best) {
+OverloadingResult OverloadCandidateSet::BestViableFunction(Sema &S,
+ SourceLocation Loc,
+ iterator &Best) {
assert(shouldDeferTemplateArgumentDeduction(S.getLangOpts()) ||
DeferredCandidates.empty() &&
>From 3257b7ffc72f87840e491ac5fa26857454c58702 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Mon, 31 Mar 2025 17:30:10 +0200
Subject: [PATCH 09/32] do not skip templates when initializing by constructors
as that might include a conversion
---
clang/include/clang/Sema/Overload.h | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index 63339a917c3a4..547845c01db11 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -1448,7 +1448,9 @@ class Sema;
// For user defined conversion we need to check against different
// combination of CV qualifiers and look at any expicit specifier, so
// always deduce template candidate.
- Kind != CSK_InitByUserDefinedConversion && Kind != CSK_CodeCompletion &&
+ Kind != CSK_InitByUserDefinedConversion
+ && Kind != CSK_InitByConstructor
+ && Kind != CSK_CodeCompletion &&
Opts.CPlusPlus && (!Opts.CUDA || Opts.GPUExcludeWrongSideOverloads);
}
>From 2a1b90b8dd912d89914cf926434917e58dd653e4 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Mon, 31 Mar 2025 17:53:23 +0200
Subject: [PATCH 10/32] move and document the special handling of implicit
object member function in non-member calls
---
clang/include/clang/Sema/Overload.h | 5 ++---
clang/lib/Sema/SemaOverload.cpp | 26 +++++++++++++++++---------
2 files changed, 19 insertions(+), 12 deletions(-)
diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index 547845c01db11..88b0e49b99ca7 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -1448,9 +1448,8 @@ class Sema;
// For user defined conversion we need to check against different
// combination of CV qualifiers and look at any expicit specifier, so
// always deduce template candidate.
- Kind != CSK_InitByUserDefinedConversion
- && Kind != CSK_InitByConstructor
- && Kind != CSK_CodeCompletion &&
+ Kind != CSK_InitByUserDefinedConversion &&
+ Kind != CSK_InitByConstructor && Kind != CSK_CodeCompletion &&
Opts.CPlusPlus && (!Opts.CUDA || Opts.GPUExcludeWrongSideOverloads);
}
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 968efcbdad114..abdcf669fc39e 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -11088,11 +11088,6 @@ OverloadCandidateSet::ResultForBestCandidate(const iterator &Best) {
Best->Best = true;
if (Best->Function && Best->Function->isDeleted())
return OR_Deleted;
- if (auto *M = dyn_cast_or_null<CXXMethodDecl>(Best->Function);
- Kind == CSK_AddressOfOverloadSet && M &&
- M->isImplicitObjectMemberFunction()) {
- return OR_No_Viable_Function;
- }
return OR_Success;
}
@@ -14591,10 +14586,12 @@ ExprResult Sema::BuildOverloadedCallExpr(Scope *S, Expr *Fn,
Expr *ExecConfig,
bool AllowTypoCorrection,
bool CalleesAddressIsTaken) {
- OverloadCandidateSet CandidateSet(
- Fn->getExprLoc(), CalleesAddressIsTaken
- ? OverloadCandidateSet::CSK_AddressOfOverloadSet
- : OverloadCandidateSet::CSK_Normal);
+
+ OverloadCandidateSet::CandidateSetKind CSK =
+ CalleesAddressIsTaken ? OverloadCandidateSet::CSK_AddressOfOverloadSet
+ : OverloadCandidateSet::CSK_Normal;
+
+ OverloadCandidateSet CandidateSet(Fn->getExprLoc(), CSK);
ExprResult result;
if (buildOverloadedCallSet(S, Fn, ULE, Args, LParenLoc, &CandidateSet,
@@ -14610,6 +14607,17 @@ ExprResult Sema::BuildOverloadedCallExpr(Scope *S, Expr *Fn,
OverloadingResult OverloadResult =
CandidateSet.BestViableFunction(*this, Fn->getBeginLoc(), Best);
+ // [C++23][over.call.func]
+ // if overload resolution selects a non-static member function,
+ // the call is ill-formed;
+ if (CSK == OverloadCandidateSet::CSK_AddressOfOverloadSet &&
+ Best != CandidateSet.end()) {
+ if (auto *M = dyn_cast_or_null<CXXMethodDecl>(Best->Function);
+ M && M->isImplicitObjectMemberFunction()) {
+ OverloadResult = OR_No_Viable_Function;
+ }
+ }
+
// Model the case with a call to a templated function whose definition
// encloses the call and whose return type contains a placeholder type as if
// the UnresolvedLookupExpr was type-dependent.
>From 9165cd46681e1181c1e8a68607ee8e64296b8197 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Tue, 1 Apr 2025 00:25:01 +0200
Subject: [PATCH 11/32] Fix typos and cuda tests
---
clang/include/clang/Sema/Overload.h | 16 +++++++++---
clang/lib/Sema/SemaInit.cpp | 2 +-
clang/lib/Sema/SemaOverload.cpp | 38 +++++++++++++----------------
3 files changed, 30 insertions(+), 26 deletions(-)
diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index 88b0e49b99ca7..d2f58864c0399 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -1388,7 +1388,8 @@ class Sema;
private:
OverloadingResult ResultForBestCandidate(const iterator &Best);
- void CudaExcludeWrongSideCandidates(Sema &S);
+ void CudaExcludeWrongSideCandidates(
+ Sema &S, SmallVectorImpl<OverloadCandidate *> &Candidates);
OverloadingResult
BestViableFunctionImpl(Sema &S, SourceLocation Loc,
OverloadCandidateSet::iterator &Best);
@@ -1447,10 +1448,17 @@ class Sema;
return
// For user defined conversion we need to check against different
// combination of CV qualifiers and look at any expicit specifier, so
- // always deduce template candidate.
+ // always deduce template candidates.
Kind != CSK_InitByUserDefinedConversion &&
- Kind != CSK_InitByConstructor && Kind != CSK_CodeCompletion &&
- Opts.CPlusPlus && (!Opts.CUDA || Opts.GPUExcludeWrongSideOverloads);
+ Kind != CSK_InitByConstructor
+ // When doing code completion, we want to see all the
+ // viable candidates.
+ && Kind != CSK_CodeCompletion &&
+ Opts.CPlusPlus
+ // When -fgpu-exclude-wrong-side-overloads, CUDA needs
+ // to exclude templates from the overload sets after they
+ // have been instantiated. See CudaExcludeWrongSideCandidates.
+ && (!Opts.CUDA || !Opts.GPUExcludeWrongSideOverloads);
}
} // namespace clang
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 460bf64543821..9cb8f99c61e00 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -10031,7 +10031,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
if (TD) {
// As template candidates are not deduced immediately,
- // persist the arry in the overload set.
+ // persist the array in the overload set.
MutableArrayRef<Expr *> TmpInits =
Candidates.getPersistentArgsArray(Inits.size());
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index abdcf669fc39e..06f8388f5eb16 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -8535,7 +8535,7 @@ void Sema::AddNonMemberOperatorCandidates(
if (CandidateSet.getRewriteInfo().shouldAddReversed(*this, Args, FD)) {
// As template candidates are not deduced immediately,
- // persist the arry in the overload set.
+ // persist the array in the overload set.
ArrayRef<Expr *> Reversed = CandidateSet.getPersistentArgsArray(
FunctionArgs[1], FunctionArgs[0]);
AddTemplateOverloadCandidate(FunTmpl, F.getPair(), ExplicitTemplateArgs,
@@ -10317,7 +10317,7 @@ Sema::AddArgumentDependentLookupCandidates(DeclarationName Name,
*this, Args, FTD->getTemplatedDecl())) {
// As template candidates are not deduced immediately,
- // persist the arry in the overload set.
+ // persist the array in the overload set.
if (ReversedArgs.empty())
ReversedArgs = CandidateSet.getPersistentArgsArray(Args[1], Args[0]);
@@ -11091,7 +11091,8 @@ OverloadCandidateSet::ResultForBestCandidate(const iterator &Best) {
return OR_Success;
}
-void OverloadCandidateSet::CudaExcludeWrongSideCandidates(Sema &S) {
+void OverloadCandidateSet::CudaExcludeWrongSideCandidates(
+ Sema &S, SmallVectorImpl<OverloadCandidate *> &Candidates) {
// [CUDA] HD->H or HD->D calls are technically not allowed by CUDA but
// are accepted by both clang and NVCC. However, during a particular
// compilation mode only one call variant is viable. We need to
@@ -11108,26 +11109,23 @@ void OverloadCandidateSet::CudaExcludeWrongSideCandidates(Sema &S) {
const FunctionDecl *Caller = S.getCurFunctionDecl(/*AllowLambda=*/true);
bool ContainsSameSideCandidate =
- llvm::any_of(Candidates, [&](const OverloadCandidate &Cand) {
+ llvm::any_of(Candidates, [&](const OverloadCandidate *Cand) {
// Check viable function only.
- return Cand.Viable && Cand.Function &&
- S.CUDA().IdentifyPreference(Caller, Cand.Function) ==
+ return Cand->Viable && Cand->Function &&
+ S.CUDA().IdentifyPreference(Caller, Cand->Function) ==
SemaCUDA::CFP_SameSide;
});
+
if (!ContainsSameSideCandidate)
return;
- auto IsWrongSideCandidate = [&](const OverloadCandidate &Cand) {
+ auto IsWrongSideCandidate = [&](const OverloadCandidate *Cand) {
// Check viable function only to avoid unnecessary data copying/moving.
- return Cand.Viable && Cand.Function &&
- S.CUDA().IdentifyPreference(Caller, Cand.Function) ==
+ return Cand->Viable && Cand->Function &&
+ S.CUDA().IdentifyPreference(Caller, Cand->Function) ==
SemaCUDA::CFP_WrongSide;
};
-
- for (auto &Cand : Candidates) {
- if (IsWrongSideCandidate(Cand))
- Cand.Viable = false;
- }
+ llvm::erase_if(Candidates, IsWrongSideCandidate);
}
/// Computes the best viable function (C++ 13.3.3)
@@ -11148,9 +11146,6 @@ OverloadingResult OverloadCandidateSet::BestViableFunction(Sema &S,
DeferredCandidates.empty() &&
"Unexpected deferred template candidate");
- if (S.getLangOpts().CUDA)
- CudaExcludeWrongSideCandidates(S);
-
bool TwoPhaseResolution = !DeferredCandidates.empty();
if (TwoPhaseResolution) {
@@ -11160,9 +11155,6 @@ OverloadingResult OverloadCandidateSet::BestViableFunction(Sema &S,
return ResultForBestCandidate(Best);
InjectNonDeducedTemplateCandidates(S);
-
- if (S.getLangOpts().CUDA)
- CudaExcludeWrongSideCandidates(S);
}
return BestViableFunctionImpl(S, Loc, Best);
@@ -11170,6 +11162,7 @@ OverloadingResult OverloadCandidateSet::BestViableFunction(Sema &S,
void OverloadCandidateSet::PerfectViableFunction(
Sema &S, SourceLocation Loc, OverloadCandidateSet::iterator &Best) {
+
Best = end();
for (auto It = begin(); It != end(); ++It) {
if (It->isPerfectMatch(S.getASTContext())) {
@@ -11202,6 +11195,9 @@ OverloadingResult OverloadCandidateSet::BestViableFunctionImpl(
std::transform(begin(), end(), std::back_inserter(Candidates),
[](OverloadCandidate &Cand) { return &Cand; });
+ if (S.getLangOpts().CUDA)
+ CudaExcludeWrongSideCandidates(S, Candidates);
+
Best = end();
for (auto *Cand : Candidates) {
Cand->Best = false;
@@ -14954,7 +14950,7 @@ void Sema::LookupOverloadedBinOp(OverloadCandidateSet &CandidateSet,
AddNonMemberOperatorCandidates(Fns, Args, CandidateSet);
// As template candidates are not deduced immediately,
- // persist the arry in the overload set.
+ // persist the array in the overload set.
ArrayRef<Expr *> ReversedArgs;
if (CandidateSet.getRewriteInfo().allowsReversed(Op) ||
CandidateSet.getRewriteInfo().allowsReversed(ExtraOp))
>From 89fc496a906781b653b3f52651e35f1a041e5e13 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Tue, 1 Apr 2025 00:28:05 +0200
Subject: [PATCH 12/32] remove redundant check
---
clang/include/clang/Sema/Overload.h | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index d2f58864c0399..43114b97b445b 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -1453,8 +1453,7 @@ class Sema;
Kind != CSK_InitByConstructor
// When doing code completion, we want to see all the
// viable candidates.
- && Kind != CSK_CodeCompletion &&
- Opts.CPlusPlus
+ && Kind != CSK_CodeCompletion
// When -fgpu-exclude-wrong-side-overloads, CUDA needs
// to exclude templates from the overload sets after they
// have been instantiated. See CudaExcludeWrongSideCandidates.
>From e3b231bd247e5b6907f0c336b3d709747e1fe50b Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Tue, 1 Apr 2025 10:05:06 +0200
Subject: [PATCH 13/32] More code simplification
---
clang/lib/Sema/SemaOverload.cpp | 30 +++++++++++++++---------------
1 file changed, 15 insertions(+), 15 deletions(-)
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 06f8388f5eb16..81addcf9eb413 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -11165,25 +11165,25 @@ void OverloadCandidateSet::PerfectViableFunction(
Best = end();
for (auto It = begin(); It != end(); ++It) {
- if (It->isPerfectMatch(S.getASTContext())) {
- if (Best == end()) {
- Best = It;
- } else {
- if (Best->Function && It->Function) {
- FunctionDecl *D =
- S.getMoreConstrainedFunction(Best->Function, It->Function);
- if (D == nullptr) {
- Best = end();
- break;
- }
- if (D == It->Function)
- Best = It;
- continue;
- }
+ if (!It->isPerfectMatch(S.getASTContext()))
+ continue;
+ if (Best == end()) {
+ Best = It;
+ continue;
+ }
+ if (Best->Function && It->Function) {
+ FunctionDecl *D =
+ S.getMoreConstrainedFunction(Best->Function, It->Function);
+ if (D == nullptr) {
Best = end();
break;
}
+ if (D == It->Function)
+ Best = It;
+ continue;
}
+ Best = end();
+ break;
}
}
>From 5c60ecf7d5650290e42505c6cf72c541141978fa Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Tue, 1 Apr 2025 11:00:05 +0200
Subject: [PATCH 14/32] fix cuda, add comments
---
clang/include/clang/Sema/Overload.h | 17 ++++++++++++-----
clang/lib/Sema/SemaOverload.cpp | 1 -
clang/test/SemaCUDA/function-overload.cu | 3 ---
3 files changed, 12 insertions(+), 9 deletions(-)
diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index 43114b97b445b..de1f27844530a 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -1199,7 +1199,8 @@ class Sema;
llvm::SmallPtrSet<uintptr_t, 16> Functions;
SmallVector<DeferredTemplateOverloadCandidate, 8> DeferredCandidates;
- // Allocator for ConversionSequenceLists. We store the first few of these
+ // Allocator for ConversionSequenceLists and deferred candidate args.
+ // We store the first few of these
// inline to avoid allocation for small sets.
llvm::BumpPtrAllocator SlabAllocator;
@@ -1207,6 +1208,8 @@ class Sema;
CandidateSetKind Kind;
OperatorRewriteInfo RewriteInfo;
+ /// Small storage size for ImplicitConversionSequences
+ /// and the persisted arguments of deferred candidates.
constexpr static unsigned NumInlineBytes =
32 * sizeof(ImplicitConversionSequence);
@@ -1304,6 +1307,11 @@ class Sema;
return ConversionSequenceList(Conversions, NumConversions);
}
+ /// Provide storage for any Expr* arg that must be preserved
+ /// until deferred template candidates are deduced.
+ /// Typically this should be used for reversed operator arguments
+ /// and any time the argument array is transformed while adding
+ /// a template candidate.
llvm::MutableArrayRef<Expr *> getPersistentArgsArray(unsigned N) {
Expr **Exprs = slabAllocate<Expr *>(N);
return llvm::MutableArrayRef<Expr *>(Exprs, N);
@@ -1454,10 +1462,9 @@ class Sema;
// When doing code completion, we want to see all the
// viable candidates.
&& Kind != CSK_CodeCompletion
- // When -fgpu-exclude-wrong-side-overloads, CUDA needs
- // to exclude templates from the overload sets after they
- // have been instantiated. See CudaExcludeWrongSideCandidates.
- && (!Opts.CUDA || !Opts.GPUExcludeWrongSideOverloads);
+ // CUDA may prefer template candidates even when a non-candidate
+ // is a perfect match
+ && !Opts.CUDA;
}
} // namespace clang
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 81addcf9eb413..a01cb4d02ba94 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -31,7 +31,6 @@
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Overload.h"
#include "clang/Sema/SemaCUDA.h"
-#include "clang/Sema/SemaCodeCompletion.h"
#include "clang/Sema/SemaObjC.h"
#include "clang/Sema/Template.h"
#include "clang/Sema/TemplateDeduction.h"
diff --git a/clang/test/SemaCUDA/function-overload.cu b/clang/test/SemaCUDA/function-overload.cu
index 4710c81763adf..3d05839af7528 100644
--- a/clang/test/SemaCUDA/function-overload.cu
+++ b/clang/test/SemaCUDA/function-overload.cu
@@ -1,6 +1,3 @@
-// REQUIRES: x86-registered-target
-// REQUIRES: nvptx-registered-target
-
// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-linux-gnu -fsyntax-only \
// RUN: -verify=host,hostdefer,devdefer,expected %s
// RUN: %clang_cc1 -std=c++14 -triple nvptx64-nvidia-cuda -fsyntax-only \
>From 1253aae36b5529430e4f30e13fd95562721461fc Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Tue, 1 Apr 2025 15:15:14 +0200
Subject: [PATCH 15/32] add tests
---
...overload-resolution-deferred-templates.cpp | 145 ++++++++++++++++++
1 file changed, 145 insertions(+)
create mode 100644 clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
diff --git a/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp b/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
new file mode 100644
index 0000000000000..d0a7a57adcf13
--- /dev/null
+++ b/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
@@ -0,0 +1,145 @@
+// RUN: %clang_cc1 -triple=x86_64-unknown-unknown -fsyntax-only -verify -std=c++11 %s
+// RUN: %clang_cc1 -triple=x86_64-unknown-unknown -fsyntax-only -verify -std=c++20 %s
+// RUN: %clang_cc1 -triple=x86_64-unknown-unknown -fsyntax-only -verify -std=c++2c %s
+
+template <typename T>
+struct Invalid { static_assert(false, "instantiated Invalid"); }; // #err-invalid
+
+template <typename T>
+int f(T a, Invalid<T> = {}); // #note-f
+
+// sanity check
+int e1 = f(0);
+//expected-error@#err-invalid {{static assertion failed: instantiated Invalid}}
+//expected-note at -2 {{in instantiation of default function argument expression for 'f<int>' required here}}
+//expected-note@#note-f {{in instantiation of template class 'Invalid<int>' requested here}}
+//expected-note@#note-f {{passing argument to parameter here}}
+
+int f(int);
+int ok1 = f(0);
+int e4 = f((const int&)(ok1));
+
+int f(int, int = 0);
+int ok2 = f(0, 0);
+
+int e2 = f(0L);
+//expected-error@#err-invalid {{static assertion failed: instantiated Invalid}}
+//expected-note at -2 {{in instantiation of default function argument expression for 'f<long>' required here}}
+//expected-note@#note-f {{in instantiation of template class 'Invalid<long>' requested here}}
+//expected-note@#note-f {{passing argument to parameter here}}
+
+int f(long);
+int ok3 = f(0L);
+
+template <typename T>
+struct Invalid2 { static_assert(false, "instantiated Invalid2"); }; // #err-qualifiers
+
+template <typename T>
+int ref(T a, Invalid2<T> = {}); // expected-note 2{{here}}
+int ref(int&);
+int ref1 = ref(ok3);
+int ref2 = ref((const int&)ok3); // expected-note {{here}}
+//expected-error@#err-qualifiers {{static assertion failed: instantiated Invalid2}}
+
+
+template <typename T>
+int f_alias(T a, Invalid<T> = {});
+using Alias = int;
+int f_alias(Alias);
+int ok4 = f_alias(0);
+
+#if __cplusplus >= 202002
+
+struct Copyable {
+ template <typename T>
+ requires __is_constructible(Copyable, T)
+ explicit Copyable(T op) noexcept; // #1
+ Copyable(const Copyable&) noexcept = default; // #2
+};
+static_assert(__is_constructible(Copyable, const Copyable&));
+
+struct ImplicitlyCopyable {
+ template <typename T>
+ requires __is_constructible(ImplicitlyCopyable, T)
+ explicit ImplicitlyCopyable(T op) = delete; // #1
+};
+static_assert(__is_constructible(ImplicitlyCopyable, const ImplicitlyCopyable&));
+
+
+struct Movable {
+ template <typename T>
+ requires __is_constructible(Movable, T) // #err-self-constraint-1
+ explicit Movable(T op) noexcept; // #1
+ Movable(Movable&&) noexcept = default; // #2
+};
+static_assert(__is_constructible(Movable, Movable&&));
+static_assert(__is_constructible(Movable, const Movable&));
+// expected-error at -1 {{static assertion failed due to requirement '__is_constructible(Movable, const Movable &)'}}
+
+static_assert(__is_constructible(Movable, int));
+// expected-error at -1{{static assertion failed due to requirement '__is_constructible(Movable, int)'}} \
+// expected-note at -1 2{{}}
+// expected-error@#err-self-constraint-1{{satisfaction of constraint '__is_constructible(Movable, T)' depends on itself}}
+// expected-note@#err-self-constraint-1 4{{}}
+
+template <typename T>
+struct Members {
+ constexpr auto f(auto) {
+ static_assert(false, "");
+ }
+ constexpr auto f(int) { return 1; }
+ constexpr auto f(int) requires true { return 2; }
+
+ constexpr auto g(auto) {
+ static_assert(false, "instantiated member"); //#err-qualified-member
+ return 0;
+ }
+ constexpr auto g(int) & { return 1; }
+
+ static constexpr auto s(auto) {
+ static_assert(false, "");
+ }
+ static constexpr auto s(int) {
+ return 1;
+ }
+ static constexpr auto s(int) requires true {
+ return 2;
+ }
+};
+
+static_assert(Members<int[1]>{}.f(0) == 2);
+static_assert(Members<int[2]>{}.g(0) == 0);
+// expected-error@#err-qualified-member {{static assertion failed: instantiated member}} \
+// expected-note at -1{{in instantiation of function template specialization 'Members<int[2]>::g<int>' }}
+Members<int[3]> m1;
+static_assert(m1.g(0) == 1);
+static_assert(Members<int[3]>{}.s(0) == 2);
+
+
+
+namespace GH62096 {
+template <typename T>
+struct Oops {
+ static_assert(sizeof(T) == 0); // #GH62096-err
+ static constexpr bool value = true;
+};
+
+template <class OPERATOR>
+concept Operator = Oops<OPERATOR>::value; // #GH62096-note1
+
+template <Operator OP> void f(OP op); // // #GH62096-note2
+void f(int);
+
+void g(int n) { f(n); } // OK
+void h(short n) { f(n); }
+// expected-error@#GH62096-err {{static assertion failed due to requirement 'sizeof(short) == 0'}} \
+// expected-note at -1{{in instantiation of function template specialization}} \
+// expected-note at -1{{while checking constraint satisfaction for template}}
+// expected-note@#GH62096-note1{{in instantiation}}
+// expected-note@#GH62096-note1{{while substituting template arguments into constraint expression here}}
+// expected-note@#GH62096-note2{{while substituting template arguments into constraint expression here}}
+// expected-note@#GH62096-note2{{while checking the satisfaction of concept}}
+// expected-note@#GH62096-err {{expression evaluates}}
+}
+
+#endif
>From 33336e0d24b3fcc7868f2229527d2eb9e8fa5fca Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Tue, 1 Apr 2025 19:30:30 +0200
Subject: [PATCH 16/32] * Fix handling of explicit specifiers * Prefer a
constructor template over a non-template conversion function
---
clang/include/clang/Sema/Overload.h | 13 ++++++----
clang/lib/Sema/SemaOverload.cpp | 26 +++++++++++++++++--
clang/lib/Sema/SemaTemplateDeduction.cpp | 4 +--
...overload-resolution-deferred-templates.cpp | 12 +++++++++
4 files changed, 46 insertions(+), 9 deletions(-)
diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index de1f27844530a..98fcd07111537 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -1199,6 +1199,9 @@ class Sema;
llvm::SmallPtrSet<uintptr_t, 16> Functions;
SmallVector<DeferredTemplateOverloadCandidate, 8> DeferredCandidates;
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned HasDeferredTemplateConstructors : 1;
+
// Allocator for ConversionSequenceLists and deferred candidate args.
// We store the first few of these
// inline to avoid allocation for small sets.
@@ -1248,7 +1251,8 @@ class Sema;
public:
OverloadCandidateSet(SourceLocation Loc, CandidateSetKind CSK,
OperatorRewriteInfo RewriteInfo = {})
- : Loc(Loc), Kind(CSK), RewriteInfo(RewriteInfo) {}
+ : HasDeferredTemplateConstructors(false), Loc(Loc), Kind(CSK),
+ RewriteInfo(RewriteInfo) {}
OverloadCandidateSet(const OverloadCandidateSet &) = delete;
OverloadCandidateSet &operator=(const OverloadCandidateSet &) = delete;
~OverloadCandidateSet() { destroyCandidates(); }
@@ -1260,7 +1264,7 @@ class Sema;
/// Whether diagnostics should be deferred.
bool shouldDeferDiags(Sema &S, ArrayRef<Expr *> Args, SourceLocation OpLoc);
- // Whether the resolution of template candidates should be defered
+ // Whether the resolution of template candidates should be deferred
bool shouldDeferTemplateArgumentDeduction(const LangOptions &Opts) const;
/// Determine when this overload candidate will be new to the
@@ -1455,10 +1459,9 @@ class Sema;
const LangOptions &Opts) const {
return
// For user defined conversion we need to check against different
- // combination of CV qualifiers and look at any expicit specifier, so
+ // combination of CV qualifiers and look at any explicit specifier, so
// always deduce template candidates.
- Kind != CSK_InitByUserDefinedConversion &&
- Kind != CSK_InitByConstructor
+ Kind != CSK_InitByUserDefinedConversion
// When doing code completion, we want to see all the
// viable candidates.
&& Kind != CSK_CodeCompletion
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index a01cb4d02ba94..fc1965ba93e32 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -1124,6 +1124,7 @@ void OverloadCandidateSet::clear(CandidateSetKind CSK) {
Candidates.clear();
Functions.clear();
Kind = CSK;
+ HasDeferredTemplateConstructors = false;
}
namespace {
@@ -7891,6 +7892,11 @@ static bool isNonDependentlyExplicit(FunctionTemplateDecl *FTD) {
return ExplicitSpecifier::getFromDecl(FTD->getTemplatedDecl()).isExplicit();
}
+static bool hasDependentExplicit(FunctionTemplateDecl *FTD) {
+ return ExplicitSpecifier::getFromDecl(FTD->getTemplatedDecl()).getKind() ==
+ ExplicitSpecKind::Unresolved;
+}
+
static void AddTemplateOverloadCandidateImmediately(
Sema &S, OverloadCandidateSet &CandidateSet,
FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
@@ -7982,7 +7988,10 @@ void Sema::AddTemplateOverloadCandidate(
return;
if (ExplicitTemplateArgs ||
- !CandidateSet.shouldDeferTemplateArgumentDeduction(getLangOpts())) {
+ !CandidateSet.shouldDeferTemplateArgumentDeduction(getLangOpts()) ||
+ (isa<CXXConstructorDecl>(FunctionTemplate->getTemplatedDecl()) &&
+ hasDependentExplicit(FunctionTemplate))) {
+
AddTemplateOverloadCandidateImmediately(
*this, CandidateSet, FunctionTemplate, FoundDecl, ExplicitTemplateArgs,
Args, SuppressUserConversions, PartialOverloading, AllowExplicit,
@@ -8370,7 +8379,9 @@ void Sema::AddTemplateConversionCandidate(
if (!CandidateSet.isNewCandidate(FunctionTemplate))
return;
- if (!CandidateSet.shouldDeferTemplateArgumentDeduction(getLangOpts())) {
+ if (!CandidateSet.shouldDeferTemplateArgumentDeduction(getLangOpts()) ||
+ hasDependentExplicit(FunctionTemplate)) {
+
AddTemplateConversionCandidateImmediately(
*this, CandidateSet, FunctionTemplate, FoundDecl, ActingDC, From,
ToType, AllowObjCConversionOnExplicit, AllowExplicit,
@@ -11011,6 +11022,8 @@ void OverloadCandidateSet::AddDeferredTemplateCandidate(
AllowExplicit,
AggregateCandidateDeduction};
DeferredCandidates.emplace_back(std::move(C));
+ HasDeferredTemplateConstructors |=
+ isa<CXXConstructorDecl>(FunctionTemplate->getTemplatedDecl());
}
void OverloadCandidateSet::AddDeferredMethodTemplateCandidate(
@@ -11166,6 +11179,15 @@ void OverloadCandidateSet::PerfectViableFunction(
for (auto It = begin(); It != end(); ++It) {
if (!It->isPerfectMatch(S.getASTContext()))
continue;
+ // We found a suitable conversion function
+ // but if there is a template constructor in the target class
+ // we might prefer that instead.
+ if (HasDeferredTemplateConstructors &&
+ isa_and_nonnull<CXXConversionDecl>(It->Function)) {
+ Best = end();
+ break;
+ }
+
if (Best == end()) {
Best = It;
continue;
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 772962ac653f7..0ecdbb3ffb89f 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -6142,9 +6142,9 @@ FunctionDecl *Sema::getMoreConstrainedFunction(FunctionDecl *FD1,
assert(!FD1->getDescribedTemplate() && !FD2->getDescribedTemplate() &&
"not for function templates");
assert(!FD1->isFunctionTemplateSpecialization() ||
- isa<CXXConversionDecl>(FD1));
+ (isa<CXXConversionDecl, CXXConstructorDecl>(FD1)));
assert(!FD2->isFunctionTemplateSpecialization() ||
- isa<CXXConversionDecl>(FD2));
+ (isa<CXXConversionDecl, CXXConstructorDecl>(FD2)));
FunctionDecl *F1 = FD1;
if (FunctionDecl *P = FD1->getTemplateInstantiationPattern(false))
diff --git a/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp b/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
index d0a7a57adcf13..e6fe155cc4d15 100644
--- a/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
+++ b/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
@@ -116,6 +116,18 @@ static_assert(m1.g(0) == 1);
static_assert(Members<int[3]>{}.s(0) == 2);
+namespace ConstructorInit{
+struct S {
+ template <typename T>
+ S(T&&) {}
+};
+struct Test {
+ operator S() = delete;
+};
+
+static_assert(__is_constructible(S, Test));
+}
+
namespace GH62096 {
template <typename T>
>From a34b533c5d2a9e65ec2cf9ea9b36fd1fdc666330 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Thu, 3 Apr 2025 09:55:20 +0200
Subject: [PATCH 17/32] slab allocate template candidates
---
clang/include/clang/Sema/Overload.h | 94 +++++++++++++----------
clang/lib/Sema/SemaOverload.cpp | 111 +++++++++++++++++++---------
2 files changed, 130 insertions(+), 75 deletions(-)
diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index 98fcd07111537..631d105280c56 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -38,7 +38,6 @@
#include <cstddef>
#include <cstdint>
#include <utility>
-#include <variant>
namespace clang {
@@ -1039,60 +1038,61 @@ class Sema;
RewriteKind(CRK_None) {}
};
- struct DeferredConversionTemplateOverloadCandidate {
- FunctionTemplateDecl *FunctionTemplate;
- DeclAccessPair FoundDecl;
- CXXRecordDecl *ActingContext;
- Expr *From;
- QualType ToType;
+ struct DeferredTemplateOverloadCandidate {
+ DeferredTemplateOverloadCandidate *Next = nullptr;
+ enum Kind { Function, Method, Conversion };
+ LLVM_PREFERRED_TYPE(Kind)
+ unsigned Kind : 2;
LLVM_PREFERRED_TYPE(bool)
unsigned AllowObjCConversionOnExplicit : 1;
LLVM_PREFERRED_TYPE(bool)
+ unsigned AllowResultConversion : 1;
+ LLVM_PREFERRED_TYPE(bool)
unsigned AllowExplicit : 1;
LLVM_PREFERRED_TYPE(bool)
- unsigned AllowResultConversion : 1;
+ unsigned SuppressUserConversions : 1;
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned PartialOverloading : 1;
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned AggregateCandidateDeduction : 1;
};
- struct DeferredMethodTemplateOverloadCandidate {
+ struct DeferredFunctionTemplateOverloadCandidate
+ : public DeferredTemplateOverloadCandidate {
FunctionTemplateDecl *FunctionTemplate;
DeclAccessPair FoundDecl;
ArrayRef<Expr *> Args;
- CXXRecordDecl *ActingContext;
- Expr::Classification ObjectClassification;
- QualType ObjectType;
-
+ CallExpr::ADLCallKind IsADLCandidate;
OverloadCandidateParamOrder PO;
- LLVM_PREFERRED_TYPE(bool)
- unsigned SuppressUserConversions : 1;
- LLVM_PREFERRED_TYPE(bool)
- unsigned PartialOverloading : 1;
};
+ static_assert(std::is_trivially_destructible_v<
+ DeferredFunctionTemplateOverloadCandidate>);
- struct DeferredFunctionTemplateOverloadCandidate {
+ struct DeferredMethodTemplateOverloadCandidate
+ : public DeferredTemplateOverloadCandidate {
FunctionTemplateDecl *FunctionTemplate;
DeclAccessPair FoundDecl;
ArrayRef<Expr *> Args;
-
- CallExpr::ADLCallKind IsADLCandidate;
+ CXXRecordDecl *ActingContext;
+ Expr::Classification ObjectClassification;
+ QualType ObjectType;
OverloadCandidateParamOrder PO;
- LLVM_PREFERRED_TYPE(bool)
- unsigned SuppressUserConversions : 1;
- LLVM_PREFERRED_TYPE(bool)
- unsigned PartialOverloading : 1;
- LLVM_PREFERRED_TYPE(bool)
- unsigned AllowExplicit : 1;
- LLVM_PREFERRED_TYPE(bool)
- unsigned AggregateCandidateDeduction : 1;
};
+ static_assert(std::is_trivially_destructible_v<
+ DeferredMethodTemplateOverloadCandidate>);
- using DeferredTemplateOverloadCandidate =
- std::variant<DeferredConversionTemplateOverloadCandidate,
- DeferredMethodTemplateOverloadCandidate,
- DeferredFunctionTemplateOverloadCandidate>;
+ struct DeferredConversionTemplateOverloadCandidate
+ : public DeferredTemplateOverloadCandidate {
+ FunctionTemplateDecl *FunctionTemplate;
+ DeclAccessPair FoundDecl;
+ CXXRecordDecl *ActingContext;
+ Expr *From;
+ QualType ToType;
+ };
- static_assert(
- std::is_trivially_destructible_v<DeferredTemplateOverloadCandidate>);
+ static_assert(std::is_trivially_destructible_v<
+ DeferredConversionTemplateOverloadCandidate>);
/// OverloadCandidateSet - A set of overload candidates, used in C++
/// overload resolution (C++ 13.3).
@@ -1197,8 +1197,9 @@ class Sema;
private:
SmallVector<OverloadCandidate, 16> Candidates;
llvm::SmallPtrSet<uintptr_t, 16> Functions;
- SmallVector<DeferredTemplateOverloadCandidate, 8> DeferredCandidates;
+ DeferredTemplateOverloadCandidate *FirstDeferredCandidate;
+ unsigned DeferredCandidatesCount : 8 * sizeof(unsigned) - 1;
LLVM_PREFERRED_TYPE(bool)
unsigned HasDeferredTemplateConstructors : 1;
@@ -1246,12 +1247,27 @@ class Sema;
return reinterpret_cast<T *>(FreeSpaceStart);
}
+ template <typename T> T *allocateDeferredCandidate() {
+ T *C = slabAllocate<T>(1);
+ if (!FirstDeferredCandidate)
+ FirstDeferredCandidate = C;
+ else {
+ auto *F = FirstDeferredCandidate;
+ while (F->Next)
+ F = F->Next;
+ F->Next = C;
+ }
+ DeferredCandidatesCount++;
+ return C;
+ }
+
void destroyCandidates();
public:
OverloadCandidateSet(SourceLocation Loc, CandidateSetKind CSK,
OperatorRewriteInfo RewriteInfo = {})
- : HasDeferredTemplateConstructors(false), Loc(Loc), Kind(CSK),
+ : FirstDeferredCandidate(nullptr), DeferredCandidatesCount(0),
+ HasDeferredTemplateConstructors(false), Loc(Loc), Kind(CSK),
RewriteInfo(RewriteInfo) {}
OverloadCandidateSet(const OverloadCandidateSet &) = delete;
OverloadCandidateSet &operator=(const OverloadCandidateSet &) = delete;
@@ -1290,11 +1306,9 @@ class Sema;
iterator begin() { return Candidates.begin(); }
iterator end() { return Candidates.end(); }
- size_t size() const {
- return Candidates.size() + DeferredCandidates.size();
- }
+ size_t size() const { return Candidates.size() + DeferredCandidatesCount; }
bool empty() const {
- return Candidates.empty() && DeferredCandidates.empty();
+ return Candidates.empty() && DeferredCandidatesCount == 0;
}
/// Allocate storage for conversion sequences for NumConversions
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index fc1965ba93e32..ac072f9d7540b 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -1124,6 +1124,8 @@ void OverloadCandidateSet::clear(CandidateSetKind CSK) {
Candidates.clear();
Functions.clear();
Kind = CSK;
+ FirstDeferredCandidate = nullptr;
+ DeferredCandidatesCount = 0;
HasDeferredTemplateConstructors = false;
}
@@ -11012,16 +11014,20 @@ void OverloadCandidateSet::AddDeferredTemplateCandidate(
bool PartialOverloading, bool AllowExplicit,
CallExpr::ADLCallKind IsADLCandidate, OverloadCandidateParamOrder PO,
bool AggregateCandidateDeduction) {
- DeferredFunctionTemplateOverloadCandidate C{FunctionTemplate,
- FoundDecl,
- Args,
- IsADLCandidate,
- PO,
- SuppressUserConversions,
- PartialOverloading,
- AllowExplicit,
- AggregateCandidateDeduction};
- DeferredCandidates.emplace_back(std::move(C));
+
+ auto *C =
+ allocateDeferredCandidate<DeferredFunctionTemplateOverloadCandidate>();
+
+ C = new (C) DeferredFunctionTemplateOverloadCandidate{
+ {nullptr, DeferredFunctionTemplateOverloadCandidate::Function,
+ /*AllowObjCConversionOnExplicit=*/false,
+ /*AllowResultConversion=*/false, AllowExplicit, SuppressUserConversions,
+ PartialOverloading, AggregateCandidateDeduction},
+ FunctionTemplate,
+ FoundDecl,
+ Args,
+ IsADLCandidate,
+ PO};
HasDeferredTemplateConstructors |=
isa<CXXConstructorDecl>(FunctionTemplate->getTemplatedDecl());
}
@@ -11032,11 +11038,23 @@ void OverloadCandidateSet::AddDeferredMethodTemplateCandidate(
Expr::Classification ObjectClassification, ArrayRef<Expr *> Args,
bool SuppressUserConversions, bool PartialOverloading,
OverloadCandidateParamOrder PO) {
- DeferredMethodTemplateOverloadCandidate C{
- MethodTmpl, FoundDecl, Args, ActingContext,
- ObjectClassification, ObjectType, PO, SuppressUserConversions,
- PartialOverloading};
- DeferredCandidates.emplace_back(std::move(C));
+
+ auto *C =
+ allocateDeferredCandidate<DeferredMethodTemplateOverloadCandidate>();
+
+ C = new (C) DeferredMethodTemplateOverloadCandidate{
+ {nullptr, DeferredFunctionTemplateOverloadCandidate::Method,
+ /*AllowObjCConversionOnExplicit=*/false,
+ /*AllowResultConversion=*/false,
+ /*AllowExplicit=*/false, SuppressUserConversions, PartialOverloading,
+ /*AggregateCandidateDeduction=*/false},
+ MethodTmpl,
+ FoundDecl,
+ Args,
+ ActingContext,
+ ObjectClassification,
+ ObjectType,
+ PO};
}
void OverloadCandidateSet::AddDeferredConversionTemplateCandidate(
@@ -11045,18 +11063,26 @@ void OverloadCandidateSet::AddDeferredConversionTemplateCandidate(
bool AllowObjCConversionOnExplicit, bool AllowExplicit,
bool AllowResultConversion) {
- DeferredConversionTemplateOverloadCandidate C{
- FunctionTemplate, FoundDecl,
- ActingContext, From,
- ToType, AllowObjCConversionOnExplicit,
- AllowExplicit, AllowResultConversion};
+ auto *C =
+ allocateDeferredCandidate<DeferredConversionTemplateOverloadCandidate>();
- DeferredCandidates.emplace_back(std::move(C));
+ C = new (C) DeferredConversionTemplateOverloadCandidate{
+ {nullptr, DeferredFunctionTemplateOverloadCandidate::Conversion,
+ AllowObjCConversionOnExplicit, AllowResultConversion,
+ /*AllowExplicit=*/false,
+ /*SuppressUserConversions=*/false,
+ /*PartialOverloading*/ false,
+ /*AggregateCandidateDeduction=*/false},
+ FunctionTemplate,
+ FoundDecl,
+ ActingContext,
+ From,
+ ToType};
}
static void
AddTemplateOverloadCandidate(Sema &S, OverloadCandidateSet &CandidateSet,
- DeferredMethodTemplateOverloadCandidate &&C) {
+ DeferredMethodTemplateOverloadCandidate &C) {
AddMethodTemplateCandidateImmediately(
S, CandidateSet, C.FunctionTemplate, C.FoundDecl, C.ActingContext,
@@ -11066,7 +11092,7 @@ AddTemplateOverloadCandidate(Sema &S, OverloadCandidateSet &CandidateSet,
static void
AddTemplateOverloadCandidate(Sema &S, OverloadCandidateSet &CandidateSet,
- DeferredFunctionTemplateOverloadCandidate &&C) {
+ DeferredFunctionTemplateOverloadCandidate &C) {
AddTemplateOverloadCandidateImmediately(
S, CandidateSet, C.FunctionTemplate, C.FoundDecl,
/*ExplicitTemplateArgs=*/nullptr, C.Args, C.SuppressUserConversions,
@@ -11076,7 +11102,7 @@ AddTemplateOverloadCandidate(Sema &S, OverloadCandidateSet &CandidateSet,
static void
AddTemplateOverloadCandidate(Sema &S, OverloadCandidateSet &CandidateSet,
- DeferredConversionTemplateOverloadCandidate &&C) {
+ DeferredConversionTemplateOverloadCandidate &C) {
return AddTemplateConversionCandidateImmediately(
S, CandidateSet, C.FunctionTemplate, C.FoundDecl, C.ActingContext, C.From,
C.ToType, C.AllowObjCConversionOnExplicit, C.AllowExplicit,
@@ -11084,15 +11110,30 @@ AddTemplateOverloadCandidate(Sema &S, OverloadCandidateSet &CandidateSet,
}
void OverloadCandidateSet::InjectNonDeducedTemplateCandidates(Sema &S) {
- Candidates.reserve(Candidates.size() + DeferredCandidates.size());
- for (auto &&Elem : DeferredCandidates) {
- std::visit(
- [&](auto &&Cand) {
- AddTemplateOverloadCandidate(S, *this, std::move(Cand));
- },
- Elem);
+ Candidates.reserve(Candidates.size() + DeferredCandidatesCount);
+ DeferredTemplateOverloadCandidate *Cand = FirstDeferredCandidate;
+ while (Cand) {
+ switch (Cand->Kind) {
+ case DeferredTemplateOverloadCandidate::Function:
+ AddTemplateOverloadCandidate(
+ S, *this,
+ *static_cast<DeferredFunctionTemplateOverloadCandidate *>(Cand));
+ break;
+ case DeferredTemplateOverloadCandidate::Method:
+ AddTemplateOverloadCandidate(
+ S, *this,
+ *static_cast<DeferredMethodTemplateOverloadCandidate *>(Cand));
+ break;
+ case DeferredTemplateOverloadCandidate::Conversion:
+ AddTemplateOverloadCandidate(
+ S, *this,
+ *static_cast<DeferredConversionTemplateOverloadCandidate *>(Cand));
+ break;
+ }
+ Cand = Cand->Next;
}
- DeferredCandidates.clear();
+ FirstDeferredCandidate = nullptr;
+ DeferredCandidatesCount = 0;
}
OverloadingResult
@@ -11155,10 +11196,10 @@ OverloadingResult OverloadCandidateSet::BestViableFunction(Sema &S,
iterator &Best) {
assert(shouldDeferTemplateArgumentDeduction(S.getLangOpts()) ||
- DeferredCandidates.empty() &&
+ DeferredCandidatesCount == 0 &&
"Unexpected deferred template candidate");
- bool TwoPhaseResolution = !DeferredCandidates.empty();
+ bool TwoPhaseResolution = DeferredCandidatesCount != 0;
if (TwoPhaseResolution) {
@@ -11269,7 +11310,7 @@ OverloadingResult OverloadCandidateSet::BestViableFunctionImpl(
OverloadingResult R = ResultForBestCandidate(Best);
- if (DeferredCandidates.empty() && !EquivalentCands.empty())
+ if (!EquivalentCands.empty())
S.diagnoseEquivalentInternalLinkageDeclarations(Loc, Best->Function,
EquivalentCands);
return R;
>From 76a860f399ea684b22ab8654672816f1e10df9c5 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Thu, 3 Apr 2025 15:53:36 +0200
Subject: [PATCH 18/32] fix rvalue to lvalue binding
---
clang/include/clang/Sema/Overload.h | 11 ++++++++--
clang/lib/Sema/SemaOverload.cpp | 2 ++
...overload-resolution-deferred-templates.cpp | 21 +++++++++++++++++++
3 files changed, 32 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index 631d105280c56..edd0e4742675a 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -408,8 +408,15 @@ class Sema;
}
bool isPerfect(const ASTContext &C) const {
- return isIdentityConversion() &&
- (!ReferenceBinding || C.hasSameType(getFromType(), getToType(2)));
+ if(!isIdentityConversion())
+ return false;
+ if(!ReferenceBinding)
+ return true;
+ if(!C.hasSameType(getFromType(), getToType(2)))
+ return false;
+ if(BindsToRvalue && IsLvalueReference)
+ return false;
+ return true;
}
ImplicitConversionRank getRank() const;
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index ac072f9d7540b..5b7da1bc4a826 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -11039,6 +11039,8 @@ void OverloadCandidateSet::AddDeferredMethodTemplateCandidate(
bool SuppressUserConversions, bool PartialOverloading,
OverloadCandidateParamOrder PO) {
+ assert(!isa<CXXConstructorDecl>(MethodTmpl->getTemplatedDecl()));
+
auto *C =
allocateDeferredCandidate<DeferredMethodTemplateOverloadCandidate>();
diff --git a/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp b/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
index e6fe155cc4d15..04b7847cb9b82 100644
--- a/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
+++ b/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
@@ -128,6 +128,27 @@ struct Test {
static_assert(__is_constructible(S, Test));
}
+namespace RefBinding {
+
+template <typename> struct remove_reference;
+template <typename _Tp> struct remove_reference<_Tp &> {
+ using type = _Tp;
+};
+template <typename _Tp> remove_reference<_Tp>::type move(_Tp &&);
+template <typename _Head> struct _Head_base {
+ _Head_base(_Head &__h) : _M_head_impl(__h) {}
+ template <typename _UHead> _Head_base(_UHead &&);
+ _Head _M_head_impl;
+};
+
+template <typename _Elements> void forward_as_tuple(_Elements &&) {
+ _Head_base<_Elements &&>(_Elements{});
+}
+struct StringRef {
+ void operator[](const StringRef __k) { forward_as_tuple((move)(__k)); }
+};
+
+}
namespace GH62096 {
template <typename T>
>From 45edd3ebc2d47f59baf7150ed5534eb610130a8a Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Thu, 3 Apr 2025 17:38:07 +0200
Subject: [PATCH 19/32] Disable resolution by perfect match when - We find a
template conversion candidate - A constructor with a dependent explicit
specifier
---
clang/include/clang/Sema/Overload.h | 11 ++++++--
clang/lib/Sema/SemaOverload.cpp | 26 ++++++++++++++-----
...overload-resolution-deferred-templates.cpp | 7 +++++
3 files changed, 35 insertions(+), 9 deletions(-)
diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index edd0e4742675a..b8d6d79c48e3c 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -1206,9 +1206,11 @@ class Sema;
llvm::SmallPtrSet<uintptr_t, 16> Functions;
DeferredTemplateOverloadCandidate *FirstDeferredCandidate;
- unsigned DeferredCandidatesCount : 8 * sizeof(unsigned) - 1;
+ unsigned DeferredCandidatesCount : 8 * sizeof(unsigned) - 2;
LLVM_PREFERRED_TYPE(bool)
unsigned HasDeferredTemplateConstructors : 1;
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned ResolutionByPerfectCandidateIsDisabled : 1;
// Allocator for ConversionSequenceLists and deferred candidate args.
// We store the first few of these
@@ -1274,7 +1276,8 @@ class Sema;
OverloadCandidateSet(SourceLocation Loc, CandidateSetKind CSK,
OperatorRewriteInfo RewriteInfo = {})
: FirstDeferredCandidate(nullptr), DeferredCandidatesCount(0),
- HasDeferredTemplateConstructors(false), Loc(Loc), Kind(CSK),
+ HasDeferredTemplateConstructors(false),
+ ResolutionByPerfectCandidateIsDisabled(false), Loc(Loc), Kind(CSK),
RewriteInfo(RewriteInfo) {}
OverloadCandidateSet(const OverloadCandidateSet &) = delete;
OverloadCandidateSet &operator=(const OverloadCandidateSet &) = delete;
@@ -1387,6 +1390,10 @@ class Sema;
void InjectNonDeducedTemplateCandidates(Sema &S);
+ void DisableResolutionByPerfectCandidate() {
+ ResolutionByPerfectCandidateIsDisabled = true;
+ }
+
/// Find the best viable function on this overload set, if it exists.
OverloadingResult BestViableFunction(Sema &S, SourceLocation Loc,
OverloadCandidateSet::iterator& Best);
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 5b7da1bc4a826..924898d91370e 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -1127,6 +1127,7 @@ void OverloadCandidateSet::clear(CandidateSetKind CSK) {
FirstDeferredCandidate = nullptr;
DeferredCandidatesCount = 0;
HasDeferredTemplateConstructors = false;
+ ResolutionByPerfectCandidateIsDisabled = false;
}
namespace {
@@ -7989,15 +7990,20 @@ void Sema::AddTemplateOverloadCandidate(
if (!CandidateSet.isNewCandidate(FunctionTemplate, PO))
return;
+ bool DependentExplicitSpecifier = hasDependentExplicit(FunctionTemplate);
+
if (ExplicitTemplateArgs ||
!CandidateSet.shouldDeferTemplateArgumentDeduction(getLangOpts()) ||
(isa<CXXConstructorDecl>(FunctionTemplate->getTemplatedDecl()) &&
- hasDependentExplicit(FunctionTemplate))) {
+ DependentExplicitSpecifier)) {
AddTemplateOverloadCandidateImmediately(
*this, CandidateSet, FunctionTemplate, FoundDecl, ExplicitTemplateArgs,
Args, SuppressUserConversions, PartialOverloading, AllowExplicit,
IsADLCandidate, PO, AggregateCandidateDeduction);
+
+ if (DependentExplicitSpecifier)
+ CandidateSet.DisableResolutionByPerfectCandidate();
return;
}
@@ -8382,13 +8388,15 @@ void Sema::AddTemplateConversionCandidate(
return;
if (!CandidateSet.shouldDeferTemplateArgumentDeduction(getLangOpts()) ||
- hasDependentExplicit(FunctionTemplate)) {
-
+ CandidateSet.getKind() ==
+ OverloadCandidateSet::CSK_InitByUserDefinedConversion ||
+ CandidateSet.getKind() == OverloadCandidateSet::CSK_InitByConstructor) {
AddTemplateConversionCandidateImmediately(
*this, CandidateSet, FunctionTemplate, FoundDecl, ActingDC, From,
ToType, AllowObjCConversionOnExplicit, AllowExplicit,
AllowResultConversion);
+ CandidateSet.DisableResolutionByPerfectCandidate();
return;
}
@@ -11028,6 +11036,7 @@ void OverloadCandidateSet::AddDeferredTemplateCandidate(
Args,
IsADLCandidate,
PO};
+
HasDeferredTemplateConstructors |=
isa<CXXConstructorDecl>(FunctionTemplate->getTemplatedDecl());
}
@@ -11199,19 +11208,19 @@ OverloadingResult OverloadCandidateSet::BestViableFunction(Sema &S,
assert(shouldDeferTemplateArgumentDeduction(S.getLangOpts()) ||
DeferredCandidatesCount == 0 &&
- "Unexpected deferred template candidate");
+ "Unexpected deferred template candidates");
- bool TwoPhaseResolution = DeferredCandidatesCount != 0;
+ bool TwoPhaseResolution =
+ DeferredCandidatesCount != 0 && !ResolutionByPerfectCandidateIsDisabled;
if (TwoPhaseResolution) {
PerfectViableFunction(S, Loc, Best);
if (Best != end())
return ResultForBestCandidate(Best);
-
- InjectNonDeducedTemplateCandidates(S);
}
+ InjectNonDeducedTemplateCandidates(S);
return BestViableFunctionImpl(S, Loc, Best);
}
@@ -11220,8 +11229,10 @@ void OverloadCandidateSet::PerfectViableFunction(
Best = end();
for (auto It = begin(); It != end(); ++It) {
+
if (!It->isPerfectMatch(S.getASTContext()))
continue;
+
// We found a suitable conversion function
// but if there is a template constructor in the target class
// we might prefer that instead.
@@ -11246,6 +11257,7 @@ void OverloadCandidateSet::PerfectViableFunction(
Best = It;
continue;
}
+ // ambiguous
Best = end();
break;
}
diff --git a/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp b/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
index 04b7847cb9b82..877816ca013ec 100644
--- a/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
+++ b/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
@@ -150,6 +150,13 @@ struct StringRef {
}
+template <class> struct tuple {};
+struct BonkersBananas {
+ template <class T> operator T();
+ template <class = void> explicit operator tuple<int>() = delete;
+};
+static_assert(!__is_constructible(tuple<int>, BonkersBananas));
+
namespace GH62096 {
template <typename T>
struct Oops {
>From d580d93c441ac7c57555435e4d3752abf7284df1 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Tue, 15 Apr 2025 08:37:15 +0200
Subject: [PATCH 20/32] address Erich's, format, add release notes
---
clang/docs/ReleaseNotes.rst | 6 ++++++
clang/include/clang/Sema/Overload.h | 25 +++++++++++++++++++++----
2 files changed, 27 insertions(+), 4 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index c75d83a6d1a7a..8f6b15170bc8d 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -96,6 +96,12 @@ C++ Language Changes
asm((std::string_view("nop")) ::: (std::string_view("memory")));
}
+- Clang now implements the changes to overload resolution proposed by `P3606 <https://wg21.link/P3606R0>`_.
+ If a non-template candidate exists in an overload set that is a perfect match (all conversion sequences
+ are identity conversions) template candiates are not instantiated.
+ Diagnostics that would have resulted from the instantiation of these template candidates are no longer
+ produced. This aligns Clang closer to the behavior of GCC and fixes (#GH62096), (#GH74581), and (#GH74581).
+
C++2c Feature Support
^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index b8d6d79c48e3c..38397b428bdba 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -408,13 +408,17 @@ class Sema;
}
bool isPerfect(const ASTContext &C) const {
- if(!isIdentityConversion())
+ if (!isIdentityConversion())
return false;
- if(!ReferenceBinding)
+ // If we are not performing a reference binding, we can skip comparing
+ // the types, which has a noticeable performance impact.
+ if (!ReferenceBinding) {
+ assert(C.hasSameType(getFromType(), getToType(2)));
return true;
- if(!C.hasSameType(getFromType(), getToType(2)))
+ }
+ if (!C.hasSameType(getFromType(), getToType(2)))
return false;
- if(BindsToRvalue && IsLvalueReference)
+ if (BindsToRvalue && IsLvalueReference)
return false;
return true;
}
@@ -755,6 +759,9 @@ class Sema;
Standard.setAllToTypes(T);
}
+ /// A conversion sequence is perfect if
+ /// it is an identity conversion and
+ /// the type of the source is the same as the type of the target.
bool isPerfect(const ASTContext &C) const {
return isStandard() && Standard.isPerfect(C);
}
@@ -995,6 +1002,8 @@ class Sema;
return false;
}
+ // An overload is a perfect match if the conversion
+ // sequences for each argument are perfect.
bool isPerfectMatch(const ASTContext &Ctx) const {
if (!Viable)
return false;
@@ -1046,7 +1055,10 @@ class Sema;
};
struct DeferredTemplateOverloadCandidate {
+
+ // intrusive linked list support for allocateDeferredCandidate
DeferredTemplateOverloadCandidate *Next = nullptr;
+
enum Kind { Function, Method, Conversion };
LLVM_PREFERRED_TYPE(Kind)
@@ -1256,6 +1268,11 @@ class Sema;
return reinterpret_cast<T *>(FreeSpaceStart);
}
+ // Because the size of OverloadCandidateSet has a noticeable impact on
+ // performance, we store each deferred template candidate in the slab
+ // allocator such that deferred candidates are ultimately a singly-linked
+ // intrusive linked list. This ends up being much more efficient than a
+ // SmallVector that is empty in the common case.
template <typename T> T *allocateDeferredCandidate() {
T *C = slabAllocate<T>(1);
if (!FirstDeferredCandidate)
>From 533e8df3839aa78df2489b70ffc231a8796c0509 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Tue, 15 Apr 2025 18:03:36 +0200
Subject: [PATCH 21/32] address more of Erich's feedback
---
clang/include/clang/Sema/Overload.h | 14 ++++++++------
clang/include/clang/Sema/Sema.h | 2 --
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index 38397b428bdba..d5c65ebceba1d 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -407,6 +407,8 @@ class Sema;
Third == ICK_Identity;
}
+ /// A conversion sequence is perfect if it is an identity conversion and
+ /// the type of the source is the same as the type of the target.
bool isPerfect(const ASTContext &C) const {
if (!isIdentityConversion())
return false;
@@ -759,8 +761,7 @@ class Sema;
Standard.setAllToTypes(T);
}
- /// A conversion sequence is perfect if
- /// it is an identity conversion and
+ /// A conversion sequence is perfect if it is an identity conversion and
/// the type of the source is the same as the type of the target.
bool isPerfect(const ASTContext &C) const {
return isStandard() && Standard.isPerfect(C);
@@ -1008,9 +1009,7 @@ class Sema;
if (!Viable)
return false;
for (const auto &C : Conversions) {
- if (!C.isInitialized())
- return false;
- if (!C.isPerfect(Ctx))
+ if (!C.isInitialized() || !C.isPerfect(Ctx))
return false;
}
if (isa_and_nonnull<CXXConversionDecl>(Function))
@@ -1142,6 +1141,9 @@ class Sema;
/// Resolve a call through the address of an overload set.
CSK_AddressOfOverloadSet,
+ /// When doing overload resolution during code completion,
+ /// we want to show all viable candidates, including otherwise
+ /// deferred template candidates.
CSK_CodeCompletion,
};
@@ -1217,7 +1219,7 @@ class Sema;
SmallVector<OverloadCandidate, 16> Candidates;
llvm::SmallPtrSet<uintptr_t, 16> Functions;
- DeferredTemplateOverloadCandidate *FirstDeferredCandidate;
+ DeferredTemplateOverloadCandidate *FirstDeferredCandidate = nullptr;
unsigned DeferredCandidatesCount : 8 * sizeof(unsigned) - 2;
LLVM_PREFERRED_TYPE(bool)
unsigned HasDeferredTemplateConstructors : 1;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 82c3fe024b28b..a757f4c6430ae 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -60,7 +60,6 @@
#include "clang/Sema/DeclSpec.h"
#include "clang/Sema/ExternalSemaSource.h"
#include "clang/Sema/IdentifierResolver.h"
-#include "clang/Sema/Overload.h"
#include "clang/Sema/Ownership.h"
#include "clang/Sema/ParsedAttr.h"
#include "clang/Sema/Redeclaration.h"
@@ -10393,7 +10392,6 @@ class Sema final : public SemaBase {
/// Add a C++ function template specialization as a candidate
/// in the candidate set, using template argument deduction to produce
/// an appropriate function template specialization.
-
void AddTemplateOverloadCandidate(
FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *> Args,
>From 037e54c7b751d4402d6ea7f490f0a4c4e51c8a83 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Tue, 15 Apr 2025 18:35:33 +0200
Subject: [PATCH 22/32] Clarify changelog
---
clang/docs/ReleaseNotes.rst | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 8f6b15170bc8d..bc0b01f8ca843 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -96,11 +96,11 @@ C++ Language Changes
asm((std::string_view("nop")) ::: (std::string_view("memory")));
}
-- Clang now implements the changes to overload resolution proposed by `P3606 <https://wg21.link/P3606R0>`_.
- If a non-template candidate exists in an overload set that is a perfect match (all conversion sequences
- are identity conversions) template candiates are not instantiated.
+- Clang now implements the changes to overload resolution proposed by section 1 and 2 of
+ `P3606 <https://wg21.link/P3606R0>`_. If a non-template candidate exists in an overload set that is
+ a perfect match (all conversion sequences are identity conversions) template candiates are not instantiated.
Diagnostics that would have resulted from the instantiation of these template candidates are no longer
- produced. This aligns Clang closer to the behavior of GCC and fixes (#GH62096), (#GH74581), and (#GH74581).
+ produced. This aligns Clang closer to the behavior of GCC, and fixes (#GH62096), (#GH74581), and (#GH74581).
C++2c Feature Support
^^^^^^^^^^^^^^^^^^^^^
>From 4cbb11caaa142b2471d257b62a3791934a86d5cf Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Tue, 15 Apr 2025 22:51:48 +0200
Subject: [PATCH 23/32] fix assertion
---
clang/include/clang/Sema/Overload.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index d5c65ebceba1d..813811af06e89 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -415,7 +415,7 @@ class Sema;
// If we are not performing a reference binding, we can skip comparing
// the types, which has a noticeable performance impact.
if (!ReferenceBinding) {
- assert(C.hasSameType(getFromType(), getToType(2)));
+ assert(First || C.hasSameUnqualifiedType(getFromType(), getToType(2)));
return true;
}
if (!C.hasSameType(getFromType(), getToType(2)))
>From 9e95b94d57f6ac93e1243baf8f926ddf5b115557 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Wed, 16 Apr 2025 14:00:05 +0200
Subject: [PATCH 24/32] cleanups
---
clang/lib/Sema/SemaOverload.cpp | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 924898d91370e..deef01c946feb 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -45,7 +45,6 @@
#include <cstddef>
#include <cstdlib>
#include <optional>
-#include <variant>
using namespace clang;
using namespace sema;
@@ -7838,7 +7837,7 @@ static void AddMethodTemplateCandidateImmediately(
Candidate.Function = MethodTmpl->getTemplatedDecl();
Candidate.Viable = false;
Candidate.RewriteKind =
- CandidateSet.getRewriteInfo().getRewriteKind(Candidate.Function, PO);
+ CandidateSet.getRewriteInfo().getRewriteKind(Candidate.Function, PO);
Candidate.IsSurrogate = false;
Candidate.IgnoreObjectArgument =
cast<CXXMethodDecl>(Candidate.Function)->isStatic() ||
@@ -7951,7 +7950,7 @@ static void AddTemplateOverloadCandidateImmediately(
Candidate.Function = FunctionTemplate->getTemplatedDecl();
Candidate.Viable = false;
Candidate.RewriteKind =
- CandidateSet.getRewriteInfo().getRewriteKind(Candidate.Function, PO);
+ CandidateSet.getRewriteInfo().getRewriteKind(Candidate.Function, PO);
Candidate.IsSurrogate = false;
Candidate.IsADLCandidate = llvm::to_underlying(IsADLCandidate);
// Ignore the object argument if there is one, since we don't have an object
>From a8e7b487e5511daa42617f6f30ab948084deb76e Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Wed, 16 Apr 2025 22:18:00 +0200
Subject: [PATCH 25/32] fix assertion
---
clang/include/clang/Sema/Overload.h | 4 ++--
clang/lib/Sema/SemaOverload.cpp | 3 ++-
.../overload-resolution-deferred-templates.cpp | 15 +++++++++++++++
3 files changed, 19 insertions(+), 3 deletions(-)
diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index 813811af06e89..5037795f2d32d 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -1005,14 +1005,14 @@ class Sema;
// An overload is a perfect match if the conversion
// sequences for each argument are perfect.
- bool isPerfectMatch(const ASTContext &Ctx) const {
+ bool isPerfectMatch(const ASTContext &Ctx, bool ForConversion) const {
if (!Viable)
return false;
for (const auto &C : Conversions) {
if (!C.isInitialized() || !C.isPerfect(Ctx))
return false;
}
- if (isa_and_nonnull<CXXConversionDecl>(Function))
+ if (ForConversion && isa_and_nonnull<CXXConversionDecl>(Function))
return FinalConversion.isPerfect(Ctx);
return true;
}
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index deef01c946feb..406ea05c06426 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -11229,7 +11229,8 @@ void OverloadCandidateSet::PerfectViableFunction(
Best = end();
for (auto It = begin(); It != end(); ++It) {
- if (!It->isPerfectMatch(S.getASTContext()))
+ if (!It->isPerfectMatch(S.getASTContext(),
+ Kind == CSK_InitByUserDefinedConversion))
continue;
// We found a suitable conversion function
diff --git a/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp b/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
index 877816ca013ec..9139fd2688790 100644
--- a/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
+++ b/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
@@ -183,3 +183,18 @@ void h(short n) { f(n); }
}
#endif
+
+template<typename ...Ts>
+struct t1 {
+};
+struct t6 {
+ template<typename T = int>
+ operator t1<float>() {
+ return {};
+ }
+};
+
+int main() {
+ t6 v6;
+ v6.operator t1<float>();
+}
>From 9f15bd39b44c3680c1b750d8005539389a3ac9cc Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Wed, 16 Apr 2025 22:46:55 +0200
Subject: [PATCH 26/32] Cleaner approach
---
clang/include/clang/Sema/Overload.h | 10 +++++++---
clang/lib/Sema/SemaOverload.cpp | 5 +++--
2 files changed, 10 insertions(+), 5 deletions(-)
diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index 5037795f2d32d..d105afc7daf94 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -963,6 +963,10 @@ class Sema;
LLVM_PREFERRED_TYPE(CallExpr::ADLCallKind)
unsigned IsADLCandidate : 1;
+ /// Whether FinalConversion has been set.
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned HasFinalConversion : 1;
+
/// Whether this is a rewritten candidate, and if so, of what kind?
LLVM_PREFERRED_TYPE(OverloadCandidateRewriteKind)
unsigned RewriteKind : 2;
@@ -1005,14 +1009,14 @@ class Sema;
// An overload is a perfect match if the conversion
// sequences for each argument are perfect.
- bool isPerfectMatch(const ASTContext &Ctx, bool ForConversion) const {
+ bool isPerfectMatch(const ASTContext &Ctx) const {
if (!Viable)
return false;
for (const auto &C : Conversions) {
if (!C.isInitialized() || !C.isPerfect(Ctx))
return false;
}
- if (ForConversion && isa_and_nonnull<CXXConversionDecl>(Function))
+ if (HasFinalConversion)
return FinalConversion.isPerfect(Ctx);
return true;
}
@@ -1050,7 +1054,7 @@ class Sema;
: IsSurrogate(false), IgnoreObjectArgument(false),
TookAddressOfOverload(false), StrictPackMatch(false),
IsADLCandidate(llvm::to_underlying(CallExpr::NotADL)),
- RewriteKind(CRK_None) {}
+ HasFinalConversion(false), RewriteKind(CRK_None) {}
};
struct DeferredTemplateOverloadCandidate {
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 406ea05c06426..e3cbecdf9a39d 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -8174,6 +8174,7 @@ void Sema::AddConversionCandidate(
Candidate.FinalConversion.setAsIdentityConversion();
Candidate.FinalConversion.setFromType(ConvType);
Candidate.FinalConversion.setAllToTypes(ToType);
+ Candidate.HasFinalConversion = true;
Candidate.Viable = true;
Candidate.ExplicitCallArguments = 1;
Candidate.StrictPackMatch = StrictPackMatch;
@@ -8278,6 +8279,7 @@ void Sema::AddConversionCandidate(
switch (ICS.getKind()) {
case ImplicitConversionSequence::StandardConversion:
Candidate.FinalConversion = ICS.Standard;
+ Candidate.HasFinalConversion = true;
// C++ [over.ics.user]p3:
// If the user-defined conversion is specified by a specialization of a
@@ -11229,8 +11231,7 @@ void OverloadCandidateSet::PerfectViableFunction(
Best = end();
for (auto It = begin(); It != end(); ++It) {
- if (!It->isPerfectMatch(S.getASTContext(),
- Kind == CSK_InitByUserDefinedConversion))
+ if (!It->isPerfectMatch(S.getASTContext()))
continue;
// We found a suitable conversion function
>From 6611bad2187452b29cb5e7aca21ad59441329ce7 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Wed, 16 Apr 2025 22:51:22 +0200
Subject: [PATCH 27/32] Add comment
---
clang/include/clang/Sema/Overload.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index d105afc7daf94..e667147bfac7e 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -415,6 +415,8 @@ class Sema;
// If we are not performing a reference binding, we can skip comparing
// the types, which has a noticeable performance impact.
if (!ReferenceBinding) {
+ // The types might differ if there is an array-to-pointer conversion
+ // or lvalue-to-rvalue conversion.
assert(First || C.hasSameUnqualifiedType(getFromType(), getToType(2)));
return true;
}
>From 64716b4e84a07894d6aa8471d50a8c042cb4f5dc Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Wed, 16 Apr 2025 23:02:06 +0200
Subject: [PATCH 28/32] Add asserts
---
clang/lib/Sema/SemaInit.cpp | 3 ++-
clang/lib/Sema/SemaOverload.cpp | 6 ++++++
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 9cb8f99c61e00..9e802a175bb05 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -5231,7 +5231,7 @@ static OverloadingResult TryRefInitWithConversionFunction(
// Add the final conversion sequence, if necessary.
if (NewRefRelationship == Sema::Ref_Incompatible) {
- assert(!isa<CXXConstructorDecl>(Function) &&
+ assert(Best->HasFinalConversion && !isa<CXXConstructorDecl>(Function) &&
"should not have conversion after constructor");
ImplicitConversionSequence ICS;
@@ -6200,6 +6200,7 @@ static void TryUserDefinedConversion(Sema &S,
// If the conversion following the call to the conversion function
// is interesting, add it as a separate step.
+ assert(Best->HasFinalConversion);
if (Best->FinalConversion.First || Best->FinalConversion.Second ||
Best->FinalConversion.Third) {
ImplicitConversionSequence ICS;
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index e3cbecdf9a39d..3b3ac9f0f7a73 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -4079,6 +4079,9 @@ IsUserDefinedConversion(Sema &S, Expr *From, QualType ToType,
}
if (CXXConversionDecl *Conversion
= dyn_cast<CXXConversionDecl>(Best->Function)) {
+
+ assert(Best->HasFinalConversion);
+
// C++ [over.ics.user]p1:
//
// [...] If the user-defined conversion is specified by a
@@ -5175,6 +5178,7 @@ FindConversionForRefInit(Sema &S, ImplicitConversionSequence &ICS,
if (!Best->FinalConversion.DirectBinding)
return false;
+ assert(Best->HasFinalConversion);
ICS.setUserDefined();
ICS.UserDefined.Before = Best->Conversions[0].Standard;
ICS.UserDefined.After = Best->FinalConversion;
@@ -10758,6 +10762,8 @@ bool clang::isBetterOverloadCandidate(
Cand1.Function && Cand2.Function &&
isa<CXXConversionDecl>(Cand1.Function) &&
isa<CXXConversionDecl>(Cand2.Function)) {
+
+ assert(Cand1.HasFinalConversion && Cand2.HasFinalConversion);
// First check whether we prefer one of the conversion functions over the
// other. This only distinguishes the results in non-standard, extension
// cases such as the conversion from a lambda closure type to a function
>From 595ca87ea9c13aa866acc7130d086b16c9913bc4 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Wed, 16 Apr 2025 23:11:26 +0200
Subject: [PATCH 29/32] fix missplaced assert
---
clang/lib/Sema/SemaOverload.cpp | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 3b3ac9f0f7a73..b7a981e08ead9 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -5165,6 +5165,9 @@ FindConversionForRefInit(Sema &S, ImplicitConversionSequence &ICS,
OverloadCandidateSet::iterator Best;
switch (CandidateSet.BestViableFunction(S, DeclLoc, Best)) {
case OR_Success:
+
+ assert(Best->HasFinalConversion);
+
// C++ [over.ics.ref]p1:
//
// [...] If the parameter binds directly to the result of
@@ -5178,7 +5181,6 @@ FindConversionForRefInit(Sema &S, ImplicitConversionSequence &ICS,
if (!Best->FinalConversion.DirectBinding)
return false;
- assert(Best->HasFinalConversion);
ICS.setUserDefined();
ICS.UserDefined.Before = Best->Conversions[0].Standard;
ICS.UserDefined.After = Best->FinalConversion;
>From 8764a6a6bf7b047e1e3063988d3f6ceeebf1c69d Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Thu, 17 Apr 2025 07:41:07 +0200
Subject: [PATCH 30/32] typo
---
clang/docs/ReleaseNotes.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index bc0b01f8ca843..7b76a2caa694b 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -98,7 +98,7 @@ C++ Language Changes
- Clang now implements the changes to overload resolution proposed by section 1 and 2 of
`P3606 <https://wg21.link/P3606R0>`_. If a non-template candidate exists in an overload set that is
- a perfect match (all conversion sequences are identity conversions) template candiates are not instantiated.
+ a perfect match (all conversion sequences are identity conversions) template candidates are not instantiated.
Diagnostics that would have resulted from the instantiation of these template candidates are no longer
produced. This aligns Clang closer to the behavior of GCC, and fixes (#GH62096), (#GH74581), and (#GH74581).
>From fb78e0e67689c79b52a0c4d67dbe106df65fed7c Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Thu, 17 Apr 2025 23:17:42 +0200
Subject: [PATCH 31/32] Fix assert
---
clang/include/clang/Sema/Overload.h | 12 ++++++++++--
.../overload-resolution-deferred-templates.cpp | 8 +++++++-
2 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index e667147bfac7e..5a9ae58447a0d 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -415,9 +415,17 @@ class Sema;
// If we are not performing a reference binding, we can skip comparing
// the types, which has a noticeable performance impact.
if (!ReferenceBinding) {
+#ifndef NDEBUG
+ auto Decay = [&](QualType T) {
+ return (T->isArrayType() || T->isFunctionType())
+ ? C.getDecayedType(T) : T;
+ };
// The types might differ if there is an array-to-pointer conversion
- // or lvalue-to-rvalue conversion.
- assert(First || C.hasSameUnqualifiedType(getFromType(), getToType(2)));
+ // an function-to-pointer conversion, or lvalue-to-rvalue conversion.
+ // In some cases, this may happen even if First is not set.
+ assert(C.hasSameUnqualifiedType(Decay(getFromType()),
+ Decay(getToType(2))));
+#endif
return true;
}
if (!C.hasSameType(getFromType(), getToType(2)))
diff --git a/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp b/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
index 9139fd2688790..d68a942f64050 100644
--- a/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
+++ b/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
@@ -194,7 +194,13 @@ struct t6 {
}
};
-int main() {
+void testT6() {
t6 v6;
v6.operator t1<float>();
}
+
+
+using a = void(int &);
+template <typename c> void d(c &);
+void f(a);
+template <class> void f(bool j) { f(&d<int>); }
>From 107445e5f5f991f3d533f4cc7fe03525f05419bf Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Thu, 17 Apr 2025 23:53:05 +0200
Subject: [PATCH 32/32] formatting
---
clang/include/clang/Sema/Overload.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index 5a9ae58447a0d..55d714c1222a1 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -417,8 +417,8 @@ class Sema;
if (!ReferenceBinding) {
#ifndef NDEBUG
auto Decay = [&](QualType T) {
- return (T->isArrayType() || T->isFunctionType())
- ? C.getDecayedType(T) : T;
+ return (T->isArrayType() || T->isFunctionType()) ? C.getDecayedType(T)
+ : T;
};
// The types might differ if there is an array-to-pointer conversion
// an function-to-pointer conversion, or lvalue-to-rvalue conversion.
More information about the cfe-commits
mailing list