[c++20] Add rewriting from comparison operators to <=> / ==.

This adds support for rewriting <, >, <=, and >= to a normal or reversed
call to operator<=>, for rewriting != to a normal or reversed call to
operator==, and for rewriting <=> and == to reversed forms of those same
operators.

Note that this is a breaking change for various C++17 code patterns,
including some in use in LLVM. The most common patterns (where an
operator== becomes ambiguous with a reversed form of itself) are still
accepted under this patch, as an extension (with a warning). I'm hopeful
that we can get the language rules fixed before C++20 ships, and the
extension warning is aimed primarily at providing data to inform that
decision.

llvm-svn: 375306
This commit is contained in:
Richard Smith 2019-10-19 00:04:43 +00:00
parent 778dc0f1d4
commit 974c8b7e2f
21 changed files with 966 additions and 199 deletions

View File

@ -220,6 +220,40 @@ public:
}
};
/// Represents a call to a CUDA kernel function.
class CUDAKernelCallExpr final : public CallExpr {
friend class ASTStmtReader;
enum { CONFIG, END_PREARG };
// CUDAKernelCallExpr has some trailing objects belonging
// to CallExpr. See CallExpr for the details.
CUDAKernelCallExpr(Expr *Fn, CallExpr *Config, ArrayRef<Expr *> Args,
QualType Ty, ExprValueKind VK, SourceLocation RP,
unsigned MinNumArgs);
CUDAKernelCallExpr(unsigned NumArgs, EmptyShell Empty);
public:
static CUDAKernelCallExpr *Create(const ASTContext &Ctx, Expr *Fn,
CallExpr *Config, ArrayRef<Expr *> Args,
QualType Ty, ExprValueKind VK,
SourceLocation RP, unsigned MinNumArgs = 0);
static CUDAKernelCallExpr *CreateEmpty(const ASTContext &Ctx,
unsigned NumArgs, EmptyShell Empty);
const CallExpr *getConfig() const {
return cast_or_null<CallExpr>(getPreArg(CONFIG));
}
CallExpr *getConfig() { return cast_or_null<CallExpr>(getPreArg(CONFIG)); }
static bool classof(const Stmt *T) {
return T->getStmtClass() == CUDAKernelCallExprClass;
}
};
/// A rewritten comparison expression that was originally written using
/// operator syntax.
///
@ -310,40 +344,6 @@ public:
}
};
/// Represents a call to a CUDA kernel function.
class CUDAKernelCallExpr final : public CallExpr {
friend class ASTStmtReader;
enum { CONFIG, END_PREARG };
// CUDAKernelCallExpr has some trailing objects belonging
// to CallExpr. See CallExpr for the details.
CUDAKernelCallExpr(Expr *Fn, CallExpr *Config, ArrayRef<Expr *> Args,
QualType Ty, ExprValueKind VK, SourceLocation RP,
unsigned MinNumArgs);
CUDAKernelCallExpr(unsigned NumArgs, EmptyShell Empty);
public:
static CUDAKernelCallExpr *Create(const ASTContext &Ctx, Expr *Fn,
CallExpr *Config, ArrayRef<Expr *> Args,
QualType Ty, ExprValueKind VK,
SourceLocation RP, unsigned MinNumArgs = 0);
static CUDAKernelCallExpr *CreateEmpty(const ASTContext &Ctx,
unsigned NumArgs, EmptyShell Empty);
const CallExpr *getConfig() const {
return cast_or_null<CallExpr>(getPreArg(CONFIG));
}
CallExpr *getConfig() { return cast_or_null<CallExpr>(getPreArg(CONFIG)); }
static bool classof(const Stmt *T) {
return T->getStmtClass() == CUDAKernelCallExprClass;
}
};
/// Abstract class common to all of the C++ "named"/"keyword" casts.
///
/// This abstract class is inherited by all of the classes

View File

@ -3769,7 +3769,8 @@ def note_ovl_too_many_candidates : Note<
"pass -fshow-overloads=all to show them">;
def select_ovl_candidate_kind : TextSubstitution<
"%select{function|function|constructor|"
"%select{function|function|function (with reversed parameter order)|"
"constructor|"
"constructor (the implicit default constructor)|"
"constructor (the implicit copy constructor)|"
"constructor (the implicit move constructor)|"
@ -3990,6 +3991,13 @@ def err_ovl_ambiguous_oper_unary : Error<
"use of overloaded operator '%0' is ambiguous (operand type %1)">;
def err_ovl_ambiguous_oper_binary : Error<
"use of overloaded operator '%0' is ambiguous (with operand types %1 and %2)">;
def ext_ovl_ambiguous_oper_binary_reversed : ExtWarn<
"ISO C++20 considers use of overloaded operator '%0' (with operand types %1 "
"and %2) to be ambiguous despite there being a unique best viable function">,
InGroup<DiagGroup<"ambiguous-reversed-operator">>, SFINAEFailure;
def note_ovl_ambiguous_oper_binary_reversed_candidate : Note<
"ambiguity is between a regular call to this operator and a call with the "
"argument order reversed">;
def err_ovl_no_viable_oper : Error<"no viable overloaded '%0'">;
def note_assign_lhs_incomplete : Note<"type %0 is incomplete">;
def err_ovl_deleted_oper : Error<
@ -3997,6 +4005,9 @@ def err_ovl_deleted_oper : Error<
def err_ovl_deleted_special_oper : Error<
"object of type %0 cannot be %select{constructed|copied|moved|assigned|"
"assigned|destroyed}1 because its %sub{select_special_member_kind}1 is implicitly deleted">;
def err_ovl_rewrite_equalequal_not_bool : Error<
"return type %0 of selected 'operator==' function for rewritten "
"'%1' comparison is not 'bool'">;
def err_ovl_no_viable_subscript :
Error<"no viable overloaded operator[] for type %0">;
def err_ovl_no_oper :
@ -9961,6 +9972,8 @@ def err_std_compare_type_not_supported : Error<
"member '%2' is missing|"
"the type is not trivially copyable|"
"the type does not have the expected form}1">;
def note_rewriting_operator_as_spaceship : Note<
"while rewriting comparison as call to 'operator<=>' declared here">;
// Memory Tagging Extensions (MTE) diagnostics
def err_memtag_arg_null_or_pointer : Error<

View File

@ -30,6 +30,25 @@ enum OverloadedOperatorKind : int {
/// the preceding "operator" keyword.
const char *getOperatorSpelling(OverloadedOperatorKind Operator);
/// Get the other overloaded operator that the given operator can be rewritten
/// into, if any such operator exists.
inline OverloadedOperatorKind
getRewrittenOverloadedOperator(OverloadedOperatorKind Kind) {
switch (Kind) {
case OO_Less:
case OO_LessEqual:
case OO_Greater:
case OO_GreaterEqual:
return OO_Spaceship;
case OO_ExclaimEqual:
return OO_EqualEqual;
default:
return OO_None;
}
}
} // end namespace clang
#endif

View File

