forked from OSchip/llvm-project
[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:
parent
778dc0f1d4
commit
974c8b7e2f
|
@ -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
|
||||
|
|
|
@ -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<
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
|
@ -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}}
|
|
@ -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});
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -5,7 +5,7 @@ int foo(int x) {
|
|||
}
|
||||
|
||||
struct X {
|
||||
bool operator==(const X &x);
|
||||
bool operator==(const X &x) const;
|
||||
};
|
||||
|
||||
struct A {
|
||||
|
|
|
@ -920,23 +920,26 @@ as the draft C++2a standard evolves.
|
|||
<tr>
|
||||
<td rowspan="6">Consistent comparison (<tt>operator<=></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>
|
||||
|
|
Loading…
Reference in New Issue