aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2019-10-19 00:04:43 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2019-10-19 00:04:43 +0000
commit05441b0db7bf23d206dfc865b5c5bde17da1dab0 (patch)
treeaca95f32bc08960a7da40f397bffc76b8e1a405a
parentea521aa6024431138f7890d486a6e3500822b3d5 (diff)
downloadclang-05441b0db7bf23d206dfc865b5c5bde17da1dab0.tar.gz
[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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@375306 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/AST/ExprCXX.h68
-rw-r--r--include/clang/Basic/DiagnosticSemaKinds.td15
-rw-r--r--include/clang/Basic/OperatorKinds.h19
-rw-r--r--include/clang/Sema/Overload.h94
-rw-r--r--include/clang/Sema/Sema.h55
-rw-r--r--lib/AST/ExprCXX.cpp4
-rw-r--r--lib/Frontend/FrontendActions.cpp2
-rw-r--r--lib/Sema/SemaExpr.cpp7
-rw-r--r--lib/Sema/SemaOverload.cpp477
-rw-r--r--lib/Sema/SemaTemplate.cpp3
-rw-r--r--lib/Sema/SemaTemplateInstantiate.cpp7
-rw-r--r--lib/Sema/TreeTransform.h59
-rw-r--r--test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp172
-rw-r--r--test/CXX/over/over.match/over.match.funcs/over.match.oper/p8-2a.cpp70
-rw-r--r--test/CXX/over/over.match/over.match.funcs/over.match.oper/p9-2a.cpp38
-rw-r--r--test/CXX/temp/temp.fct.spec/temp.deduct/p7.cpp34
-rw-r--r--test/CodeGenCXX/mangle-cxx2a.cpp11
-rw-r--r--test/PCH/cxx2a-compare.cpp13
-rw-r--r--test/SemaCXX/compare-cxx2a.cpp8
-rw-r--r--test/SemaCXX/self-comparison.cpp2
-rwxr-xr-xwww/cxx_status.html7
21 files changed, 966 insertions, 199 deletions
diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h
index 2b3085183c..7b7ca9bf8f 100644
--- a/include/clang/AST/ExprCXX.h
+++ b/include/clang/AST/ExprCXX.h
@@ -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
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index 755fac842d..e41cfe2fe7 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -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<
diff --git a/include/clang/Basic/OperatorKinds.h b/include/clang/Basic/OperatorKinds.h
index 9757acaa53..d661891445 100644
--- a/include/clang/Basic/OperatorKinds.h
+++ b/include/clang/Basic/OperatorKinds.h
@@ -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
diff --git a/include/clang/Sema/Overload.h b/include/clang/Sema/Overload.h
index 144e6d927c..a97a7181f7 100644
--- a/include/clang/Sema/Overload.h
+++ b/include/clang/Sema/Overload.h
@@ -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.
diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index d3481af50a..1e3d6b93d0 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -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
diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp
index 0b0d3c6880..904928bdf2 100644
--- a/lib/AST/ExprCXX.cpp
+++ b/lib/AST/ExprCXX.cpp
@@ -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();
diff --git a/lib/Frontend/FrontendActions.cpp b/lib/Frontend/FrontendActions.cpp
index fea5826179..4d47c768ad 100644
--- a/lib/Frontend/FrontendActions.cpp
+++ b/lib/Frontend/FrontendActions.cpp
@@ -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:
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index 333983a430..2734b4d076 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -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);
diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp
index 80bd3562bc..6793079ff0 100644
--- a/lib/Sema/SemaOverload.cpp
+++ b/lib/Sema/SemaOverload.cpp
@@ -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);
diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp
index 09cc525837..3f2d38630c 100644
--- a/lib/Sema/SemaTemplate.cpp
+++ b/lib/Sema/SemaTemplate.cpp
@@ -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;
diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp
index 42411c9c33..0daa33cfbe 100644
--- a/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/lib/Sema/SemaTemplateInstantiate.cpp
@@ -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;
diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h
index 461cd909c2..89a7b8cc84 100644
--- a/lib/Sema/TreeTransform.h
+++ b/lib/Sema/TreeTransform.h
@@ -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>
diff --git a/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp b/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp
new file mode 100644
index 0000000000..3be8dc7749
--- /dev/null
+++ b/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp
@@ -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
diff --git a/test/CXX/over/over.match/over.match.funcs/over.match.oper/p8-2a.cpp b/test/CXX/over/over.match/over.match.funcs/over.match.oper/p8-2a.cpp
new file mode 100644
index 0000000000..da0f576f8c
--- /dev/null
+++ b/test/CXX/over/over.match/over.match.funcs/over.match.oper/p8-2a.cpp
@@ -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}}
diff --git a/test/CXX/over/over.match/over.match.funcs/over.match.oper/p9-2a.cpp b/test/CXX/over/over.match/over.match.funcs/over.match.oper/p9-2a.cpp
new file mode 100644
index 0000000000..fce46816ce
--- /dev/null
+++ b/test/CXX/over/over.match/over.match.funcs/over.match.oper/p9-2a.cpp
@@ -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});
diff --git a/test/CXX/temp/temp.fct.spec/temp.deduct/p7.cpp b/test/CXX/temp/temp.fct.spec/temp.deduct/p7.cpp
index bc074ba25e..f0ab7b8ea7 100644
--- a/test/CXX/temp/temp.fct.spec/temp.deduct/p7.cpp
+++ b/test/CXX/temp/temp.fct.spec/temp.deduct/p7.cpp
@@ -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
diff --git a/test/CodeGenCXX/mangle-cxx2a.cpp b/test/CodeGenCXX/mangle-cxx2a.cpp
new file mode 100644
index 0000000000..3e4689948a
--- /dev/null
+++ b/test/CodeGenCXX/mangle-cxx2a.cpp
@@ -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);
+}
diff --git a/test/PCH/cxx2a-compare.cpp b/test/PCH/cxx2a-compare.cpp
index 9ad368d88f..019544d28c 100644
--- a/test/PCH/cxx2a-compare.cpp
+++ b/test/PCH/cxx2a-compare.cpp
@@ -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
diff --git a/test/SemaCXX/compare-cxx2a.cpp b/test/SemaCXX/compare-cxx2a.cpp
index b6e7fc8061..28854a77fb 100644
--- a/test/SemaCXX/compare-cxx2a.cpp
+++ b/test/SemaCXX/compare-cxx2a.cpp
@@ -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() {
diff --git a/test/SemaCXX/self-comparison.cpp b/test/SemaCXX/self-comparison.cpp
index e20706a8b6..95b830c910 100644
--- a/test/SemaCXX/self-comparison.cpp
+++ b/test/SemaCXX/self-comparison.cpp
@@ -5,7 +5,7 @@ int foo(int x) {
}
struct X {
- bool operator==(const X &x);
+ bool operator==(const X &x) const;
};
struct A {
diff --git a/www/cxx_status.html b/www/cxx_status.html
index 322c49a614..70fab7ab34 100755
--- a/www/cxx_status.html
+++ b/www/cxx_status.html
@@ -920,23 +920,26 @@ as the draft C++2a standard evolves.
<tr>
<td rowspan="6">Consistent comparison (<tt>operator&lt;=&gt;</tt>)</td>
<td><a href="http://wg21.link/p0515r3">P0515R3</a></td>
- <td rowspan="3" class="partial" align="center">Partial</td>
+ <td class="partial" align="center">Partial</td>
</tr>
<tr> <!-- from Jacksonville -->
<td><a href="http://wg21.link/p0905r1">P0905R1</a></td>
+ <td class="svn" align="center">Clang 10</td>
</tr>
<tr> <!-- from Rapperswil -->
<td><a href="http://wg21.link/p1120r0">P1120R0</a></td>
+ <td rowspan="2" class="partial" align="center">Partial</td>
</tr>
<tr> <!-- from Kona 2019 -->
<td><a href="http://wg21.link/p1185r2">P1185R2</a></td>
- <td rowspan="3" class="none" align="center">No</td>
</tr>
<tr> <!-- from Cologne -->
<td><a href="http://wg21.link/p1186r3">P1186R3</a></td>
+ <td class="none" align="center">No</td>
</tr>
<tr>
<td><a href="http://wg21.link/p1630r1">P1630R1</a></td>
+ <td class="partial" align="center">Partial</td>
</tr>
<tr>
<td>Access checking on specializations</td>