@ -71,6 +71,30 @@ class Sema;
OCD_ViableCandidates
};
/// The parameter ordering that will be used for the candidate. This is
/// used to represent C++20 binary operator rewrites that reverse the order
/// of the arguments. If the parameter ordering is Reversed, the Args list is
/// reversed (but obviously the ParamDecls for the function are not).
///
/// After forming an OverloadCandidate with reversed parameters, the list
/// of conversions will (as always) be indexed by argument, so will be
/// in reverse parameter order.
enum class OverloadCandidateParamOrder : char { Normal, Reversed };
/// The kinds of rewrite we perform on overload candidates. Note that the
/// values here are chosen to serve as both bitflags and as a rank (lower
/// values are preferred by overload resolution).
enum OverloadCandidateRewriteKind : unsigned {
/// Candidate is not a rewritten candidate.
CRK_None = 0x0,
/// Candidate is a rewritten candidate with a different operator name.
CRK_DifferentOperator = 0x1,
/// Candidate is a rewritten candidate with a reversed order of parameters.
CRK_Reversed = 0x2,
};
/// ImplicitConversionKind - The kind of implicit conversion used to
/// convert an argument to a parameter's type. The enumerator values
/// match with the table titled 'Conversions' in [over.ics.scs] and are listed
@ -757,7 +781,8 @@ class Sema;
CXXConversionDecl *Surrogate;
/// The conversion sequences used to convert the function arguments
/// to the function parameters.
/// to the function parameters. Note that these are indexed by argument,
/// so may not match the parameter order of Function.
ConversionSequenceList Conversions;
/// The FixIt hints which can be used to fix the Bad candidate.
@ -783,6 +808,9 @@ class Sema;
/// True if the candidate was found using ADL.
CallExpr::ADLCallKind IsADLCandidate : 1;
/// Whether this is a rewritten candidate, and if so, of what kind?
OverloadCandidateRewriteKind RewriteKind : 2;
/// FailureKind - The reason why this candidate is not viable.
/// Actually an OverloadFailureKind.
unsigned char FailureKind;
@ -838,7 +866,8 @@ class Sema;
private:
friend class OverloadCandidateSet;
OverloadCandidate() : IsADLCandidate(CallExpr::NotADL) {}
OverloadCandidate()
: IsADLCandidate(CallExpr::NotADL), RewriteKind(CRK_None) {}
};
/// OverloadCandidateSet - A set of overload candidates, used in C++
@ -867,9 +896,54 @@ class Sema;
CSK_InitByConstructor,
};
/// Information about operator rewrites to consider when adding operator
/// functions to a candidate set.
struct OperatorRewriteInfo {
OperatorRewriteInfo()
: OriginalOperator(OO_None), AllowRewrittenCandidates(false) {}
OperatorRewriteInfo(OverloadedOperatorKind Op, bool AllowRewritten)
: OriginalOperator(Op), AllowRewrittenCandidates(AllowRewritten) {}
/// The original operator as written in the source.
OverloadedOperatorKind OriginalOperator;
/// Whether we should include rewritten candidates in the overload set.
bool AllowRewrittenCandidates;
/// Would use of this function result in a rewrite using a different
/// operator?
bool isRewrittenOperator(const FunctionDecl *FD) {
return OriginalOperator &&
FD->getDeclName().getCXXOverloadedOperator() != OriginalOperator;
}
bool isAcceptableCandidate(const FunctionDecl *FD) {
return AllowRewrittenCandidates || !isRewrittenOperator(FD);
}
/// Determine the kind of rewrite that should be performed for this
/// candidate.
OverloadCandidateRewriteKind
getRewriteKind(const FunctionDecl *FD, OverloadCandidateParamOrder PO) {
OverloadCandidateRewriteKind CRK = CRK_None;
if (isRewrittenOperator(FD))
CRK = OverloadCandidateRewriteKind(CRK | CRK_DifferentOperator);
if (PO == OverloadCandidateParamOrder::Reversed)
CRK = OverloadCandidateRewriteKind(CRK | CRK_Reversed);
return CRK;
}
/// Determine whether we should consider looking for and adding reversed
/// candidates for operator Op.
bool shouldAddReversed(OverloadedOperatorKind Op);
/// Determine whether we should add a rewritten candidate for \p FD with
/// reversed parameter order.
bool shouldAddReversed(ASTContext &Ctx, const FunctionDecl *FD);
};
private:
SmallVector<OverloadCandidate, 16> Candidates;
llvm::SmallPtrSet<Decl *, 16> Functions;
llvm::SmallPtrSet<uintptr_t, 16> Functions;
// Allocator for ConversionSequenceLists. We store the first few of these
// inline to avoid allocation for small sets.
@ -877,6 +951,7 @@ class Sema;
SourceLocation Loc;
CandidateSetKind Kind;
OperatorRewriteInfo RewriteInfo;
constexpr static unsigned NumInlineBytes =
24 * sizeof(ImplicitConversionSequence);
@ -915,19 +990,24 @@ class Sema;
void destroyCandidates();
public:
OverloadCandidateSet(SourceLocation Loc, CandidateSetKind CSK)
: Loc(Loc), Kind(CSK) {}
OverloadCandidateSet(SourceLocation Loc, CandidateSetKind CSK,
OperatorRewriteInfo RewriteInfo = {})
: Loc(Loc), Kind(CSK), RewriteInfo(RewriteInfo) {}
OverloadCandidateSet(const OverloadCandidateSet &) = delete;
OverloadCandidateSet &operator=(const OverloadCandidateSet &) = delete;
~OverloadCandidateSet() { destroyCandidates(); }
SourceLocation getLocation() const { return Loc; }
CandidateSetKind getKind() const { return Kind; }
OperatorRewriteInfo getRewriteInfo() const { return RewriteInfo; }
/// Determine when this overload candidate will be new to the
/// overload set.
bool isNewCandidate(Decl *F) {
return Functions.insert(F->getCanonicalDecl()).second;
bool isNewCandidate(Decl *F, OverloadCandidateParamOrder PO =
OverloadCandidateParamOrder::Normal) {
uintptr_t Key = reinterpret_cast<uintptr_t>(F->getCanonicalDecl());
Key |= static_cast<uintptr_t>(PO);
return Functions.insert(Key).second;
}
/// Clear out all of the candidates.

View File

@ -159,6 +159,8 @@ namespace clang {
class OMPClause;
struct OMPVarListLocTy;
struct OverloadCandidate;
enum class OverloadCandidateParamOrder : char;
enum OverloadCandidateRewriteKind : unsigned;
class OverloadCandidateSet;
class OverloadExpr;
class ParenListExpr;
@ -3019,7 +3021,8 @@ public:
bool AllowExplicit = true,
bool AllowExplicitConversion = false,
ADLCallKind IsADLCandidate = ADLCallKind::NotADL,
ConversionSequenceList EarlyConversions = None);
ConversionSequenceList EarlyConversions = None,
OverloadCandidateParamOrder PO = {});
void AddFunctionCandidates(const UnresolvedSetImpl &Functions,
ArrayRef<Expr *> Args,
OverloadCandidateSet &CandidateSet,
@ -3032,7 +3035,8 @@ public:
Expr::Classification ObjectClassification,
ArrayRef<Expr *> Args,
OverloadCandidateSet& CandidateSet,
bool SuppressUserConversion = false);
bool SuppressUserConversion = false,
OverloadCandidateParamOrder PO = {});
void AddMethodCandidate(CXXMethodDecl *Method,
DeclAccessPair FoundDecl,
CXXRecordDecl *ActingContext, QualType ObjectType,
@ -3041,7 +3045,8 @@ public:
OverloadCandidateSet& CandidateSet,
bool SuppressUserConversions = false,
bool PartialOverloading = false,
ConversionSequenceList EarlyConversions = None);
ConversionSequenceList EarlyConversions = None,
OverloadCandidateParamOrder PO = {});
void AddMethodTemplateCandidate(FunctionTemplateDecl *MethodTmpl,
DeclAccessPair FoundDecl,
CXXRecordDecl *ActingContext,
@ -3051,23 +3056,22 @@ public:
ArrayRef<Expr *> Args,
OverloadCandidateSet& CandidateSet,
bool SuppressUserConversions = false,
bool PartialOverloading = false);
bool PartialOverloading = false,
OverloadCandidateParamOrder PO = {});
void AddTemplateOverloadCandidate(
FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *> Args,
OverloadCandidateSet &CandidateSet, bool SuppressUserConversions = false,
bool PartialOverloading = false, bool AllowExplicit = true,
ADLCallKind IsADLCandidate = ADLCallKind::NotADL);
bool CheckNonDependentConversions(FunctionTemplateDecl *FunctionTemplate,
ArrayRef<QualType> ParamTypes,
ArrayRef<Expr *> Args,
OverloadCandidateSet &CandidateSet,
ConversionSequenceList &Conversions,
bool SuppressUserConversions,
CXXRecordDecl *ActingContext = nullptr,
QualType ObjectType = QualType(),
Expr::Classification
ObjectClassification = {});
ADLCallKind IsADLCandidate = ADLCallKind::NotADL,
OverloadCandidateParamOrder PO = {});
bool CheckNonDependentConversions(
FunctionTemplateDecl *FunctionTemplate, ArrayRef<QualType> ParamTypes,
ArrayRef<Expr *> Args, OverloadCandidateSet &CandidateSet,
ConversionSequenceList &Conversions, bool SuppressUserConversions,
CXXRecordDecl *ActingContext = nullptr, QualType ObjectType = QualType(),
Expr::Classification ObjectClassification = {},
OverloadCandidateParamOrder PO = {});
void AddConversionCandidate(
CXXConversionDecl *Conversion, DeclAccessPair FoundDecl,
CXXRecordDecl *ActingContext, Expr *From, QualType ToType,
@ -3084,10 +3088,14 @@ public:
const FunctionProtoType *Proto,
Expr *Object, ArrayRef<Expr *> Args,
OverloadCandidateSet& CandidateSet);
void AddNonMemberOperatorCandidates(
const UnresolvedSetImpl &Functions, ArrayRef<Expr *> Args,
OverloadCandidateSet &CandidateSet,
TemplateArgumentListInfo *ExplicitTemplateArgs = nullptr);
void AddMemberOperatorCandidates(OverloadedOperatorKind Op,
SourceLocation OpLoc, ArrayRef<Expr *> Args,
OverloadCandidateSet& CandidateSet,
SourceRange OpRange = SourceRange());
OverloadCandidateSet &CandidateSet,
OverloadCandidateParamOrder PO = {});
void AddBuiltinCandidate(QualType *ParamTys, ArrayRef<Expr *> Args,
OverloadCandidateSet& CandidateSet,
bool IsAssignmentOperator = false,
@ -3103,9 +3111,10 @@ public:
bool PartialOverloading = false);
// Emit as a 'note' the specific overload candidate
void NoteOverloadCandidate(NamedDecl *Found, FunctionDecl *Fn,
QualType DestType = QualType(),
bool TakingAddress = false);
void NoteOverloadCandidate(
NamedDecl *Found, FunctionDecl *Fn,
OverloadCandidateRewriteKind RewriteKind = OverloadCandidateRewriteKind(),
QualType DestType = QualType(), bool TakingAddress = false);
// Emit as a series of 'note's all template and non-templates identified by
// the expression Expr
@ -3237,7 +3246,8 @@ public:
BinaryOperatorKind Opc,
const UnresolvedSetImpl &Fns,
Expr *LHS, Expr *RHS,
bool RequiresADL = true);
bool RequiresADL = true,
bool AllowRewrittenCandidates = true);
ExprResult CreateOverloadedArraySubscriptExpr(SourceLocation LLoc,
SourceLocation RLoc,
@ -7665,6 +7675,9 @@ public:
// We are substituting template arguments into a constraint expression.
ConstraintSubstitution,
/// We are rewriting a comparison operator in terms of an operator<=>.
RewritingOperatorAsSpaceship,
/// Added for Template instantiation observation.
/// Memoization means we are _not_ instantiating a template because
/// it is already instantiated (but we entered a context where we

View File

@ -79,7 +79,7 @@ CXXRewrittenBinaryOperator::getDecomposedForm() const {
Result.RHS = BO->getRHS();
Result.InnerBinOp = BO;
} else if (auto *BO = dyn_cast<CXXOperatorCallExpr>(E)) {
assert(!SkippedNot || BO->getOperator() == OO_Equal);
assert(!SkippedNot || BO->getOperator() == OO_EqualEqual);
assert(BO->isInfixBinaryOp());
switch (BO->getOperator()) {
case OO_Less: Result.Opcode = BO_LT; break;
@ -107,7 +107,7 @@ CXXRewrittenBinaryOperator::getDecomposedForm() const {
return Result;
// Otherwise, we expect a <=> to now be on the LHS.
E = Result.InnerBinOp->IgnoreImplicit();
E = Result.LHS->IgnoreImplicit();
if (auto *BO = dyn_cast<BinaryOperator>(E)) {
assert(BO->getOpcode() == BO_Cmp);
Result.LHS = BO->getLHS();

View File

@ -415,6 +415,8 @@ private:
return "DeclaringSpecialMember";
case CodeSynthesisContext::DefiningSynthesizedFunction:
return "DefiningSynthesizedFunction";
case CodeSynthesisContext::RewritingOperatorAsSpaceship:
return "RewritingOperatorAsSpaceship";
case CodeSynthesisContext::Memoization:
return "Memoization";
case CodeSynthesisContext::ConstraintsCheck:

View File

@ -13310,6 +13310,13 @@ static ExprResult BuildOverloadedBinOp(Sema &S, Scope *Sc, SourceLocation OpLoc,
S.LookupOverloadedOperatorName(OverOp, Sc, LHS->getType(),
RHS->getType(), Functions);
// In C++20 onwards, we may have a second operator to look up.
if (S.getLangOpts().CPlusPlus2a) {
if (OverloadedOperatorKind ExtraOp = getRewrittenOverloadedOperator(OverOp))
S.LookupOverloadedOperatorName(ExtraOp, Sc, LHS->getType(),
RHS->getType(), Functions);
}
// Build the (potentially-overloaded, potentially-dependent)
// binary operation.
return S.CreateOverloadedBinOp(OpLoc, Opc, Functions, LHS, RHS);

View File

@ -848,6 +848,25 @@ llvm::Optional<unsigned> DeductionFailureInfo::getCallArgIndex() {
}
}
bool OverloadCandidateSet::OperatorRewriteInfo::shouldAddReversed(
OverloadedOperatorKind Op) {
if (!AllowRewrittenCandidates)
return false;
return Op == OO_EqualEqual || Op == OO_Spaceship;
}
bool OverloadCandidateSet::OperatorRewriteInfo::shouldAddReversed(
ASTContext &Ctx, const FunctionDecl *FD) {
if (!shouldAddReversed(FD->getDeclName().getCXXOverloadedOperator()))
return false;
// Don't bother adding a reversed candidate that can never be a better
// match than the non-reversed version.
return FD->getNumParams() != 2 ||
!Ctx.hasSameUnqualifiedType(FD->getParamDecl(0)->getType(),
FD->getParamDecl(1)->getType()) ||
FD->hasAttr<EnableIfAttr>();
}
void OverloadCandidateSet::destroyCandidates() {
for (iterator i = begin(), e = end(); i != e; ++i) {
for (auto &C : i->Conversions)
@ -6056,7 +6075,8 @@ void Sema::AddOverloadCandidate(
FunctionDecl *Function, DeclAccessPair FoundDecl, ArrayRef<Expr *> Args,
OverloadCandidateSet &CandidateSet, bool SuppressUserConversions,
bool PartialOverloading, bool AllowExplicit, bool AllowExplicitConversions,
ADLCallKind IsADLCandidate, ConversionSequenceList EarlyConversions) {
ADLCallKind IsADLCandidate, ConversionSequenceList EarlyConversions,
OverloadCandidateParamOrder PO) {
const FunctionProtoType *Proto
= dyn_cast<FunctionProtoType>(Function->getType()->getAs<FunctionType>());
assert(Proto && "Functions without a prototype cannot be overloaded");
@ -6075,25 +6095,14 @@ void Sema::AddOverloadCandidate(
AddMethodCandidate(Method, FoundDecl, Method->getParent(), QualType(),
Expr::Classification::makeSimpleLValue(), Args,
CandidateSet, SuppressUserConversions,
PartialOverloading, EarlyConversions);
PartialOverloading, EarlyConversions, PO);
return;
}
// We treat a constructor like a non-member function, since its object
// argument doesn't participate in overload resolution.
}
if (!CandidateSet.isNewCandidate(Function))
return;
// C++ [over.match.oper]p3:
// if no operand has a class type, only those non-member functions in the
// lookup set that have a first parameter of type T1 or "reference to
// (possibly cv-qualified) T1", when T1 is an enumeration type, or (if there
// is a right operand) a second parameter of type T2 or "reference to
// (possibly cv-qualified) T2", when T2 is an enumeration type, are
// candidate functions.
if (CandidateSet.getKind() == OverloadCandidateSet::CSK_Operator &&
!IsAcceptableNonMemberOperatorCandidate(Context, Function, Args))
if (!CandidateSet.isNewCandidate(Function, PO))
return;
// C++11 [class.copy]p11: [DR1402]
@ -6108,12 +6117,25 @@ void Sema::AddOverloadCandidate(
EnterExpressionEvaluationContext Unevaluated(
*this, Sema::ExpressionEvaluationContext::Unevaluated);
// C++ [over.match.oper]p3:
// if no operand has a class type, only those non-member functions in the
// lookup set that have a first parameter of type T1 or "reference to
// (possibly cv-qualified) T1", when T1 is an enumeration type, or (if there
// is a right operand) a second parameter of type T2 or "reference to
// (possibly cv-qualified) T2", when T2 is an enumeration type, are
// candidate functions.
if (CandidateSet.getKind() == OverloadCandidateSet::CSK_Operator &&
!IsAcceptableNonMemberOperatorCandidate(Context, Function, Args))
return;
// Add this candidate
OverloadCandidate &Candidate =
CandidateSet.addCandidate(Args.size(), EarlyConversions);
Candidate.FoundDecl = FoundDecl;
Candidate.Function = Function;
Candidate.Viable = true;
Candidate.RewriteKind =
CandidateSet.getRewriteInfo().getRewriteKind(Function, PO);
Candidate.IsSurrogate = false;
Candidate.IsADLCandidate = IsADLCandidate;
Candidate.IgnoreObjectArgument = false;
@ -6213,7 +6235,9 @@ void Sema::AddOverloadCandidate(
// Determine the implicit conversion sequences for each of the
// arguments.
for (unsigned ArgIdx = 0; ArgIdx < Args.size(); ++ArgIdx) {
if (Candidate.Conversions[ArgIdx].isInitialized()) {
unsigned ConvIdx =
PO == OverloadCandidateParamOrder::Reversed ? 1 - ArgIdx : ArgIdx;
if (Candidate.Conversions[ConvIdx].isInitialized()) {
// We already formed a conversion sequence for this parameter during
// template argument deduction.
} else if (ArgIdx < NumParams) {
@ -6222,12 +6246,12 @@ void Sema::AddOverloadCandidate(
// (13.3.3.1) that converts that argument to the corresponding
// parameter of F.
QualType ParamType = Proto->getParamType(ArgIdx);
Candidate.Conversions[ArgIdx] = TryCopyInitialization(
Candidate.Conversions[ConvIdx] = TryCopyInitialization(
*this, Args[ArgIdx], ParamType, SuppressUserConversions,
/*InOverloadResolution=*/true,
/*AllowObjCWritebackConversion=*/
getLangOpts().ObjCAutoRefCount, AllowExplicitConversions);
if (Candidate.Conversions[ArgIdx].isBad()) {
if (Candidate.Conversions[ConvIdx].isBad()) {
Candidate.Viable = false;
Candidate.FailureKind = ovl_fail_bad_conversion;
return;
@ -6236,7 +6260,7 @@ void Sema::AddOverloadCandidate(
// (C++ 13.3.2p2): For the purposes of overload resolution, any
// argument for which there is no corresponding parameter is
// considered to ""match the ellipsis" (C+ 13.3.3.1.3).
Candidate.Conversions[ArgIdx].setEllipsis();
Candidate.Conversions[ConvIdx].setEllipsis();
}
}
@ -6583,9 +6607,10 @@ void Sema::AddFunctionCandidates(const UnresolvedSetImpl &Fns,
FunctionArgs = Args.slice(1);
}
if (FunTmpl) {
AddTemplateOverloadCandidate(
FunTmpl, F.getPair(), ExplicitTemplateArgs, FunctionArgs,
CandidateSet, SuppressUserConversions, PartialOverloading);
AddTemplateOverloadCandidate(FunTmpl, F.getPair(),
ExplicitTemplateArgs, FunctionArgs,
CandidateSet, SuppressUserConversions,
PartialOverloading);
} else {
AddOverloadCandidate(FD, F.getPair(), FunctionArgs, CandidateSet,
SuppressUserConversions, PartialOverloading);
@ -6596,12 +6621,12 @@ void Sema::AddFunctionCandidates(const UnresolvedSetImpl &Fns,
/// AddMethodCandidate - Adds a named decl (which is some kind of
/// method) as a method candidate to the given overload set.
void Sema::AddMethodCandidate(DeclAccessPair FoundDecl,
QualType ObjectType,
void Sema::AddMethodCandidate(DeclAccessPair FoundDecl, QualType ObjectType,
Expr::Classification ObjectClassification,
ArrayRef<Expr *> Args,
OverloadCandidateSet& CandidateSet,
bool SuppressUserConversions) {
OverloadCandidateSet &CandidateSet,
bool SuppressUserConversions,
OverloadCandidateParamOrder PO) {
NamedDecl *Decl = FoundDecl.getDecl();
CXXRecordDecl *ActingContext = cast<CXXRecordDecl>(Decl->getDeclContext());
@ -6614,11 +6639,11 @@ void Sema::AddMethodCandidate(DeclAccessPair FoundDecl,
AddMethodTemplateCandidate(TD, FoundDecl, ActingContext,
/*ExplicitArgs*/ nullptr, ObjectType,
ObjectClassification, Args, CandidateSet,
SuppressUserConversions);
SuppressUserConversions, false, PO);
} else {
AddMethodCandidate(cast<CXXMethodDecl>(Decl), FoundDecl, ActingContext,
ObjectType, ObjectClassification, Args, CandidateSet,
SuppressUserConversions);
SuppressUserConversions, false, None, PO);
}
}
@ -6637,14 +6662,15 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
OverloadCandidateSet &CandidateSet,
bool SuppressUserConversions,
bool PartialOverloading,
ConversionSequenceList EarlyConversions) {
ConversionSequenceList EarlyConversions,
OverloadCandidateParamOrder PO) {
const FunctionProtoType *Proto
= dyn_cast<FunctionProtoType>(Method->getType()->getAs<FunctionType>());
assert(Proto && "Methods without a prototype cannot be overloaded");
assert(!isa<CXXConstructorDecl>(Method) &&
"Use AddOverloadCandidate for constructors");
if (!CandidateSet.isNewCandidate(Method))
if (!CandidateSet.isNewCandidate(Method, PO))
return;
// C++11 [class.copy]p23: [DR1402]
@ -6663,6 +6689,8 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
CandidateSet.addCandidate(Args.size() + 1, EarlyConversions);
Candidate.FoundDecl = FoundDecl;
Candidate.Function = Method;
Candidate.RewriteKind =
CandidateSet.getRewriteInfo().getRewriteKind(Method, PO);
Candidate.IsSurrogate = false;
Candidate.IgnoreObjectArgument = false;
Candidate.ExplicitCallArguments = Args.size();
@ -6698,12 +6726,13 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
// The implicit object argument is ignored.
Candidate.IgnoreObjectArgument = true;
else {
unsigned ConvIdx = PO == OverloadCandidateParamOrder::Reversed ? 1 : 0;
// Determine the implicit conversion sequence for the object
// parameter.
Candidate.Conversions[0] = TryObjectArgumentInitialization(
Candidate.Conversions[ConvIdx] = TryObjectArgumentInitialization(
*this, CandidateSet.getLocation(), ObjectType, ObjectClassification,
Method, ActingContext);
if (Candidate.Conversions[0].isBad()) {
if (Candidate.Conversions[ConvIdx].isBad()) {
Candidate.Viable = false;
Candidate.FailureKind = ovl_fail_bad_conversion;
return;
@ -6722,7 +6751,9 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
// Determine the implicit conversion sequences for each of the
// arguments.
for (unsigned ArgIdx = 0; ArgIdx < Args.size(); ++ArgIdx) {
if (Candidate.Conversions[ArgIdx + 1].isInitialized()) {
unsigned ConvIdx =
PO == OverloadCandidateParamOrder::Reversed ? 0 : (ArgIdx + 1);
if (Candidate.Conversions[ConvIdx].isInitialized()) {
// We already formed a conversion sequence for this parameter during
// template argument deduction.
} else if (ArgIdx < NumParams) {
@ -6731,13 +6762,13 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
// (13.3.3.1) that converts that argument to the corresponding
// parameter of F.
QualType ParamType = Proto->getParamType(ArgIdx);
Candidate.Conversions[ArgIdx + 1]
Candidate.Conversions[ConvIdx]
= TryCopyInitialization(*this, Args[ArgIdx], ParamType,
SuppressUserConversions,
/*InOverloadResolution=*/true,
/*AllowObjCWritebackConversion=*/
getLangOpts().ObjCAutoRefCount);
if (Candidate.Conversions[ArgIdx + 1].isBad()) {
if (Candidate.Conversions[ConvIdx].isBad()) {
Candidate.Viable = false;
Candidate.FailureKind = ovl_fail_bad_conversion;
return;
@ -6746,7 +6777,7 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
// (C++ 13.3.2p2): For the purposes of overload resolution, any
// argument for which there is no corresponding parameter is
// considered to "match the ellipsis" (C+ 13.3.3.1.3).
Candidate.Conversions[ArgIdx + 1].setEllipsis();
Candidate.Conversions[ConvIdx].setEllipsis();
}
}
@ -6767,18 +6798,14 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
/// Add a C++ member function template as a candidate to the candidate
/// set, using template argument deduction to produce an appropriate member
/// function template specialization.
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) {
if (!CandidateSet.isNewCandidate(MethodTmpl))
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, OverloadCandidateParamOrder PO) {
if (!CandidateSet.isNewCandidate(MethodTmpl, PO))
return;
// C++ [over.match.funcs]p7:
@ -6799,13 +6826,15 @@ Sema::AddMethodTemplateCandidate(FunctionTemplateDecl *MethodTmpl,
return CheckNonDependentConversions(
MethodTmpl, ParamTypes, Args, CandidateSet, Conversions,
SuppressUserConversions, ActingContext, ObjectType,
ObjectClassification);
ObjectClassification, PO);
})) {
OverloadCandidate &Candidate =
CandidateSet.addCandidate(Conversions.size(), Conversions);
Candidate.FoundDecl = FoundDecl;
Candidate.Function = MethodTmpl->getTemplatedDecl();
Candidate.Viable = false;
Candidate.RewriteKind =
CandidateSet.getRewriteInfo().getRewriteKind(Candidate.Function, PO);
Candidate.IsSurrogate = false;
Candidate.IgnoreObjectArgument =
cast<CXXMethodDecl>(Candidate.Function)->isStatic() ||
@ -6829,7 +6858,7 @@ Sema::AddMethodTemplateCandidate(FunctionTemplateDecl *MethodTmpl,
AddMethodCandidate(cast<CXXMethodDecl>(Specialization), FoundDecl,
ActingContext, ObjectType, ObjectClassification, Args,
CandidateSet, SuppressUserConversions, PartialOverloading,
Conversions);
Conversions, PO);
}
/// Add a C++ function template specialization as a candidate
@ -6839,8 +6868,9 @@ void Sema::AddTemplateOverloadCandidate(
FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *> Args,
OverloadCandidateSet &CandidateSet, bool SuppressUserConversions,
bool PartialOverloading, bool AllowExplicit, ADLCallKind IsADLCandidate) {
if (!CandidateSet.isNewCandidate(FunctionTemplate))
bool PartialOverloading, bool AllowExplicit, ADLCallKind IsADLCandidate,
OverloadCandidateParamOrder PO) {
if (!CandidateSet.isNewCandidate(FunctionTemplate, PO))
return;
// C++ [over.match.funcs]p7:
@ -6858,15 +6888,17 @@ void Sema::AddTemplateOverloadCandidate(
if (TemplateDeductionResult Result = DeduceTemplateArguments(
FunctionTemplate, ExplicitTemplateArgs, Args, Specialization, Info,
PartialOverloading, [&](ArrayRef<QualType> ParamTypes) {
return CheckNonDependentConversions(FunctionTemplate, ParamTypes,
Args, CandidateSet, Conversions,
SuppressUserConversions);
return CheckNonDependentConversions(
FunctionTemplate, ParamTypes, Args, CandidateSet, Conversions,
SuppressUserConversions, nullptr, QualType(), {}, PO);
})) {
OverloadCandidate &Candidate =
CandidateSet.addCandidate(Conversions.size(), Conversions);
Candidate.FoundDecl = FoundDecl;
Candidate.Function = FunctionTemplate->getTemplatedDecl();
Candidate.Viable = false;
Candidate.RewriteKind =
CandidateSet.getRewriteInfo().getRewriteKind(Candidate.Function, PO);
Candidate.IsSurrogate = false;
Candidate.IsADLCandidate = IsADLCandidate;
// Ignore the object argument if there is one, since we don't have an object
@ -6891,7 +6923,7 @@ void Sema::AddTemplateOverloadCandidate(
AddOverloadCandidate(
Specialization, FoundDecl, Args, CandidateSet, SuppressUserConversions,
PartialOverloading, AllowExplicit,
/*AllowExplicitConversions*/ false, IsADLCandidate, Conversions);
/*AllowExplicitConversions*/ false, IsADLCandidate, Conversions, PO);
}
/// Check that implicit conversion sequences can be formed for each argument
@ -6902,7 +6934,7 @@ bool Sema::CheckNonDependentConversions(
ArrayRef<Expr *> Args, OverloadCandidateSet &CandidateSet,
ConversionSequenceList &Conversions, bool SuppressUserConversions,
CXXRecordDecl *ActingContext, QualType ObjectType,
Expr::Classification ObjectClassification) {
Expr::Classification ObjectClassification, OverloadCandidateParamOrder PO) {
// FIXME: The cases in which we allow explicit conversions for constructor
// arguments never consider calling a constructor template. It's not clear
// that is correct.
@ -6925,10 +6957,11 @@ bool Sema::CheckNonDependentConversions(
// overload resolution is permitted to sidestep instantiations.
if (HasThisConversion && !cast<CXXMethodDecl>(FD)->isStatic() &&
!ObjectType.isNull()) {
Conversions[0] = TryObjectArgumentInitialization(
unsigned ConvIdx = PO == OverloadCandidateParamOrder::Reversed ? 1 : 0;
Conversions[ConvIdx] = TryObjectArgumentInitialization(
*this, CandidateSet.getLocation(), ObjectType, ObjectClassification,
Method, ActingContext);
if (Conversions[0].isBad())
if (Conversions[ConvIdx].isBad())
return true;
}
@ -6936,14 +6969,17 @@ bool Sema::CheckNonDependentConversions(
++I) {
QualType ParamType = ParamTypes[I];
if (!ParamType->isDependentType()) {
Conversions[ThisConversions + I]
unsigned ConvIdx = PO == OverloadCandidateParamOrder::Reversed
? 0
: (ThisConversions + I);
Conversions[ConvIdx]
= TryCopyInitialization(*this, Args[I], ParamType,
SuppressUserConversions,
/*InOverloadResolution=*/true,
/*AllowObjCWritebackConversion=*/
getLangOpts().ObjCAutoRefCount,
AllowExplicit);
if (Conversions[ThisConversions + I].isBad())
if (Conversions[ConvIdx].isBad())
return true;
}
}
@ -7331,6 +7367,48 @@ void Sema::AddSurrogateCandidate(CXXConversionDecl *Conversion,
}
}
/// Add all of the non-member operator function declarations in the given
/// function set to the overload candidate set.
void Sema::AddNonMemberOperatorCandidates(
const UnresolvedSetImpl &Fns, ArrayRef<Expr *> Args,
OverloadCandidateSet &CandidateSet,
TemplateArgumentListInfo *ExplicitTemplateArgs) {
for (UnresolvedSetIterator F = Fns.begin(), E = Fns.end(); F != E; ++F) {
NamedDecl *D = F.getDecl()->getUnderlyingDecl();
ArrayRef<Expr *> FunctionArgs = Args;
FunctionTemplateDecl *FunTmpl = dyn_cast<FunctionTemplateDecl>(D);
FunctionDecl *FD =
FunTmpl ? FunTmpl->getTemplatedDecl() : cast<FunctionDecl>(D);
// Don't consider rewritten functions if we're not rewriting.
if (!CandidateSet.getRewriteInfo().isAcceptableCandidate(FD))
continue;
assert(!isa<CXXMethodDecl>(FD) &&
"unqualified operator lookup found a member function");
if (FunTmpl) {
AddTemplateOverloadCandidate(FunTmpl, F.getPair(), ExplicitTemplateArgs,
FunctionArgs, CandidateSet);
if (CandidateSet.getRewriteInfo().shouldAddReversed(Context, FD))
AddTemplateOverloadCandidate(
FunTmpl, F.getPair(), ExplicitTemplateArgs,
{FunctionArgs[1], FunctionArgs[0]}, CandidateSet, false, false,
true, ADLCallKind::NotADL, OverloadCandidateParamOrder::Reversed);
} else {
if (ExplicitTemplateArgs)
continue;
AddOverloadCandidate(FD, F.getPair(), FunctionArgs, CandidateSet);
if (CandidateSet.getRewriteInfo().shouldAddReversed(Context, FD))
AddOverloadCandidate(FD, F.getPair(),
{FunctionArgs[1], FunctionArgs[0]}, CandidateSet,
false, false, true, false, ADLCallKind::NotADL,
None, OverloadCandidateParamOrder::Reversed);
}
}
}
/// Add overload candidates for overloaded operators that are
/// member functions.
///
@ -7342,8 +7420,8 @@ void Sema::AddSurrogateCandidate(CXXConversionDecl *Conversion,
void Sema::AddMemberOperatorCandidates(OverloadedOperatorKind Op,
SourceLocation OpLoc,
ArrayRef<Expr *> Args,
OverloadCandidateSet& CandidateSet,
SourceRange OpRange) {
OverloadCandidateSet &CandidateSet,
OverloadCandidateParamOrder PO) {
DeclarationName OpName = Context.DeclarationNames.getCXXOperatorName(Op);
// C++ [over.match.oper]p3:
@ -7378,7 +7456,7 @@ void Sema::AddMemberOperatorCandidates(OverloadedOperatorKind Op,
++Oper)
AddMethodCandidate(Oper.getPair(), Args[0]->getType(),
Args[0]->Classify(Context), Args.slice(1),
CandidateSet, /*SuppressUserConversion=*/false);
CandidateSet, /*SuppressUserConversion=*/false, PO);
}
}
@ -8183,10 +8261,16 @@ public:
if (C->Function->isFunctionTemplateSpecialization())
continue;
QualType FirstParamType =
C->Function->getParamDecl(0)->getType().getUnqualifiedType();
QualType SecondParamType =
C->Function->getParamDecl(1)->getType().getUnqualifiedType();
// We interpret "same parameter-type-list" as applying to the
// "synthesized candidate, with the order of the two parameters
// reversed", not to the original function.
bool Reversed = C->RewriteKind & CRK_Reversed;
QualType FirstParamType = C->Function->getParamDecl(Reversed ? 1 : 0)
->getType()
.getUnqualifiedType();
QualType SecondParamType = C->Function->getParamDecl(Reversed ? 0 : 1)
->getType()
.getUnqualifiedType();
// Skip if either parameter isn't of enumeral type.
if (!FirstParamType->isEnumeralType() ||
@ -9240,6 +9324,7 @@ bool clang::isBetterOverloadCandidate(
// A viable function F1 is defined to be a better function than another
// viable function F2 if for all arguments i, ICSi(F1) is not a worse
// conversion sequence than ICSi(F2), and then...
bool HasWorseConversion = false;
for (unsigned ArgIdx = StartArg; ArgIdx < NumArgs; ++ArgIdx) {
switch (CompareImplicitConversionSequences(S, Loc,
Cand1.Conversions[ArgIdx],
@ -9250,6 +9335,24 @@ bool clang::isBetterOverloadCandidate(
break;
case ImplicitConversionSequence::Worse:
if (Cand1.Function && Cand1.Function == Cand2.Function &&
(Cand2.RewriteKind & CRK_Reversed) != 0) {
// Work around large-scale breakage caused by considering reversed
// forms of operator== in C++20:
//
// When comparing a function against its reversed form, if we have a
// better conversion for one argument and a worse conversion for the
// other, we prefer the non-reversed form.
//
// This prevents a conversion function from being considered ambiguous
// with its own reversed form in various where it's only incidentally
// heterogeneous.
//
// We diagnose this as an extension from CreateOverloadedBinOp.
HasWorseConversion = true;
break;
}
// Cand1 can't be better than Cand2.
return false;
@ -9263,6 +9366,8 @@ bool clang::isBetterOverloadCandidate(
// ICSj(F2), or, if not that,
if (HasBetterConversion)
return true;
if (HasWorseConversion)
return false;
// -- the context is an initialization by user-defined conversion
// (see 8.5, 13.3.1.5) and the standard conversion sequence
@ -9330,8 +9435,10 @@ bool clang::isBetterOverloadCandidate(
return BetterTemplate == Cand1.Function->getPrimaryTemplate();
}
// FIXME: Work around a defect in the C++17 inheriting constructor wording.
// A derived-class constructor beats an (inherited) base class constructor.
// -- F1 is a constructor for a class D, F2 is a constructor for a base
// class B of D, and for all arguments the corresponding parameters of
// F1 and F2 have the same type.
// FIXME: Implement the "all parameters have the same type" check.
bool Cand1IsInherited =
dyn_cast_or_null<ConstructorUsingShadowDecl>(Cand1.FoundDecl.getDecl());
bool Cand2IsInherited =
@ -9349,6 +9456,16 @@ bool clang::isBetterOverloadCandidate(
// Inherited from sibling base classes: still ambiguous.
}
// -- F2 is a rewritten candidate (12.4.1.2) and F1 is not
// -- F1 and F2 are rewritten candidates, and F2 is a synthesized candidate
// with reversed order of parameters and F1 is not
//
// We rank reversed + different operator as worse than just reversed, but
// that comparison can never happen, because we only consider reversing for
// the maximally-rewritten operator (== or <=>).
if (Cand1.RewriteKind != Cand2.RewriteKind)
return Cand1.RewriteKind < Cand2.RewriteKind;
// Check C++17 tie-breakers for deduction guides.
{
auto *Guide1 = dyn_cast_or_null<CXXDeductionGuideDecl>(Cand1.Function);
@ -9544,6 +9661,7 @@ namespace {
enum OverloadCandidateKind {
oc_function,
oc_method,
oc_reversed_binary_operator,
oc_constructor,
oc_implicit_default_constructor,
oc_implicit_copy_constructor,
@ -9561,6 +9679,7 @@ enum OverloadCandidateSelect {
static std::pair<OverloadCandidateKind, OverloadCandidateSelect>
ClassifyOverloadCandidate(Sema &S, NamedDecl *Found, FunctionDecl *Fn,
OverloadCandidateRewriteKind CRK,
std::string &Description) {
bool isTemplate = Fn->isTemplateDecl() || Found->isTemplateDecl();
@ -9577,6 +9696,9 @@ ClassifyOverloadCandidate(Sema &S, NamedDecl *Found, FunctionDecl *Fn,
}();
OverloadCandidateKind Kind = [&]() {
if (CRK & CRK_Reversed)
return oc_reversed_binary_operator;
if (CXXConstructorDecl *Ctor = dyn_cast<CXXConstructorDecl>(Fn)) {
if (!Ctor->isImplicit()) {
if (isa<ConstructorUsingShadowDecl>(Found))
@ -9701,6 +9823,7 @@ bool Sema::checkAddressOfFunctionIsAvailable(const FunctionDecl *Function,
// Notes the location of an overload candidate.
void Sema::NoteOverloadCandidate(NamedDecl *Found, FunctionDecl *Fn,
OverloadCandidateRewriteKind RewriteKind,
QualType DestType, bool TakingAddress) {
if (TakingAddress && !checkAddressOfCandidateIsAvailable(*this, Fn))
return;
@ -9710,7 +9833,7 @@ void Sema::NoteOverloadCandidate(NamedDecl *Found, FunctionDecl *Fn,
std::string FnDesc;
std::pair<OverloadCandidateKind, OverloadCandidateSelect> KSPair =
ClassifyOverloadCandidate(*this, Found, Fn, FnDesc);
ClassifyOverloadCandidate(*this, Found, Fn, RewriteKind, FnDesc);
PartialDiagnostic PD = PDiag(diag::note_ovl_candidate)
<< (unsigned)KSPair.first << (unsigned)KSPair.second
<< Fn << FnDesc;
@ -9734,11 +9857,11 @@ void Sema::NoteAllOverloadCandidates(Expr *OverloadedExpr, QualType DestType,
I != IEnd; ++I) {
if (FunctionTemplateDecl *FunTmpl =
dyn_cast<FunctionTemplateDecl>((*I)->getUnderlyingDecl()) ) {
NoteOverloadCandidate(*I, FunTmpl->getTemplatedDecl(), DestType,
NoteOverloadCandidate(*I, FunTmpl->getTemplatedDecl(), CRK_None, DestType,
TakingAddress);
} else if (FunctionDecl *Fun
= dyn_cast<FunctionDecl>((*I)->getUnderlyingDecl()) ) {
NoteOverloadCandidate(*I, Fun, DestType, TakingAddress);
NoteOverloadCandidate(*I, Fun, CRK_None, DestType, TakingAddress);
}
}
}
@ -9788,7 +9911,8 @@ static void DiagnoseBadConversion(Sema &S, OverloadCandidate *Cand,
std::string FnDesc;
std::pair<OverloadCandidateKind, OverloadCandidateSelect> FnKindPair =
ClassifyOverloadCandidate(S, Cand->FoundDecl, Fn, FnDesc);
ClassifyOverloadCandidate(S, Cand->FoundDecl, Fn, Cand->RewriteKind,
FnDesc);
Expr *FromExpr = Conv.Bad.FromExpr;
QualType FromTy = Conv.Bad.getFromType();
@ -10060,7 +10184,7 @@ static void DiagnoseArityMismatch(Sema &S, NamedDecl *Found, Decl *D,
std::string Description;
std::pair<OverloadCandidateKind, OverloadCandidateSelect> FnKindPair =
ClassifyOverloadCandidate(S, Found, Fn, Description);
ClassifyOverloadCandidate(S, Found, Fn, CRK_None, Description);
if (modeCount == 1 && Fn->getParamDecl(0)->getDeclName())
S.Diag(Fn->getLocation(), diag::note_ovl_candidate_arity_one)
@ -10357,7 +10481,8 @@ static void DiagnoseBadTarget(Sema &S, OverloadCandidate *Cand) {
std::string FnDesc;
std::pair<OverloadCandidateKind, OverloadCandidateSelect> FnKindPair =
ClassifyOverloadCandidate(S, Cand->FoundDecl, Callee, FnDesc);
ClassifyOverloadCandidate(S, Cand->FoundDecl, Callee, Cand->RewriteKind,
FnDesc);
S.Diag(Callee->getLocation(), diag::note_ovl_candidate_bad_target)
<< (unsigned)FnKindPair.first << (unsigned)ocs_non_template
@ -10475,7 +10600,8 @@ static void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand,
if (Fn->isDeleted()) {
std::string FnDesc;
std::pair<OverloadCandidateKind, OverloadCandidateSelect> FnKindPair =
ClassifyOverloadCandidate(S, Cand->FoundDecl, Fn, FnDesc);
ClassifyOverloadCandidate(S, Cand->FoundDecl, Fn, Cand->RewriteKind,
FnDesc);
S.Diag(Fn->getLocation(), diag::note_ovl_candidate_deleted)
<< (unsigned)FnKindPair.first << (unsigned)FnKindPair.second << FnDesc
@ -10485,7 +10611,7 @@ static void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand,
}
// We don't really have anything else to say about viable candidates.
S.NoteOverloadCandidate(Cand->FoundDecl, Fn);
S.NoteOverloadCandidate(Cand->FoundDecl, Fn, Cand->RewriteKind);
return;
}
@ -10518,7 +10644,7 @@ static void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand,
case ovl_fail_trivial_conversion:
case ovl_fail_bad_final_conversion:
case ovl_fail_final_conversion_not_exact:
return S.NoteOverloadCandidate(Cand->FoundDecl, Fn);
return S.NoteOverloadCandidate(Cand->FoundDecl, Fn, Cand->RewriteKind);
case ovl_fail_bad_conversion: {
unsigned I = (Cand->IgnoreObjectArgument ? 1 : 0);
@ -10529,7 +10655,7 @@ static void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand,
// FIXME: this currently happens when we're called from SemaInit
// when user-conversion overload fails. Figure out how to handle
// those conditions and diagnose them well.
return S.NoteOverloadCandidate(Cand->FoundDecl, Fn);
return S.NoteOverloadCandidate(Cand->FoundDecl, Fn, Cand->RewriteKind);
}
case ovl_fail_bad_target:
@ -10805,8 +10931,10 @@ struct CompareOverloadCandidatesForDisplay {
/// CompleteNonViableCandidate - Normally, overload resolution only
/// computes up to the first bad conversion. Produces the FixIt set if
/// possible.
static void CompleteNonViableCandidate(Sema &S, OverloadCandidate *Cand,
ArrayRef<Expr *> Args) {
static void
CompleteNonViableCandidate(Sema &S, OverloadCandidate *Cand,
ArrayRef<Expr *> Args,
OverloadCandidateSet::CandidateSetKind CSK) {
assert(!Cand->Viable);
// Don't do anything on failures other than bad conversion.
@ -10834,6 +10962,7 @@ static void CompleteNonViableCandidate(Sema &S, OverloadCandidate *Cand,
bool SuppressUserConversions = false;
unsigned ConvIdx = 0;
unsigned ArgIdx = 0;
ArrayRef<QualType> ParamTypes;
if (Cand->IsSurrogate) {
@ -10842,15 +10971,18 @@ static void CompleteNonViableCandidate(Sema &S, OverloadCandidate *Cand,
if (const PointerType *ConvPtrType = ConvType->getAs<PointerType>())
ConvType = ConvPtrType->getPointeeType();
ParamTypes = ConvType->castAs<FunctionProtoType>()->getParamTypes();
// Conversion 0 is 'this', which doesn't have a corresponding argument.
// Conversion 0 is 'this', which doesn't have a corresponding parameter.
ConvIdx = 1;
} else if (Cand->Function) {
ParamTypes =
Cand->Function->getType()->castAs<FunctionProtoType>()->getParamTypes();
if (isa<CXXMethodDecl>(Cand->Function) &&
!isa<CXXConstructorDecl>(Cand->Function)) {
// Conversion 0 is 'this', which doesn't have a corresponding argument.
// Conversion 0 is 'this', which doesn't have a corresponding parameter.
ConvIdx = 1;
if (CSK == OverloadCandidateSet::CSK_Operator)
// Argument 0 is 'this', which doesn't have a corresponding parameter.
ArgIdx = 1;
}
} else {
// Builtin operator.
@ -10859,16 +10991,19 @@ static void CompleteNonViableCandidate(Sema &S, OverloadCandidate *Cand,
}
// Fill in the rest of the conversions.
for (unsigned ArgIdx = 0; ConvIdx != ConvCount; ++ConvIdx, ++ArgIdx) {
bool Reversed = Cand->RewriteKind & CRK_Reversed;
for (unsigned ParamIdx = Reversed ? ParamTypes.size() - 1 : 0;
ConvIdx != ConvCount;
++ConvIdx, ++ArgIdx, ParamIdx += (Reversed ? -1 : 1)) {
if (Cand->Conversions[ConvIdx].isInitialized()) {
// We've already checked this conversion.
} else if (ArgIdx < ParamTypes.size()) {
if (ParamTypes[ArgIdx]->isDependentType())
if (ParamTypes[ParamIdx]->isDependentType())
Cand->Conversions[ConvIdx].setAsIdentityConversion(
Args[ArgIdx]->getType());
else {
Cand->Conversions[ConvIdx] =
TryCopyInitialization(S, Args[ArgIdx], ParamTypes[ArgIdx],
TryCopyInitialization(S, Args[ArgIdx], ParamTypes[ParamIdx],
SuppressUserConversions,
/*InOverloadResolution=*/true,
/*AllowObjCWritebackConversion=*/
@ -10896,7 +11031,7 @@ SmallVector<OverloadCandidate *, 32> OverloadCandidateSet::CompleteCandidates(
if (Cand->Viable)
Cands.push_back(Cand);
else if (OCD == OCD_AllCandidates) {
CompleteNonViableCandidate(S, Cand, Args);
CompleteNonViableCandidate(S, Cand, Args, Kind);
if (Cand->Function || Cand->IsSurrogate)
Cands.push_back(Cand);
// Otherwise, this a non-viable builtin candidate. We do not, in general,
@ -11453,7 +11588,7 @@ public:
if (FunctionDecl *Fun =
dyn_cast<FunctionDecl>((*I)->getUnderlyingDecl()))
if (!functionHasPassObjectSizeParams(Fun))
S.NoteOverloadCandidate(*I, Fun, TargetFunctionType,
S.NoteOverloadCandidate(*I, Fun, CRK_None, TargetFunctionType,
/*TakingAddress=*/true);
FailedCandidates.NoteCandidates(S, OvlExpr->getBeginLoc());
}
@ -12403,7 +12538,7 @@ Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, UnaryOperatorKind Opc,
OverloadCandidateSet CandidateSet(OpLoc, OverloadCandidateSet::CSK_Operator);
// Add the candidates from the given function set.
AddFunctionCandidates(Fns, ArgsArray, CandidateSet);
AddNonMemberOperatorCandidates(Fns, ArgsArray, CandidateSet);
// Add operator candidates that are member functions.
AddMemberOperatorCandidates(Op, OpLoc, ArgsArray, CandidateSet);
@ -12548,14 +12683,17 @@ Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, UnaryOperatorKind Opc,
///
/// \param LHS Left-hand argument.
/// \param RHS Right-hand argument.
ExprResult
Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
BinaryOperatorKind Opc,
const UnresolvedSetImpl &Fns,
Expr *LHS, Expr *RHS, bool PerformADL) {
ExprResult Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
BinaryOperatorKind Opc,
const UnresolvedSetImpl &Fns, Expr *LHS,
Expr *RHS, bool PerformADL,
bool AllowRewrittenCandidates) {
Expr *Args[2] = { LHS, RHS };
LHS=RHS=nullptr; // Please use only Args instead of LHS/RHS couple
if (!getLangOpts().CPlusPlus2a)
AllowRewrittenCandidates = false;
OverloadedOperatorKind Op = BinaryOperator::getOverloadedOperator(Opc);
DeclarationName OpName = Context.DeclarationNames.getCXXOperatorName(Op);
@ -12613,23 +12751,61 @@ Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
return CreateBuiltinBinOp(OpLoc, Opc, Args[0], Args[1]);
// Build an empty overload set.
OverloadCandidateSet CandidateSet(OpLoc, OverloadCandidateSet::CSK_Operator);
OverloadCandidateSet CandidateSet(
OpLoc, OverloadCandidateSet::CSK_Operator,
OverloadCandidateSet::OperatorRewriteInfo(Op, AllowRewrittenCandidates));
// Add the candidates from the given function set.
AddFunctionCandidates(Fns, Args, CandidateSet);
OverloadedOperatorKind ExtraOp =
AllowRewrittenCandidates ? getRewrittenOverloadedOperator(Op) : OO_None;
// Add the candidates from the given function set. This also adds the
// rewritten candidates using these functions if necessary.
AddNonMemberOperatorCandidates(Fns, Args, CandidateSet);
// Add operator candidates that are member functions.
AddMemberOperatorCandidates(Op, OpLoc, Args, CandidateSet);
if (CandidateSet.getRewriteInfo().shouldAddReversed(Op))
AddMemberOperatorCandidates(Op, OpLoc, {Args[1], Args[0]}, CandidateSet,
OverloadCandidateParamOrder::Reversed);
// In C++20, also add any rewritten member candidates.
if (ExtraOp) {
AddMemberOperatorCandidates(ExtraOp, OpLoc, Args, CandidateSet);
if (CandidateSet.getRewriteInfo().shouldAddReversed(ExtraOp))
AddMemberOperatorCandidates(ExtraOp, OpLoc, {Args[1], Args[0]},
CandidateSet,
OverloadCandidateParamOrder::Reversed);
}
// Add candidates from ADL. Per [over.match.oper]p2, this lookup is not
// performed for an assignment operator (nor for operator[] nor operator->,
// which don't get here).
if (Opc != BO_Assign && PerformADL)
if (Opc != BO_Assign && PerformADL) {
AddArgumentDependentLookupCandidates(OpName, OpLoc, Args,
/*ExplicitTemplateArgs*/ nullptr,
CandidateSet);
if (ExtraOp) {
DeclarationName ExtraOpName =
Context.DeclarationNames.getCXXOperatorName(ExtraOp);
AddArgumentDependentLookupCandidates(ExtraOpName, OpLoc, Args,
/*ExplicitTemplateArgs*/ nullptr,
CandidateSet);
}
}
// Add builtin operator candidates.
//
// FIXME: We don't add any rewritten candidates here. This is strictly
// incorrect; a builtin candidate could be hidden by a non-viable candidate,
// resulting in our selecting a rewritten builtin candidate. For example:
//
// enum class E { e };
// bool operator!=(E, E) requires false;
// bool k = E::e != E::e;
//
// ... should select the rewritten builtin candidate 'operator==(E, E)'. But
// it seems unreasonable to consider rewritten builtin candidates. A core
// issue has been filed proposing to removed this requirement.
AddBuiltinOperatorCandidates(Op, OpLoc, Args, CandidateSet);
bool HadMultipleCandidates = (CandidateSet.size() > 1);
@ -12641,11 +12817,57 @@ Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
// We found a built-in operator or an overloaded operator.
FunctionDecl *FnDecl = Best->Function;
bool IsReversed = (Best->RewriteKind & CRK_Reversed);
if (IsReversed)
std::swap(Args[0], Args[1]);
if (FnDecl) {
Expr *Base = nullptr;
// We matched an overloaded operator. Build a call to that
// operator.
OverloadedOperatorKind ChosenOp =
FnDecl->getDeclName().getCXXOverloadedOperator();
// C++2a [over.match.oper]p9:
// If a rewritten operator== candidate is selected by overload
// resolution for an operator@, its return type shall be cv bool
if (Best->RewriteKind && ChosenOp == OO_EqualEqual &&
!FnDecl->getReturnType()->isBooleanType()) {
Diag(OpLoc, diag::err_ovl_rewrite_equalequal_not_bool)
<< FnDecl->getReturnType() << BinaryOperator::getOpcodeStr(Opc)
<< Args[0]->getSourceRange() << Args[1]->getSourceRange();
Diag(FnDecl->getLocation(), diag::note_declared_at);
return ExprError();
}
if (AllowRewrittenCandidates && !IsReversed &&
CandidateSet.getRewriteInfo().shouldAddReversed(ChosenOp)) {
// We could have reversed this operator, but didn't. Check if the
// reversed form was a viable candidate, and if so, if it had a
// better conversion for either parameter. If so, this call is
// formally ambiguous, and allowing it is an extension.
for (OverloadCandidate &Cand : CandidateSet) {
if (Cand.Viable && Cand.Function == FnDecl &&
Cand.RewriteKind & CRK_Reversed) {
for (unsigned ArgIdx = 0; ArgIdx < 2; ++ArgIdx) {
if (CompareImplicitConversionSequences(
*this, OpLoc, Cand.Conversions[ArgIdx],
Best->Conversions[ArgIdx]) ==
ImplicitConversionSequence::Better) {
Diag(OpLoc, diag::ext_ovl_ambiguous_oper_binary_reversed)
<< BinaryOperator::getOpcodeStr(Opc)
<< Args[0]->getType() << Args[1]->getType()
<< Args[0]->getSourceRange() << Args[1]->getSourceRange();
Diag(FnDecl->getLocation(),
diag::note_ovl_ambiguous_oper_binary_reversed_candidate);
}
}
break;
}
}
}
// Convert the arguments.
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(FnDecl)) {
// Best->Access is only meaningful for class members.
@ -12699,8 +12921,8 @@ Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
ResultTy = ResultTy.getNonLValueExprType(Context);
CXXOperatorCallExpr *TheCall = CXXOperatorCallExpr::Create(
Context, Op, FnExpr.get(), Args, ResultTy, VK, OpLoc, FPFeatures,
Best->IsADLCandidate);
Context, ChosenOp, FnExpr.get(), Args, ResultTy, VK, OpLoc,
FPFeatures, Best->IsADLCandidate);
if (CheckCallReturnType(FnDecl->getReturnType(), OpLoc, TheCall,
FnDecl))
@ -12722,7 +12944,46 @@ Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
isa<CXXMethodDecl>(FnDecl), OpLoc, TheCall->getSourceRange(),
VariadicDoesNotApply);
return MaybeBindToTemporary(TheCall);
ExprResult R = MaybeBindToTemporary(TheCall);
if (R.isInvalid())
return ExprError();
// For a rewritten candidate, we've already reversed the arguments
// if needed. Perform the rest of the rewrite now.
if ((Best->RewriteKind & CRK_DifferentOperator) ||
(Op == OO_Spaceship && IsReversed)) {
if (Op == OO_ExclaimEqual) {
assert(ChosenOp == OO_EqualEqual && "unexpected operator name");
R = CreateBuiltinUnaryOp(OpLoc, UO_LNot, R.get());
} else {
assert(ChosenOp == OO_Spaceship && "unexpected operator name");
llvm::APSInt Zero(Context.getTypeSize(Context.IntTy), false);
Expr *ZeroLiteral =
IntegerLiteral::Create(Context, Zero, Context.IntTy, OpLoc);
Sema::CodeSynthesisContext Ctx;
Ctx.Kind = Sema::CodeSynthesisContext::RewritingOperatorAsSpaceship;
Ctx.Entity = FnDecl;
pushCodeSynthesisContext(Ctx);
R = CreateOverloadedBinOp(
OpLoc, Opc, Fns, IsReversed ? ZeroLiteral : R.get(),
IsReversed ? R.get() : ZeroLiteral, PerformADL,
/*AllowRewrittenCandidates=*/false);
popCodeSynthesisContext();
}
if (R.isInvalid())
return ExprError();
} else {
assert(ChosenOp == Op && "unexpected operator name");
}
// Make a note in the AST if we did any rewriting.
if (Best->RewriteKind != CRK_None)
R = new (Context) CXXRewrittenBinaryOperator(R.get(), IsReversed);
return R;
} else {
// We matched a built-in operator. Convert the arguments, then
// break out so that we will build the appropriate built-in
@ -12812,10 +13073,12 @@ Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
return ExprError();
}
CandidateSet.NoteCandidates(
PartialDiagnosticAt(OpLoc, PDiag(diag::err_ovl_deleted_oper)
<< BinaryOperator::getOpcodeStr(Opc)
<< Args[0]->getSourceRange()
<< Args[1]->getSourceRange()),
PartialDiagnosticAt(
OpLoc, PDiag(diag::err_ovl_deleted_oper)
<< getOperatorSpelling(Best->Function->getDeclName()
.getCXXOverloadedOperator())
<< Args[0]->getSourceRange()
<< Args[1]->getSourceRange()),
*this, OCD_AllCandidates, Args, BinaryOperator::getOpcodeStr(Opc),
OpLoc);
return ExprError();
@ -13692,8 +13955,8 @@ ExprResult Sema::BuildLiteralOperatorCall(LookupResult &R,
OverloadCandidateSet CandidateSet(UDSuffixLoc,
OverloadCandidateSet::CSK_Normal);
AddFunctionCandidates(R.asUnresolvedSet(), Args, CandidateSet, TemplateArgs,
/*SuppressUserConversions=*/true);
AddNonMemberOperatorCandidates(R.asUnresolvedSet(), Args, CandidateSet,
TemplateArgs);
bool HadMultipleCandidates = (CandidateSet.size() > 1);

View File

@ -24,6 +24,7 @@
#include "clang/Basic/TargetInfo.h"
#include "clang/Sema/DeclSpec.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Overload.h"
#include "clang/Sema/ParsedTemplate.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/SemaInternal.h"
@ -8488,7 +8489,7 @@ bool Sema::CheckFunctionTemplateSpecialization(
// candidates at once, to get proper sorting and limiting.
for (auto *OldND : Previous) {
if (auto *OldFD = dyn_cast<FunctionDecl>(OldND->getUnderlyingDecl()))
NoteOverloadCandidate(OldND, OldFD, FD->getType(), false);
NoteOverloadCandidate(OldND, OldFD, CRK_None, FD->getType(), false);
}
FailedCandidates.NoteCandidates(*this, FD->getLocation());
return true;

View File

@ -206,6 +206,7 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const {
case DefiningSynthesizedFunction:
case ExceptionSpecEvaluation:
case ConstraintSubstitution:
case RewritingOperatorAsSpaceship:
return false;
// This function should never be called when Kind's value is Memoization.
@ -682,6 +683,11 @@ void Sema::PrintInstantiationStack() {
break;
}
case CodeSynthesisContext::RewritingOperatorAsSpaceship:
Diags.Report(Active->Entity->getLocation(),
diag::note_rewriting_operator_as_spaceship);
break;
case CodeSynthesisContext::Memoization:
break;
@ -754,6 +760,7 @@ Optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const {
case CodeSynthesisContext::DeclaringSpecialMember:
case CodeSynthesisContext::DefiningSynthesizedFunction:
case CodeSynthesisContext::RewritingOperatorAsSpaceship:
// This happens in a context unrelated to template instantiation, so
// there is no SFINAE.
return None;

View File

@ -2355,13 +2355,13 @@ public:
/// Build a new rewritten operator expression.
///
/// By default, builds the rewritten operator without performing any semantic
/// analysis. Subclasses may override this routine to provide different
/// behavior.
ExprResult RebuildCXXRewrittenBinaryOperator(Expr *SemanticForm,
bool IsReversed) {
return new (getSema().Context)
CXXRewrittenBinaryOperator(SemanticForm, IsReversed);
/// By default, performs semantic analysis to build the new expression.
/// Subclasses may override this routine to provide different behavior.
ExprResult RebuildCXXRewrittenBinaryOperator(
SourceLocation OpLoc, BinaryOperatorKind Opcode,
const UnresolvedSetImpl &UnqualLookups, Expr *LHS, Expr *RHS) {
return getSema().CreateOverloadedBinOp(OpLoc, Opcode, UnqualLookups, LHS,
RHS, /*RequiresADL*/false);
}
/// Build a new conditional operator expression.
@ -9783,24 +9783,45 @@ TreeTransform<Derived>::TransformBinaryOperator(BinaryOperator *E) {
template <typename Derived>
ExprResult TreeTransform<Derived>::TransformCXXRewrittenBinaryOperator(
CXXRewrittenBinaryOperator *E) {
// FIXME: C++ [temp.deduct]p7 "The substitution proceeds in lexical order and
// stops when a condition that causes deduction to fail is encountered."
// requires us to substitute into the LHS before the RHS, even in a rewrite
// that reversed the operand order.
//
// We can't decompose back to a binary operator here, because that would lose
// the unqualified lookup results from the phase 1 name lookup.
CXXRewrittenBinaryOperator::DecomposedForm Decomp = E->getDecomposedForm();
ExprResult SemanticForm = getDerived().TransformExpr(E->getSemanticForm());
if (SemanticForm.isInvalid())
ExprResult LHS = getDerived().TransformExpr(const_cast<Expr*>(Decomp.LHS));
if (LHS.isInvalid())
return ExprError();
ExprResult RHS = getDerived().TransformExpr(const_cast<Expr*>(Decomp.RHS));
if (RHS.isInvalid())
return ExprError();
if (!getDerived().AlwaysRebuild() &&
SemanticForm.get() == E->getSemanticForm())
LHS.get() == Decomp.LHS &&
RHS.get() == Decomp.RHS)
return E;
return getDerived().RebuildCXXRewrittenBinaryOperator(SemanticForm.get(),
E->isReversed());
// Extract the already-resolved callee declarations so that we can restrict
// ourselves to using them as the unqualified lookup results when rebuilding.
UnresolvedSet<2> UnqualLookups;
Expr *PossibleBinOps[] = {E->getSemanticForm(),
const_cast<Expr *>(Decomp.InnerBinOp)};
for (Expr *PossibleBinOp : PossibleBinOps) {
auto *Op = dyn_cast<CXXOperatorCallExpr>(PossibleBinOp->IgnoreImplicit());
if (!Op)
continue;
auto *Callee = dyn_cast<DeclRefExpr>(Op->getCallee()->IgnoreImplicit());
if (!Callee || isa<CXXMethodDecl>(Callee->getDecl()))
continue;
// Transform the callee in case we built a call to a local extern
// declaration.
NamedDecl *Found = cast_or_null<NamedDecl>(getDerived().TransformDecl(
E->getOperatorLoc(), Callee->getFoundDecl()));
if (!Found)
return ExprError();
UnqualLookups.addDecl(Found);
}
return getDerived().RebuildCXXRewrittenBinaryOperator(
E->getOperatorLoc(), Decomp.Opcode, UnqualLookups, LHS.get(), RHS.get());
}
template<typename Derived>

View File

@ -0,0 +1,172 @@
// RUN: %clang_cc1 -std=c++2a -verify %s
// RUN: %clang_cc1 -std=c++2a -verify -Wall -DNO_ERRORS %s
#ifndef NO_ERRORS
namespace bullet3 {
// the built-in candidates include all of the candidate operator fnuctions
// [...] that, compared to the given operator
// - do not have the same parameter-type-list as any non-member candidate
enum E { e };
// Suppress both builtin operator<=>(E, E) and operator<(E, E).
void operator<=>(E, E); // expected-note {{while rewriting}}
bool cmp = e < e; // expected-error {{invalid operands to binary expression ('void' and 'int')}}
// None of the other bullets have anything to test here. In principle we
// need to suppress both builtin operator@(A, B) and operator@(B, A) when we
// see a user-declared reversible operator@(A, B), and we do, but that's
// untestable because the only built-in reversible candidates are
// operator<=>(E, E) and operator==(E, E) for E an enumeration type, and
// those are both symmetric anyway.
}
namespace bullet4 {
// The rewritten candidate set is determined as follows:
template<int> struct X {};
X<1> x1;
X<2> x2;
struct Y {
int operator<=>(X<2>) = delete; // #1member
bool operator==(X<2>) = delete; // #2member
};
Y y;
// - For the relational operators, the rewritten candidates include all
// non-rewritten candidates for the expression x <=> y.
int operator<=>(X<1>, X<2>) = delete; // #1
// expected-note@#1 5{{candidate function has been explicitly deleted}}
// expected-note@#1 5{{candidate function (with reversed parameter order) not viable: no known conversion from 'X<1>' to 'X<2>' for 1st argument}}
bool lt = x1 < x2; // expected-error {{selected deleted operator '<=>'}}
bool le = x1 <= x2; // expected-error {{selected deleted operator '<=>'}}
bool gt = x1 > x2; // expected-error {{selected deleted operator '<=>'}}
bool ge = x1 >= x2; // expected-error {{selected deleted operator '<=>'}}
bool cmp = x1 <=> x2; // expected-error {{selected deleted operator '<=>'}}
// expected-note@#1member 5{{candidate function has been explicitly deleted}}
// expected-note@#1 5{{candidate function not viable: no known conversion from 'bullet4::Y' to 'X<1>' for 1st argument}}
// expected-note@#1 5{{candidate function (with reversed parameter order) not viable: no known conversion from 'bullet4::Y' to 'X<2>' for 1st argument}}
bool mem_lt = y < x2; // expected-error {{selected deleted operator '<=>'}}
bool mem_le = y <= x2; // expected-error {{selected deleted operator '<=>'}}
bool mem_gt = y > x2; // expected-error {{selected deleted operator '<=>'}}
bool mem_ge = y >= x2; // expected-error {{selected deleted operator '<=>'}}
bool mem_cmp = y <=> x2; // expected-error {{selected deleted operator '<=>'}}
// - For the relational and three-way comparison operators, the rewritten
// candidates also include a synthesized candidate, with the order of the
// two parameters reversed, for each non-rewritten candidate for the
// expression y <=> x.
// expected-note@#1 5{{candidate function (with reversed parameter order) has been explicitly deleted}}
// expected-note@#1 5{{candidate function not viable: no known conversion from 'X<2>' to 'X<1>' for 1st argument}}
bool rlt = x2 < x1; // expected-error {{selected deleted operator '<=>'}}
bool rle = x2 <= x1; // expected-error {{selected deleted operator '<=>'}}
bool rgt = x2 > x1; // expected-error {{selected deleted operator '<=>'}}
bool rge = x2 >= x1; // expected-error {{selected deleted operator '<=>'}}
bool rcmp = x2 <=> x1; // expected-error {{selected deleted operator '<=>'}}
// expected-note@#1member 5{{candidate function (with reversed parameter order) has been explicitly deleted}}
// expected-note@#1 5{{candidate function not viable: no known conversion from 'X<2>' to 'X<1>' for 1st argument}}
// expected-note@#1 5{{candidate function (with reversed parameter order) not viable: no known conversion from 'bullet4::Y' to 'X<1>' for 2nd argument}}
bool mem_rlt = x2 < y; // expected-error {{selected deleted operator '<=>'}}
bool mem_rle = x2 <= y; // expected-error {{selected deleted operator '<=>'}}
bool mem_rgt = x2 > y; // expected-error {{selected deleted operator '<=>'}}
bool mem_rge = x2 >= y; // expected-error {{selected deleted operator '<=>'}}
bool mem_rcmp = x2 <=> y; // expected-error {{selected deleted operator '<=>'}}
// For the != operator, the rewritten candidates include all non-rewritten
// candidates for the expression x == y
int operator==(X<1>, X<2>) = delete; // #2
// expected-note@#2 2{{candidate function has been explicitly deleted}}
// expected-note@#2 2{{candidate function (with reversed parameter order) not viable: no known conversion from 'X<1>' to 'X<2>' for 1st argument}}
bool eq = x1 == x2; // expected-error {{selected deleted operator '=='}}
bool ne = x1 != x2; // expected-error {{selected deleted operator '=='}}
// expected-note@#2member 2{{candidate function has been explicitly deleted}}
// expected-note@#2 2{{candidate function not viable: no known conversion from 'bullet4::Y' to 'X<1>' for 1st argument}}
// expected-note@#2 2{{candidate function (with reversed parameter order) not viable: no known conversion from 'bullet4::Y' to 'X<2>' for 1st argument}}
bool mem_eq = y == x2; // expected-error {{selected deleted operator '=='}}
bool mem_ne = y != x2; // expected-error {{selected deleted operator '=='}}
// For the equality operators, the rewritten candidates also include a
// synthesized candidate, with the order of the two parameters reversed, for
// each non-rewritten candidate for the expression y == x
// expected-note@#2 2{{candidate function (with reversed parameter order) has been explicitly deleted}}
// expected-note@#2 2{{candidate function not viable: no known conversion from 'X<2>' to 'X<1>' for 1st argument}}
bool req = x2 == x1; // expected-error {{selected deleted operator '=='}}
bool rne = x2 != x1; // expected-error {{selected deleted operator '=='}}
// expected-note@#2member 2{{candidate function (with reversed parameter order) has been explicitly deleted}}
// expected-note@#2 2{{candidate function not viable: no known conversion from 'X<2>' to 'X<1>' for 1st argument}}
// expected-note@#2 2{{candidate function (with reversed parameter order) not viable: no known conversion from 'bullet4::Y' to 'X<1>' for 2nd argument}}
bool mem_req = x2 == y; // expected-error {{selected deleted operator '=='}}
bool mem_rne = x2 != y; // expected-error {{selected deleted operator '=='}}
// For all other operators, the rewritten candidate set is empty.
X<3> operator+(X<1>, X<2>) = delete; // expected-note {{no known conversion from 'X<2>' to 'X<1>'}}
X<3> reversed_add = x2 + x1; // expected-error {{invalid operands}}
}
// Various C++17 cases that are known to be broken by the C++20 rules.
namespace problem_cases {
// We can have an ambiguity between an operator and its reversed form. This
// wasn't intended by the original "consistent comparison" proposal, and we
// allow it as extension, picking the non-reversed form.
struct A {
bool operator==(const A&); // expected-note {{ambiguity is between a regular call to this operator and a call with the argument order reversed}}
};
bool cmp_non_const = A() == A(); // expected-warning {{ambiguous}}
struct B {
virtual bool operator==(const B&) const;
};
struct D : B {
bool operator==(const B&) const override; // expected-note {{operator}}
};
bool cmp_base_derived = D() == D(); // expected-warning {{ambiguous}}
template<typename T> struct CRTPBase {
bool operator==(const T&) const; // expected-note {{operator}}
};
struct CRTP : CRTPBase<CRTP> {};
bool cmp_crtp = CRTP() == CRTP(); // expected-warning {{ambiguous}}
// We can select a non-rewriteable operator== for a != comparison, when there
// was a viable operator!= candidate we could have used instead.
//
// Rejecting this seems OK on balance.
using UBool = signed char; // ICU uses this.
struct ICUBase {
virtual UBool operator==(const ICUBase&) const;
UBool operator!=(const ICUBase &arg) const { return !operator==(arg); }
};
struct ICUDerived : ICUBase {
UBool operator==(const ICUBase&) const override; // expected-note {{declared here}}
};
bool cmp_icu = ICUDerived() != ICUDerived(); // expected-error {{not 'bool'}}
}
#else // NO_ERRORS
namespace problem_cases {
// We can select a reversed candidate where we used to select a non-reversed
// one, and in the worst case this can dramatically change the meaning of the
// program. Make sure we at least warn on the worst cases under -Wall.
struct iterator;
struct const_iterator {
const_iterator(iterator);
bool operator==(const const_iterator&) const;
};
struct iterator {
bool operator==(const const_iterator &o) const { // expected-warning {{all paths through this function will call itself}}
return o == *this;
}
};
}
#endif // NO_ERRORS

View File

@ -0,0 +1,70 @@
// RUN: %clang_cc1 -std=c++2a -verify %s
template<typename L, typename R> struct Op { L l; const char *op; R r; };
// FIXME: Remove once we implement P1816R0.
template<typename L, typename R> Op(L, R) -> Op<L, R>;
struct A {};
struct B {};
constexpr Op<A, B> operator<=>(A a, B b) { return {a, "<=>", b}; }
template<typename T, typename U, typename V> constexpr Op<Op<T, U>, V> operator< (Op<T, U> a, V b) { return {a, "<", b}; }
template<typename T, typename U, typename V> constexpr Op<Op<T, U>, V> operator<= (Op<T, U> a, V b) { return {a, "<=", b}; }
template<typename T, typename U, typename V> constexpr Op<Op<T, U>, V> operator> (Op<T, U> a, V b) { return {a, ">", b}; }
template<typename T, typename U, typename V> constexpr Op<Op<T, U>, V> operator>= (Op<T, U> a, V b) { return {a, ">=", b}; }
template<typename T, typename U, typename V> constexpr Op<Op<T, U>, V> operator<=>(Op<T, U> a, V b) { return {a, "<=>", b}; }
template<typename T, typename U, typename V> constexpr Op<T, Op<U, V>> operator< (T a, Op<U, V> b) { return {a, "<", b}; }
template<typename T, typename U, typename V> constexpr Op<T, Op<U, V>> operator<= (T a, Op<U, V> b) { return {a, "<=", b}; }
template<typename T, typename U, typename V> constexpr Op<T, Op<U, V>> operator> (T a, Op<U, V> b) { return {a, ">", b}; }
template<typename T, typename U, typename V> constexpr Op<T, Op<U, V>> operator>= (T a, Op<U, V> b) { return {a, ">=", b}; }
template<typename T, typename U, typename V> constexpr Op<T, Op<U, V>> operator<=>(T a, Op<U, V> b) { return {a, "<=>", b}; }
constexpr bool same(A, A) { return true; }
constexpr bool same(B, B) { return true; }
constexpr bool same(int a, int b) { return a == b; }
template<typename T, typename U>
constexpr bool same(Op<T, U> x, Op<T, U> y) {
return same(x.l, y.l) && __builtin_strcmp(x.op, y.op) == 0 && same(x.r, y.r);
}
// x @ y is interpreted as:
void f(A x, B y) {
// -- (x <=> y) @ 0 if not reversed
static_assert(same(x < y, (x <=> y) < 0));
static_assert(same(x <= y, (x <=> y) <= 0));
static_assert(same(x > y, (x <=> y) > 0));
static_assert(same(x >= y, (x <=> y) >= 0));
static_assert(same(x <=> y, x <=> y)); // (not rewritten)
}
void g(B x, A y) {
// -- 0 @ (y <=> x) if reversed
static_assert(same(x < y, 0 < (y <=> x)));
static_assert(same(x <= y, 0 <= (y <=> x)));
static_assert(same(x > y, 0 > (y <=> x)));
static_assert(same(x >= y, 0 >= (y <=> x)));
static_assert(same(x <=> y, 0 <=> (y <=> x)));
}
// We can rewrite into a call involving a builtin operator.
struct X { int result; };
struct Y {};
constexpr int operator<=>(X x, Y) { return x.result; }
static_assert(X{-1} < Y{});
static_assert(X{0} < Y{}); // expected-error {{failed}}
static_assert(X{0} <= Y{});
static_assert(X{1} <= Y{}); // expected-error {{failed}}
static_assert(X{1} > Y{});
static_assert(X{0} > Y{}); // expected-error {{failed}}
static_assert(X{0} >= Y{});
static_assert(X{-1} >= Y{}); // expected-error {{failed}}
static_assert(Y{} < X{1});
static_assert(Y{} < X{0}); // expected-error {{failed}}
static_assert(Y{} <= X{0});
static_assert(Y{} <= X{-1}); // expected-error {{failed}}
static_assert(Y{} > X{-1});
static_assert(Y{} > X{0}); // expected-error {{failed}}
static_assert(Y{} >= X{0});
static_assert(Y{} >= X{1}); // expected-error {{failed}}

View File

@ -0,0 +1,38 @@
// RUN: %clang_cc1 -std=c++2a -verify %s
// ... return type shall be cv bool ...
namespace not_bool {
struct X {} x;
struct Y {} y;
int operator==(X, Y); // expected-note 4{{here}}
bool a = x == y; // ok
bool b = y == x; // expected-error {{return type 'int' of selected 'operator==' function for rewritten '==' comparison is not 'bool'}}
bool c = x != y; // expected-error {{return type 'int' of selected 'operator==' function for rewritten '!=' comparison is not 'bool'}}
bool d = y != x; // expected-error {{return type 'int' of selected 'operator==' function for rewritten '!=' comparison is not 'bool'}}
// cv-qualifiers are OK
const bool operator==(Y, X);
bool e = y != x; // ok
// We don't prefer a function with bool return type over one witn non-bool return type.
bool f = x != y; // expected-error {{return type 'int' of selected 'operator==' function for rewritten '!=' comparison is not 'bool'}}
}
struct X { bool equal; };
struct Y {};
constexpr bool operator==(X x, Y) { return x.equal; }
static_assert(X{true} == Y{});
static_assert(X{false} == Y{}); // expected-error {{failed}}
// x == y -> y == x
static_assert(Y{} == X{true});
static_assert(Y{} == X{false}); // expected-error {{failed}}
// x != y -> !(x == y)
static_assert(X{true} != Y{}); // expected-error {{failed}}
static_assert(X{false} != Y{});
// x != y -> !(y == x)
static_assert(Y{} != X{true}); // expected-error {{failed}}
static_assert(Y{} != X{false});

View File

@ -1,4 +1,5 @@
// RUN: %clang_cc1 -std=c++11 -verify %s
// RUN: %clang_cc1 -std=c++2a -verify %s
struct Q { typedef int type; };
@ -20,3 +21,36 @@ template<typename T, template<typename T::type...> class ...X> void c(T);
int &c(...);
int &c_disabled = c(0);
int &c_enabled = c(Q()); // expected-error {{cannot bind to a temporary of type 'void'}}
// The substitution proceeds in lexical order and stops when a condition that
// causes deduction to fail is encountered.
#if __cplusplus > 201702L
namespace reversed_operator_substitution_order {
struct X { X(int); };
struct Y { Y(int); };
struct Cat {};
namespace no_adl {
Cat operator<=>(Y, X);
bool operator<(int, Cat);
template<typename T> struct indirect_sizeof {
static_assert(sizeof(T) != 0);
static const auto value = sizeof(T);
};
// We should substitute into the construction of the X object before the
// construction of the Y object, so this is a SFINAE case rather than a
// hard error. This requires substitution to proceed in lexical order
// despite the prior rewrite to
// 0 < (Y(...) <=> X(...))
template<typename T> float &f(
decltype(
X(sizeof(T)) < Y(indirect_sizeof<T>::value)
)
);
template<typename T> int &f(...);
}
int &r = no_adl::f<void>(true);
float &s = no_adl::f<int>(true);
}
#endif

View File

@ -0,0 +1,11 @@
// RUN: %clang_cc1 -emit-llvm %s -o - -triple=x86_64-linux-gnu -std=c++2a | FileCheck %s
namespace spaceship {
struct X {};
struct Y {};
int operator<=>(X, Y);
// CHECK-LABEL: define {{.*}} @_ZN9spaceship1fIiEEvDTcmltcvNS_1YE_EcvNS_1XE_EcvT__EE
template<typename T> void f(decltype(Y() < X(), T()) x) {}
template void f<int>(int);
}

View File

@ -15,6 +15,16 @@ inline auto bar(int x) {
return (1 <=> x);
}
struct X {
int a;
friend constexpr std::strong_ordering operator<=>(const X &x, const X &y) {
return x.a <=> y.a;
}
};
constexpr auto baz(int x) {
return X{3} < X{x};
}
#else
// expected-no-diagnostics
@ -25,4 +35,7 @@ auto bar2(int x) {
return bar(x);
}
static_assert(!baz(3));
static_assert(baz(4));
#endif

View File

@ -298,11 +298,11 @@ void test_enum_enum_compare_no_builtin() {
template <int>
struct Tag {};
// expected-note@+1 {{candidate}}
Tag<0> operator<=>(EnumA, EnumA) {
Tag<0> operator<=>(EnumA, EnumA) { // expected-note {{not viable}}
return {};
}
Tag<1> operator<=>(EnumA, EnumB) {
// expected-note@+1 {{while rewriting comparison as call to 'operator<=>' declared here}}
Tag<1> operator<=>(EnumA, EnumB) { // expected-note {{not viable}}
return {};
}
@ -311,7 +311,7 @@ void test_enum_ovl_provided() {
ASSERT_EXPR_TYPE(r1, Tag<0>);
auto r2 = (EnumA::A <=> EnumB::B);
ASSERT_EXPR_TYPE(r2, Tag<1>);
(void)(EnumB::B <=> EnumA::A); // expected-error {{invalid operands to binary expression ('EnumCompareTests::EnumB' and 'EnumCompareTests::EnumA')}}
(void)(EnumB::B <=> EnumA::A); // expected-error {{invalid operands to binary expression ('int' and 'Tag<1>')}}
}
void enum_float_test() {

View File

@ -5,7 +5,7 @@ int foo(int x) {
}
struct X {
bool operator==(const X &x);
bool operator==(const X &x) const;
};
struct A {

View File

@ -920,23 +920,26 @@ as the draft C++2a standard evolves.
<tr>
<td rowspan="6">Consistent comparison (<tt>operator&lt;=&gt;</tt>)</td>
<td><a href="http://wg21.link/p0515r3">P0515R3</a></td>
<td rowspan="3" class="partial" align="center">Partial</td>
<td class="partial" align="center">Partial</td>
</tr>
<tr> <!-- from Jacksonville -->
<td><a href="http://wg21.link/p0905r1">P0905R1</a></td>
<td class="svn" align="center">Clang 10</td>
</tr>
<tr> <!-- from Rapperswil -->
<td><a href="http://wg21.link/p1120r0">P1120R0</a></td>
<td rowspan="2" class="partial" align="center">Partial</td>
</tr>
<tr> <!-- from Kona 2019 -->
<td><a href="http://wg21.link/p1185r2">P1185R2</a></td>
<td rowspan="3" class="none" align="center">No</td>
</tr>
<tr> <!-- from Cologne -->
<td><a href="http://wg21.link/p1186r3">P1186R3</a></td>
<td class="none" align="center">No</td>
</tr>
<tr>
<td><a href="http://wg21.link/p1630r1">P1630R1</a></td>
<td class="partial" align="center">Partial</td>
</tr>
<tr>
<td>Access checking on specializations</td>