<div dir="ltr"><div dir="ltr">On Mon, Jan 13, 2020 at 5:50 PM Richard Smith <<a href="mailto:richard@metafoo.co.uk" target="_blank">richard@metafoo.co.uk</a>> wrote:<br></div><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div dir="ltr">On Mon, 13 Jan 2020 at 15:12, Peter Collingbourne via cfe-commits <<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a>> wrote:<br></div><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div dir="ltr">On Fri, Oct 18, 2019 at 5:02 PM Richard Smith via cfe-commits <<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a>> wrote:<br></div><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Author: rsmith<br>
Date: Fri Oct 18 17:04:43 2019<br>
New Revision: 375306<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=375306&view=rev" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project?rev=375306&view=rev</a><br>
Log:<br>
[c++20] Add rewriting from comparison operators to <=> / ==.<br>
<br>
This adds support for rewriting <, >, <=, and >= to a normal or reversed<br>
call to operator<=>, for rewriting != to a normal or reversed call to<br>
operator==, and for rewriting <=> and == to reversed forms of those same<br>
operators.<br>
<br>
Note that this is a breaking change for various C++17 code patterns,<br>
including some in use in LLVM. The most common patterns (where an<br>
operator== becomes ambiguous with a reversed form of itself) are still<br>
accepted under this patch, as an extension (with a warning). I'm hopeful<br>
that we can get the language rules fixed before C++20 ships, and the<br>
extension warning is aimed primarily at providing data to inform that<br>
decision.<br></blockquote><div><br></div><div>Hi Richard, it looks like this change caused us to start rejecting the following input:</div><div><br></div><div>struct S {<br> bool operator==(int rhs) const;<br> bool operator!=(int rhs) const;<br> operator int() const;<br>};<br><br>void f(S a, S b) {<br> a != b;<br>}<br></div><div><br></div><div>with:</div><div><br></div><div>$ clang -std=gnu++2a -fsyntax-only /tmp/test.cpp<br></div>/tmp/test.cpp:8:5: error: use of overloaded operator '!=' is ambiguous (with operand types 'S' and 'S')<br> a != b;<br> ~ ^ ~<br>/tmp/test.cpp:2:8: note: candidate function (with reversed parameter order)<br> bool operator==(int rhs) const;<br> ^<br>/tmp/test.cpp:3:8: note: candidate function<br> bool operator!=(int rhs) const;<br> ^<br>/tmp/test.cpp:2:8: note: candidate function<br> bool operator==(int rhs) const;<br> ^<br><div><br></div><div>It looks like this was intentional, but I wanted to make sure. The code pattern isn't very common (I see only one instance in Android), but I figured that you might want to be aware of it in case it would inform any changes to the language.</div></div></div></blockquote><div><br></div><div>Thanks. Yes, this C++20 change is known to break some (hopefully mostly rare) code patterns. I'd not seen the one above before, but I think that's moderately likely to stay broken in C++20 onwards; the ambiguity between non-reversed and reversed != seems real here (note that C++17 wouldn't have allowed 0 != a, but C++20 does, which is why this became ambiguous). I'll ask on the committee reflectors just in case.</div></div></div></blockquote><div><br></div><div>Got it, thanks for the information. </div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div class="gmail_quote"><div>Hopefully you're only seeing a build break under -std=c++2a? (If not, that'd be a problem.)</div></div></div></blockquote><div><br></div><div>Yes, I'm only seeing this under -std=c++2a.</div><div><br></div><div>Peter </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div class="gmail_quote"><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div class="gmail_quote"><div>Peter</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
Added:<br>
cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp<br>
cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p8-2a.cpp<br>
cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p9-2a.cpp<br>
cfe/trunk/test/CodeGenCXX/mangle-cxx2a.cpp<br>
Modified:<br>
cfe/trunk/include/clang/AST/ExprCXX.h<br>
cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td<br>
cfe/trunk/include/clang/Basic/OperatorKinds.h<br>
cfe/trunk/include/clang/Sema/Overload.h<br>
cfe/trunk/include/clang/Sema/Sema.h<br>
cfe/trunk/lib/AST/ExprCXX.cpp<br>
cfe/trunk/lib/Frontend/FrontendActions.cpp<br>
cfe/trunk/lib/Sema/SemaExpr.cpp<br>
cfe/trunk/lib/Sema/SemaOverload.cpp<br>
cfe/trunk/lib/Sema/SemaTemplate.cpp<br>
cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp<br>
cfe/trunk/lib/Sema/TreeTransform.h<br>
cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/p7.cpp<br>
cfe/trunk/test/PCH/cxx2a-compare.cpp<br>
cfe/trunk/test/SemaCXX/compare-cxx2a.cpp<br>
cfe/trunk/test/SemaCXX/self-comparison.cpp<br>
cfe/trunk/www/cxx_status.html<br>
<br>
Modified: cfe/trunk/include/clang/AST/ExprCXX.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ExprCXX.h?rev=375306&r1=375305&r2=375306&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ExprCXX.h?rev=375306&r1=375305&r2=375306&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/include/clang/AST/ExprCXX.h (original)<br>
+++ cfe/trunk/include/clang/AST/ExprCXX.h Fri Oct 18 17:04:43 2019<br>
@@ -220,6 +220,40 @@ public:<br>
}<br>
};<br>
<br>
+/// Represents a call to a CUDA kernel function.<br>
+class CUDAKernelCallExpr final : public CallExpr {<br>
+ friend class ASTStmtReader;<br>
+<br>
+ enum { CONFIG, END_PREARG };<br>
+<br>
+ // CUDAKernelCallExpr has some trailing objects belonging<br>
+ // to CallExpr. See CallExpr for the details.<br>
+<br>
+ CUDAKernelCallExpr(Expr *Fn, CallExpr *Config, ArrayRef<Expr *> Args,<br>
+ QualType Ty, ExprValueKind VK, SourceLocation RP,<br>
+ unsigned MinNumArgs);<br>
+<br>
+ CUDAKernelCallExpr(unsigned NumArgs, EmptyShell Empty);<br>
+<br>
+public:<br>
+ static CUDAKernelCallExpr *Create(const ASTContext &Ctx, Expr *Fn,<br>
+ CallExpr *Config, ArrayRef<Expr *> Args,<br>
+ QualType Ty, ExprValueKind VK,<br>
+ SourceLocation RP, unsigned MinNumArgs = 0);<br>
+<br>
+ static CUDAKernelCallExpr *CreateEmpty(const ASTContext &Ctx,<br>
+ unsigned NumArgs, EmptyShell Empty);<br>
+<br>
+ const CallExpr *getConfig() const {<br>
+ return cast_or_null<CallExpr>(getPreArg(CONFIG));<br>
+ }<br>
+ CallExpr *getConfig() { return cast_or_null<CallExpr>(getPreArg(CONFIG)); }<br>
+<br>
+ static bool classof(const Stmt *T) {<br>
+ return T->getStmtClass() == CUDAKernelCallExprClass;<br>
+ }<br>
+};<br>
+<br>
/// A rewritten comparison expression that was originally written using<br>
/// operator syntax.<br>
///<br>
@@ -310,40 +344,6 @@ public:<br>
}<br>
};<br>
<br>
-/// Represents a call to a CUDA kernel function.<br>
-class CUDAKernelCallExpr final : public CallExpr {<br>
- friend class ASTStmtReader;<br>
-<br>
- enum { CONFIG, END_PREARG };<br>
-<br>
- // CUDAKernelCallExpr has some trailing objects belonging<br>
- // to CallExpr. See CallExpr for the details.<br>
-<br>
- CUDAKernelCallExpr(Expr *Fn, CallExpr *Config, ArrayRef<Expr *> Args,<br>
- QualType Ty, ExprValueKind VK, SourceLocation RP,<br>
- unsigned MinNumArgs);<br>
-<br>
- CUDAKernelCallExpr(unsigned NumArgs, EmptyShell Empty);<br>
-<br>
-public:<br>
- static CUDAKernelCallExpr *Create(const ASTContext &Ctx, Expr *Fn,<br>
- CallExpr *Config, ArrayRef<Expr *> Args,<br>
- QualType Ty, ExprValueKind VK,<br>
- SourceLocation RP, unsigned MinNumArgs = 0);<br>
-<br>
- static CUDAKernelCallExpr *CreateEmpty(const ASTContext &Ctx,<br>
- unsigned NumArgs, EmptyShell Empty);<br>
-<br>
- const CallExpr *getConfig() const {<br>
- return cast_or_null<CallExpr>(getPreArg(CONFIG));<br>
- }<br>
- CallExpr *getConfig() { return cast_or_null<CallExpr>(getPreArg(CONFIG)); }<br>
-<br>
- static bool classof(const Stmt *T) {<br>
- return T->getStmtClass() == CUDAKernelCallExprClass;<br>
- }<br>
-};<br>
-<br>
/// Abstract class common to all of the C++ "named"/"keyword" casts.<br>
///<br>
/// This abstract class is inherited by all of the classes<br>
<br>
Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=375306&r1=375305&r2=375306&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=375306&r1=375305&r2=375306&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)<br>
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Fri Oct 18 17:04:43 2019<br>
@@ -3769,7 +3769,8 @@ def note_ovl_too_many_candidates : Note<<br>
"pass -fshow-overloads=all to show them">;<br>
<br>
def select_ovl_candidate_kind : TextSubstitution<<br>
- "%select{function|function|constructor|"<br>
+ "%select{function|function|function (with reversed parameter order)|"<br>
+ "constructor|"<br>
"constructor (the implicit default constructor)|"<br>
"constructor (the implicit copy constructor)|"<br>
"constructor (the implicit move constructor)|"<br>
@@ -3990,6 +3991,13 @@ def err_ovl_ambiguous_oper_unary : Error<br>
"use of overloaded operator '%0' is ambiguous (operand type %1)">;<br>
def err_ovl_ambiguous_oper_binary : Error<<br>
"use of overloaded operator '%0' is ambiguous (with operand types %1 and %2)">;<br>
+def ext_ovl_ambiguous_oper_binary_reversed : ExtWarn<<br>
+ "ISO C++20 considers use of overloaded operator '%0' (with operand types %1 "<br>
+ "and %2) to be ambiguous despite there being a unique best viable function">,<br>
+ InGroup<DiagGroup<"ambiguous-reversed-operator">>, SFINAEFailure;<br>
+def note_ovl_ambiguous_oper_binary_reversed_candidate : Note<<br>
+ "ambiguity is between a regular call to this operator and a call with the "<br>
+ "argument order reversed">;<br>
def err_ovl_no_viable_oper : Error<"no viable overloaded '%0'">;<br>
def note_assign_lhs_incomplete : Note<"type %0 is incomplete">;<br>
def err_ovl_deleted_oper : Error<<br>
@@ -3997,6 +4005,9 @@ def err_ovl_deleted_oper : Error<<br>
def err_ovl_deleted_special_oper : Error<<br>
"object of type %0 cannot be %select{constructed|copied|moved|assigned|"<br>
"assigned|destroyed}1 because its %sub{select_special_member_kind}1 is implicitly deleted">;<br>
+def err_ovl_rewrite_equalequal_not_bool : Error<<br>
+ "return type %0 of selected 'operator==' function for rewritten "<br>
+ "'%1' comparison is not 'bool'">;<br>
def err_ovl_no_viable_subscript :<br>
Error<"no viable overloaded operator[] for type %0">;<br>
def err_ovl_no_oper :<br>
@@ -9961,6 +9972,8 @@ def err_std_compare_type_not_supported :<br>
"member '%2' is missing|"<br>
"the type is not trivially copyable|"<br>
"the type does not have the expected form}1">;<br>
+def note_rewriting_operator_as_spaceship : Note<<br>
+ "while rewriting comparison as call to 'operator<=>' declared here">;<br>
<br>
// Memory Tagging Extensions (MTE) diagnostics<br>
def err_memtag_arg_null_or_pointer : Error<<br>
<br>
Modified: cfe/trunk/include/clang/Basic/OperatorKinds.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/OperatorKinds.h?rev=375306&r1=375305&r2=375306&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/OperatorKinds.h?rev=375306&r1=375305&r2=375306&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/include/clang/Basic/OperatorKinds.h (original)<br>
+++ cfe/trunk/include/clang/Basic/OperatorKinds.h Fri Oct 18 17:04:43 2019<br>
@@ -30,6 +30,25 @@ enum OverloadedOperatorKind : int {<br>
/// the preceding "operator" keyword.<br>
const char *getOperatorSpelling(OverloadedOperatorKind Operator);<br>
<br>
+/// Get the other overloaded operator that the given operator can be rewritten<br>
+/// into, if any such operator exists.<br>
+inline OverloadedOperatorKind<br>
+getRewrittenOverloadedOperator(OverloadedOperatorKind Kind) {<br>
+ switch (Kind) {<br>
+ case OO_Less:<br>
+ case OO_LessEqual:<br>
+ case OO_Greater:<br>
+ case OO_GreaterEqual:<br>
+ return OO_Spaceship;<br>
+<br>
+ case OO_ExclaimEqual:<br>
+ return OO_EqualEqual;<br>
+<br>
+ default:<br>
+ return OO_None;<br>
+ }<br>
+}<br>
+<br>
} // end namespace clang<br>
<br>
#endif<br>
<br>
Modified: cfe/trunk/include/clang/Sema/Overload.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Overload.h?rev=375306&r1=375305&r2=375306&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Overload.h?rev=375306&r1=375305&r2=375306&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/include/clang/Sema/Overload.h (original)<br>
+++ cfe/trunk/include/clang/Sema/Overload.h Fri Oct 18 17:04:43 2019<br>
@@ -71,6 +71,30 @@ class Sema;<br>
OCD_ViableCandidates<br>
};<br>
<br>
+ /// The parameter ordering that will be used for the candidate. This is<br>
+ /// used to represent C++20 binary operator rewrites that reverse the order<br>
+ /// of the arguments. If the parameter ordering is Reversed, the Args list is<br>
+ /// reversed (but obviously the ParamDecls for the function are not).<br>
+ ///<br>
+ /// After forming an OverloadCandidate with reversed parameters, the list<br>
+ /// of conversions will (as always) be indexed by argument, so will be<br>
+ /// in reverse parameter order.<br>
+ enum class OverloadCandidateParamOrder : char { Normal, Reversed };<br>
+<br>
+ /// The kinds of rewrite we perform on overload candidates. Note that the<br>
+ /// values here are chosen to serve as both bitflags and as a rank (lower<br>
+ /// values are preferred by overload resolution).<br>
+ enum OverloadCandidateRewriteKind : unsigned {<br>
+ /// Candidate is not a rewritten candidate.<br>
+ CRK_None = 0x0,<br>
+<br>
+ /// Candidate is a rewritten candidate with a different operator name.<br>
+ CRK_DifferentOperator = 0x1,<br>
+<br>
+ /// Candidate is a rewritten candidate with a reversed order of parameters.<br>
+ CRK_Reversed = 0x2,<br>
+ };<br>
+<br>
/// ImplicitConversionKind - The kind of implicit conversion used to<br>
/// convert an argument to a parameter's type. The enumerator values<br>
/// match with the table titled 'Conversions' in [over.ics.scs] and are listed<br>
@@ -757,7 +781,8 @@ class Sema;<br>
CXXConversionDecl *Surrogate;<br>
<br>
/// The conversion sequences used to convert the function arguments<br>
- /// to the function parameters.<br>
+ /// to the function parameters. Note that these are indexed by argument,<br>
+ /// so may not match the parameter order of Function.<br>
ConversionSequenceList Conversions;<br>
<br>
/// The FixIt hints which can be used to fix the Bad candidate.<br>
@@ -783,6 +808,9 @@ class Sema;<br>
/// True if the candidate was found using ADL.<br>
CallExpr::ADLCallKind IsADLCandidate : 1;<br>
<br>
+ /// Whether this is a rewritten candidate, and if so, of what kind?<br>
+ OverloadCandidateRewriteKind RewriteKind : 2;<br>
+<br>
/// FailureKind - The reason why this candidate is not viable.<br>
/// Actually an OverloadFailureKind.<br>
unsigned char FailureKind;<br>
@@ -838,7 +866,8 @@ class Sema;<br>
<br>
private:<br>
friend class OverloadCandidateSet;<br>
- OverloadCandidate() : IsADLCandidate(CallExpr::NotADL) {}<br>
+ OverloadCandidate()<br>
+ : IsADLCandidate(CallExpr::NotADL), RewriteKind(CRK_None) {}<br>
};<br>
<br>
/// OverloadCandidateSet - A set of overload candidates, used in C++<br>
@@ -867,9 +896,54 @@ class Sema;<br>
CSK_InitByConstructor,<br>
};<br>
<br>
+ /// Information about operator rewrites to consider when adding operator<br>
+ /// functions to a candidate set.<br>
+ struct OperatorRewriteInfo {<br>
+ OperatorRewriteInfo()<br>
+ : OriginalOperator(OO_None), AllowRewrittenCandidates(false) {}<br>
+ OperatorRewriteInfo(OverloadedOperatorKind Op, bool AllowRewritten)<br>
+ : OriginalOperator(Op), AllowRewrittenCandidates(AllowRewritten) {}<br>
+<br>
+ /// The original operator as written in the source.<br>
+ OverloadedOperatorKind OriginalOperator;<br>
+ /// Whether we should include rewritten candidates in the overload set.<br>
+ bool AllowRewrittenCandidates;<br>
+<br>
+ /// Would use of this function result in a rewrite using a different<br>
+ /// operator?<br>
+ bool isRewrittenOperator(const FunctionDecl *FD) {<br>
+ return OriginalOperator &&<br>
+ FD->getDeclName().getCXXOverloadedOperator() != OriginalOperator;<br>
+ }<br>
+<br>
+ bool isAcceptableCandidate(const FunctionDecl *FD) {<br>
+ return AllowRewrittenCandidates || !isRewrittenOperator(FD);<br>
+ }<br>
+<br>
+ /// Determine the kind of rewrite that should be performed for this<br>
+ /// candidate.<br>
+ OverloadCandidateRewriteKind<br>
+ getRewriteKind(const FunctionDecl *FD, OverloadCandidateParamOrder PO) {<br>
+ OverloadCandidateRewriteKind CRK = CRK_None;<br>
+ if (isRewrittenOperator(FD))<br>
+ CRK = OverloadCandidateRewriteKind(CRK | CRK_DifferentOperator);<br>
+ if (PO == OverloadCandidateParamOrder::Reversed)<br>
+ CRK = OverloadCandidateRewriteKind(CRK | CRK_Reversed);<br>
+ return CRK;<br>
+ }<br>
+<br>
+ /// Determine whether we should consider looking for and adding reversed<br>
+ /// candidates for operator Op.<br>
+ bool shouldAddReversed(OverloadedOperatorKind Op);<br>
+<br>
+ /// Determine whether we should add a rewritten candidate for \p FD with<br>
+ /// reversed parameter order.<br>
+ bool shouldAddReversed(ASTContext &Ctx, const FunctionDecl *FD);<br>
+ };<br>
+<br>
private:<br>
SmallVector<OverloadCandidate, 16> Candidates;<br>
- llvm::SmallPtrSet<Decl *, 16> Functions;<br>
+ llvm::SmallPtrSet<uintptr_t, 16> Functions;<br>
<br>
// Allocator for ConversionSequenceLists. We store the first few of these<br>
// inline to avoid allocation for small sets.<br>
@@ -877,6 +951,7 @@ class Sema;<br>
<br>
SourceLocation Loc;<br>
CandidateSetKind Kind;<br>
+ OperatorRewriteInfo RewriteInfo;<br>
<br>
constexpr static unsigned NumInlineBytes =<br>
24 * sizeof(ImplicitConversionSequence);<br>
@@ -915,19 +990,24 @@ class Sema;<br>
void destroyCandidates();<br>
<br>
public:<br>
- OverloadCandidateSet(SourceLocation Loc, CandidateSetKind CSK)<br>
- : Loc(Loc), Kind(CSK) {}<br>
+ OverloadCandidateSet(SourceLocation Loc, CandidateSetKind CSK,<br>
+ OperatorRewriteInfo RewriteInfo = {})<br>
+ : Loc(Loc), Kind(CSK), RewriteInfo(RewriteInfo) {}<br>
OverloadCandidateSet(const OverloadCandidateSet &) = delete;<br>
OverloadCandidateSet &operator=(const OverloadCandidateSet &) = delete;<br>
~OverloadCandidateSet() { destroyCandidates(); }<br>
<br>
SourceLocation getLocation() const { return Loc; }<br>
CandidateSetKind getKind() const { return Kind; }<br>
+ OperatorRewriteInfo getRewriteInfo() const { return RewriteInfo; }<br>
<br>
/// Determine when this overload candidate will be new to the<br>
/// overload set.<br>
- bool isNewCandidate(Decl *F) {<br>
- return Functions.insert(F->getCanonicalDecl()).second;<br>
+ bool isNewCandidate(Decl *F, OverloadCandidateParamOrder PO =<br>
+ OverloadCandidateParamOrder::Normal) {<br>
+ uintptr_t Key = reinterpret_cast<uintptr_t>(F->getCanonicalDecl());<br>
+ Key |= static_cast<uintptr_t>(PO);<br>
+ return Functions.insert(Key).second;<br>
}<br>
<br>
/// Clear out all of the candidates.<br>
<br>
Modified: cfe/trunk/include/clang/Sema/Sema.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=375306&r1=375305&r2=375306&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=375306&r1=375305&r2=375306&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/include/clang/Sema/Sema.h (original)<br>
+++ cfe/trunk/include/clang/Sema/Sema.h Fri Oct 18 17:04:43 2019<br>
@@ -159,6 +159,8 @@ namespace clang {<br>
class OMPClause;<br>
struct OMPVarListLocTy;<br>
struct OverloadCandidate;<br>
+ enum class OverloadCandidateParamOrder : char;<br>
+ enum OverloadCandidateRewriteKind : unsigned;<br>
class OverloadCandidateSet;<br>
class OverloadExpr;<br>
class ParenListExpr;<br>
@@ -3019,7 +3021,8 @@ public:<br>
bool AllowExplicit = true,<br>
bool AllowExplicitConversion = false,<br>
ADLCallKind IsADLCandidate = ADLCallKind::NotADL,<br>
- ConversionSequenceList EarlyConversions = None);<br>
+ ConversionSequenceList EarlyConversions = None,<br>
+ OverloadCandidateParamOrder PO = {});<br>
void AddFunctionCandidates(const UnresolvedSetImpl &Functions,<br>
ArrayRef<Expr *> Args,<br>
OverloadCandidateSet &CandidateSet,<br>
@@ -3032,7 +3035,8 @@ public:<br>
Expr::Classification ObjectClassification,<br>
ArrayRef<Expr *> Args,<br>
OverloadCandidateSet& CandidateSet,<br>
- bool SuppressUserConversion = false);<br>
+ bool SuppressUserConversion = false,<br>
+ OverloadCandidateParamOrder PO = {});<br>
void AddMethodCandidate(CXXMethodDecl *Method,<br>
DeclAccessPair FoundDecl,<br>
CXXRecordDecl *ActingContext, QualType ObjectType,<br>
@@ -3041,7 +3045,8 @@ public:<br>
OverloadCandidateSet& CandidateSet,<br>
bool SuppressUserConversions = false,<br>
bool PartialOverloading = false,<br>
- ConversionSequenceList EarlyConversions = None);<br>
+ ConversionSequenceList EarlyConversions = None,<br>
+ OverloadCandidateParamOrder PO = {});<br>
void AddMethodTemplateCandidate(FunctionTemplateDecl *MethodTmpl,<br>
DeclAccessPair FoundDecl,<br>
CXXRecordDecl *ActingContext,<br>
@@ -3051,23 +3056,22 @@ public:<br>
ArrayRef<Expr *> Args,<br>
OverloadCandidateSet& CandidateSet,<br>
bool SuppressUserConversions = false,<br>
- bool PartialOverloading = false);<br>
+ bool PartialOverloading = false,<br>
+ OverloadCandidateParamOrder PO = {});<br>
void AddTemplateOverloadCandidate(<br>
FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,<br>
TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *> Args,<br>
OverloadCandidateSet &CandidateSet, bool SuppressUserConversions = false,<br>
bool PartialOverloading = false, bool AllowExplicit = true,<br>
- ADLCallKind IsADLCandidate = ADLCallKind::NotADL);<br>
- bool CheckNonDependentConversions(FunctionTemplateDecl *FunctionTemplate,<br>
- ArrayRef<QualType> ParamTypes,<br>
- ArrayRef<Expr *> Args,<br>
- OverloadCandidateSet &CandidateSet,<br>
- ConversionSequenceList &Conversions,<br>
- bool SuppressUserConversions,<br>
- CXXRecordDecl *ActingContext = nullptr,<br>
- QualType ObjectType = QualType(),<br>
- Expr::Classification<br>
- ObjectClassification = {});<br>
+ ADLCallKind IsADLCandidate = ADLCallKind::NotADL,<br>
+ OverloadCandidateParamOrder PO = {});<br>
+ bool CheckNonDependentConversions(<br>
+ FunctionTemplateDecl *FunctionTemplate, ArrayRef<QualType> ParamTypes,<br>
+ ArrayRef<Expr *> Args, OverloadCandidateSet &CandidateSet,<br>
+ ConversionSequenceList &Conversions, bool SuppressUserConversions,<br>
+ CXXRecordDecl *ActingContext = nullptr, QualType ObjectType = QualType(),<br>
+ Expr::Classification ObjectClassification = {},<br>
+ OverloadCandidateParamOrder PO = {});<br>
void AddConversionCandidate(<br>
CXXConversionDecl *Conversion, DeclAccessPair FoundDecl,<br>
CXXRecordDecl *ActingContext, Expr *From, QualType ToType,<br>
@@ -3084,10 +3088,14 @@ public:<br>
const FunctionProtoType *Proto,<br>
Expr *Object, ArrayRef<Expr *> Args,<br>
OverloadCandidateSet& CandidateSet);<br>
+ void AddNonMemberOperatorCandidates(<br>
+ const UnresolvedSetImpl &Functions, ArrayRef<Expr *> Args,<br>
+ OverloadCandidateSet &CandidateSet,<br>
+ TemplateArgumentListInfo *ExplicitTemplateArgs = nullptr);<br>
void AddMemberOperatorCandidates(OverloadedOperatorKind Op,<br>
SourceLocation OpLoc, ArrayRef<Expr *> Args,<br>
- OverloadCandidateSet& CandidateSet,<br>
- SourceRange OpRange = SourceRange());<br>
+ OverloadCandidateSet &CandidateSet,<br>
+ OverloadCandidateParamOrder PO = {});<br>
void AddBuiltinCandidate(QualType *ParamTys, ArrayRef<Expr *> Args,<br>
OverloadCandidateSet& CandidateSet,<br>
bool IsAssignmentOperator = false,<br>
@@ -3103,9 +3111,10 @@ public:<br>
bool PartialOverloading = false);<br>
<br>
// Emit as a 'note' the specific overload candidate<br>
- void NoteOverloadCandidate(NamedDecl *Found, FunctionDecl *Fn,<br>
- QualType DestType = QualType(),<br>
- bool TakingAddress = false);<br>
+ void NoteOverloadCandidate(<br>
+ NamedDecl *Found, FunctionDecl *Fn,<br>
+ OverloadCandidateRewriteKind RewriteKind = OverloadCandidateRewriteKind(),<br>
+ QualType DestType = QualType(), bool TakingAddress = false);<br>
<br>
// Emit as a series of 'note's all template and non-templates identified by<br>
// the expression Expr<br>
@@ -3237,7 +3246,8 @@ public:<br>
BinaryOperatorKind Opc,<br>
const UnresolvedSetImpl &Fns,<br>
Expr *LHS, Expr *RHS,<br>
- bool RequiresADL = true);<br>
+ bool RequiresADL = true,<br>
+ bool AllowRewrittenCandidates = true);<br>
<br>
ExprResult CreateOverloadedArraySubscriptExpr(SourceLocation LLoc,<br>
SourceLocation RLoc,<br>
@@ -7665,6 +7675,9 @@ public:<br>
// We are substituting template arguments into a constraint expression.<br>
ConstraintSubstitution,<br>
<br>
+ /// We are rewriting a comparison operator in terms of an operator<=>.<br>
+ RewritingOperatorAsSpaceship,<br>
+<br>
/// Added for Template instantiation observation.<br>
/// Memoization means we are _not_ instantiating a template because<br>
/// it is already instantiated (but we entered a context where we<br>
<br>
Modified: cfe/trunk/lib/AST/ExprCXX.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprCXX.cpp?rev=375306&r1=375305&r2=375306&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprCXX.cpp?rev=375306&r1=375305&r2=375306&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/AST/ExprCXX.cpp (original)<br>
+++ cfe/trunk/lib/AST/ExprCXX.cpp Fri Oct 18 17:04:43 2019<br>
@@ -79,7 +79,7 @@ CXXRewrittenBinaryOperator::getDecompose<br>
Result.RHS = BO->getRHS();<br>
Result.InnerBinOp = BO;<br>
} else if (auto *BO = dyn_cast<CXXOperatorCallExpr>(E)) {<br>
- assert(!SkippedNot || BO->getOperator() == OO_Equal);<br>
+ assert(!SkippedNot || BO->getOperator() == OO_EqualEqual);<br>
assert(BO->isInfixBinaryOp());<br>
switch (BO->getOperator()) {<br>
case OO_Less: Result.Opcode = BO_LT; break;<br>
@@ -107,7 +107,7 @@ CXXRewrittenBinaryOperator::getDecompose<br>
return Result;<br>
<br>
// Otherwise, we expect a <=> to now be on the LHS.<br>
- E = Result.InnerBinOp->IgnoreImplicit();<br>
+ E = Result.LHS->IgnoreImplicit();<br>
if (auto *BO = dyn_cast<BinaryOperator>(E)) {<br>
assert(BO->getOpcode() == BO_Cmp);<br>
Result.LHS = BO->getLHS();<br>
<br>
Modified: cfe/trunk/lib/Frontend/FrontendActions.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/FrontendActions.cpp?rev=375306&r1=375305&r2=375306&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/FrontendActions.cpp?rev=375306&r1=375305&r2=375306&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/Frontend/FrontendActions.cpp (original)<br>
+++ cfe/trunk/lib/Frontend/FrontendActions.cpp Fri Oct 18 17:04:43 2019<br>
@@ -415,6 +415,8 @@ private:<br>
return "DeclaringSpecialMember";<br>
case CodeSynthesisContext::DefiningSynthesizedFunction:<br>
return "DefiningSynthesizedFunction";<br>
+ case CodeSynthesisContext::RewritingOperatorAsSpaceship:<br>
+ return "RewritingOperatorAsSpaceship";<br>
case CodeSynthesisContext::Memoization:<br>
return "Memoization";<br>
case CodeSynthesisContext::ConstraintsCheck:<br>
<br>
Modified: cfe/trunk/lib/Sema/SemaExpr.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=375306&r1=375305&r2=375306&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=375306&r1=375305&r2=375306&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)<br>
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Fri Oct 18 17:04:43 2019<br>
@@ -13310,6 +13310,13 @@ static ExprResult BuildOverloadedBinOp(S<br>
S.LookupOverloadedOperatorName(OverOp, Sc, LHS->getType(),<br>
RHS->getType(), Functions);<br>
<br>
+ // In C++20 onwards, we may have a second operator to look up.<br>
+ if (S.getLangOpts().CPlusPlus2a) {<br>
+ if (OverloadedOperatorKind ExtraOp = getRewrittenOverloadedOperator(OverOp))<br>
+ S.LookupOverloadedOperatorName(ExtraOp, Sc, LHS->getType(),<br>
+ RHS->getType(), Functions);<br>
+ }<br>
+<br>
// Build the (potentially-overloaded, potentially-dependent)<br>
// binary operation.<br>
return S.CreateOverloadedBinOp(OpLoc, Opc, Functions, LHS, RHS);<br>
<br>
Modified: cfe/trunk/lib/Sema/SemaOverload.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaOverload.cpp?rev=375306&r1=375305&r2=375306&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaOverload.cpp?rev=375306&r1=375305&r2=375306&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/Sema/SemaOverload.cpp (original)<br>
+++ cfe/trunk/lib/Sema/SemaOverload.cpp Fri Oct 18 17:04:43 2019<br>
@@ -848,6 +848,25 @@ llvm::Optional<unsigned> DeductionFailur<br>
}<br>
}<br>
<br>
+bool OverloadCandidateSet::OperatorRewriteInfo::shouldAddReversed(<br>
+ OverloadedOperatorKind Op) {<br>
+ if (!AllowRewrittenCandidates)<br>
+ return false;<br>
+ return Op == OO_EqualEqual || Op == OO_Spaceship;<br>
+}<br>
+<br>
+bool OverloadCandidateSet::OperatorRewriteInfo::shouldAddReversed(<br>
+ ASTContext &Ctx, const FunctionDecl *FD) {<br>
+ if (!shouldAddReversed(FD->getDeclName().getCXXOverloadedOperator()))<br>
+ return false;<br>
+ // Don't bother adding a reversed candidate that can never be a better<br>
+ // match than the non-reversed version.<br>
+ return FD->getNumParams() != 2 ||<br>
+ !Ctx.hasSameUnqualifiedType(FD->getParamDecl(0)->getType(),<br>
+ FD->getParamDecl(1)->getType()) ||<br>
+ FD->hasAttr<EnableIfAttr>();<br>
+}<br>
+<br>
void OverloadCandidateSet::destroyCandidates() {<br>
for (iterator i = begin(), e = end(); i != e; ++i) {<br>
for (auto &C : i->Conversions)<br>
@@ -6056,7 +6075,8 @@ void Sema::AddOverloadCandidate(<br>
FunctionDecl *Function, DeclAccessPair FoundDecl, ArrayRef<Expr *> Args,<br>
OverloadCandidateSet &CandidateSet, bool SuppressUserConversions,<br>
bool PartialOverloading, bool AllowExplicit, bool AllowExplicitConversions,<br>
- ADLCallKind IsADLCandidate, ConversionSequenceList EarlyConversions) {<br>
+ ADLCallKind IsADLCandidate, ConversionSequenceList EarlyConversions,<br>
+ OverloadCandidateParamOrder PO) {<br>
const FunctionProtoType *Proto<br>
= dyn_cast<FunctionProtoType>(Function->getType()->getAs<FunctionType>());<br>
assert(Proto && "Functions without a prototype cannot be overloaded");<br>
@@ -6075,25 +6095,14 @@ void Sema::AddOverloadCandidate(<br>
AddMethodCandidate(Method, FoundDecl, Method->getParent(), QualType(),<br>
Expr::Classification::makeSimpleLValue(), Args,<br>
CandidateSet, SuppressUserConversions,<br>
- PartialOverloading, EarlyConversions);<br>
+ PartialOverloading, EarlyConversions, PO);<br>
return;<br>
}<br>
// We treat a constructor like a non-member function, since its object<br>
// argument doesn't participate in overload resolution.<br>
}<br>
<br>
- if (!CandidateSet.isNewCandidate(Function))<br>
- return;<br>
-<br>
- // C++ [over.match.oper]p3:<br>
- // if no operand has a class type, only those non-member functions in the<br>
- // lookup set that have a first parameter of type T1 or "reference to<br>
- // (possibly cv-qualified) T1", when T1 is an enumeration type, or (if there<br>
- // is a right operand) a second parameter of type T2 or "reference to<br>
- // (possibly cv-qualified) T2", when T2 is an enumeration type, are<br>
- // candidate functions.<br>
- if (CandidateSet.getKind() == OverloadCandidateSet::CSK_Operator &&<br>
- !IsAcceptableNonMemberOperatorCandidate(Context, Function, Args))<br>
+ if (!CandidateSet.isNewCandidate(Function, PO))<br>
return;<br>
<br>
// C++11 [class.copy]p11: [DR1402]<br>
@@ -6108,12 +6117,25 @@ void Sema::AddOverloadCandidate(<br>
EnterExpressionEvaluationContext Unevaluated(<br>
*this, Sema::ExpressionEvaluationContext::Unevaluated);<br>
<br>
+ // C++ [over.match.oper]p3:<br>
+ // if no operand has a class type, only those non-member functions in the<br>
+ // lookup set that have a first parameter of type T1 or "reference to<br>
+ // (possibly cv-qualified) T1", when T1 is an enumeration type, or (if there<br>
+ // is a right operand) a second parameter of type T2 or "reference to<br>
+ // (possibly cv-qualified) T2", when T2 is an enumeration type, are<br>
+ // candidate functions.<br>
+ if (CandidateSet.getKind() == OverloadCandidateSet::CSK_Operator &&<br>
+ !IsAcceptableNonMemberOperatorCandidate(Context, Function, Args))<br>
+ return;<br>
+<br>
// Add this candidate<br>
OverloadCandidate &Candidate =<br>
CandidateSet.addCandidate(Args.size(), EarlyConversions);<br>
Candidate.FoundDecl = FoundDecl;<br>
Candidate.Function = Function;<br>
Candidate.Viable = true;<br>
+ Candidate.RewriteKind =<br>
+ CandidateSet.getRewriteInfo().getRewriteKind(Function, PO);<br>
Candidate.IsSurrogate = false;<br>
Candidate.IsADLCandidate = IsADLCandidate;<br>
Candidate.IgnoreObjectArgument = false;<br>
@@ -6213,7 +6235,9 @@ void Sema::AddOverloadCandidate(<br>
// Determine the implicit conversion sequences for each of the<br>
// arguments.<br>
for (unsigned ArgIdx = 0; ArgIdx < Args.size(); ++ArgIdx) {<br>
- if (Candidate.Conversions[ArgIdx].isInitialized()) {<br>
+ unsigned ConvIdx =<br>
+ PO == OverloadCandidateParamOrder::Reversed ? 1 - ArgIdx : ArgIdx;<br>
+ if (Candidate.Conversions[ConvIdx].isInitialized()) {<br>
// We already formed a conversion sequence for this parameter during<br>
// template argument deduction.<br>
} else if (ArgIdx < NumParams) {<br>
@@ -6222,12 +6246,12 @@ void Sema::AddOverloadCandidate(<br>
// (13.3.3.1) that converts that argument to the corresponding<br>
// parameter of F.<br>
QualType ParamType = Proto->getParamType(ArgIdx);<br>
- Candidate.Conversions[ArgIdx] = TryCopyInitialization(<br>
+ Candidate.Conversions[ConvIdx] = TryCopyInitialization(<br>
*this, Args[ArgIdx], ParamType, SuppressUserConversions,<br>
/*InOverloadResolution=*/true,<br>
/*AllowObjCWritebackConversion=*/<br>
getLangOpts().ObjCAutoRefCount, AllowExplicitConversions);<br>
- if (Candidate.Conversions[ArgIdx].isBad()) {<br>
+ if (Candidate.Conversions[ConvIdx].isBad()) {<br>
Candidate.Viable = false;<br>
Candidate.FailureKind = ovl_fail_bad_conversion;<br>
return;<br>
@@ -6236,7 +6260,7 @@ void Sema::AddOverloadCandidate(<br>
// (C++ 13.3.2p2): For the purposes of overload resolution, any<br>
// argument for which there is no corresponding parameter is<br>
// considered to ""match the ellipsis" (C+ 13.3.3.1.3).<br>
- Candidate.Conversions[ArgIdx].setEllipsis();<br>
+ Candidate.Conversions[ConvIdx].setEllipsis();<br>
}<br>
}<br>
<br>
@@ -6583,9 +6607,10 @@ void Sema::AddFunctionCandidates(const U<br>
FunctionArgs = Args.slice(1);<br>
}<br>
if (FunTmpl) {<br>
- AddTemplateOverloadCandidate(<br>
- FunTmpl, F.getPair(), ExplicitTemplateArgs, FunctionArgs,<br>
- CandidateSet, SuppressUserConversions, PartialOverloading);<br>
+ AddTemplateOverloadCandidate(FunTmpl, F.getPair(),<br>
+ ExplicitTemplateArgs, FunctionArgs,<br>
+ CandidateSet, SuppressUserConversions,<br>
+ PartialOverloading);<br>
} else {<br>
AddOverloadCandidate(FD, F.getPair(), FunctionArgs, CandidateSet,<br>
SuppressUserConversions, PartialOverloading);<br>
@@ -6596,12 +6621,12 @@ void Sema::AddFunctionCandidates(const U<br>
<br>
/// AddMethodCandidate - Adds a named decl (which is some kind of<br>
/// method) as a method candidate to the given overload set.<br>
-void Sema::AddMethodCandidate(DeclAccessPair FoundDecl,<br>
- QualType ObjectType,<br>
+void Sema::AddMethodCandidate(DeclAccessPair FoundDecl, QualType ObjectType,<br>
Expr::Classification ObjectClassification,<br>
ArrayRef<Expr *> Args,<br>
- OverloadCandidateSet& CandidateSet,<br>
- bool SuppressUserConversions) {<br>
+ OverloadCandidateSet &CandidateSet,<br>
+ bool SuppressUserConversions,<br>
+ OverloadCandidateParamOrder PO) {<br>
NamedDecl *Decl = FoundDecl.getDecl();<br>
CXXRecordDecl *ActingContext = cast<CXXRecordDecl>(Decl->getDeclContext());<br>
<br>
@@ -6614,11 +6639,11 @@ void Sema::AddMethodCandidate(DeclAccess<br>
AddMethodTemplateCandidate(TD, FoundDecl, ActingContext,<br>
/*ExplicitArgs*/ nullptr, ObjectType,<br>
ObjectClassification, Args, CandidateSet,<br>
- SuppressUserConversions);<br>
+ SuppressUserConversions, false, PO);<br>
} else {<br>
AddMethodCandidate(cast<CXXMethodDecl>(Decl), FoundDecl, ActingContext,<br>
ObjectType, ObjectClassification, Args, CandidateSet,<br>
- SuppressUserConversions);<br>
+ SuppressUserConversions, false, None, PO);<br>
}<br>
}<br>
<br>
@@ -6637,14 +6662,15 @@ Sema::AddMethodCandidate(CXXMethodDecl *<br>
OverloadCandidateSet &CandidateSet,<br>
bool SuppressUserConversions,<br>
bool PartialOverloading,<br>
- ConversionSequenceList EarlyConversions) {<br>
+ ConversionSequenceList EarlyConversions,<br>
+ OverloadCandidateParamOrder PO) {<br>
const FunctionProtoType *Proto<br>
= dyn_cast<FunctionProtoType>(Method->getType()->getAs<FunctionType>());<br>
assert(Proto && "Methods without a prototype cannot be overloaded");<br>
assert(!isa<CXXConstructorDecl>(Method) &&<br>
"Use AddOverloadCandidate for constructors");<br>
<br>
- if (!CandidateSet.isNewCandidate(Method))<br>
+ if (!CandidateSet.isNewCandidate(Method, PO))<br>
return;<br>
<br>
// C++11 [class.copy]p23: [DR1402]<br>
@@ -6663,6 +6689,8 @@ Sema::AddMethodCandidate(CXXMethodDecl *<br>
CandidateSet.addCandidate(Args.size() + 1, EarlyConversions);<br>
Candidate.FoundDecl = FoundDecl;<br>
Candidate.Function = Method;<br>
+ Candidate.RewriteKind =<br>
+ CandidateSet.getRewriteInfo().getRewriteKind(Method, PO);<br>
Candidate.IsSurrogate = false;<br>
Candidate.IgnoreObjectArgument = false;<br>
Candidate.ExplicitCallArguments = Args.size();<br>
@@ -6698,12 +6726,13 @@ Sema::AddMethodCandidate(CXXMethodDecl *<br>
// The implicit object argument is ignored.<br>
Candidate.IgnoreObjectArgument = true;<br>
else {<br>
+ unsigned ConvIdx = PO == OverloadCandidateParamOrder::Reversed ? 1 : 0;<br>
// Determine the implicit conversion sequence for the object<br>
// parameter.<br>
- Candidate.Conversions[0] = TryObjectArgumentInitialization(<br>
+ Candidate.Conversions[ConvIdx] = TryObjectArgumentInitialization(<br>
*this, CandidateSet.getLocation(), ObjectType, ObjectClassification,<br>
Method, ActingContext);<br>
- if (Candidate.Conversions[0].isBad()) {<br>
+ if (Candidate.Conversions[ConvIdx].isBad()) {<br>
Candidate.Viable = false;<br>
Candidate.FailureKind = ovl_fail_bad_conversion;<br>
return;<br>
@@ -6722,7 +6751,9 @@ Sema::AddMethodCandidate(CXXMethodDecl *<br>
// Determine the implicit conversion sequences for each of the<br>
// arguments.<br>
for (unsigned ArgIdx = 0; ArgIdx < Args.size(); ++ArgIdx) {<br>
- if (Candidate.Conversions[ArgIdx + 1].isInitialized()) {<br>
+ unsigned ConvIdx =<br>
+ PO == OverloadCandidateParamOrder::Reversed ? 0 : (ArgIdx + 1);<br>
+ if (Candidate.Conversions[ConvIdx].isInitialized()) {<br>
// We already formed a conversion sequence for this parameter during<br>
// template argument deduction.<br>
} else if (ArgIdx < NumParams) {<br>
@@ -6731,13 +6762,13 @@ Sema::AddMethodCandidate(CXXMethodDecl *<br>
// (13.3.3.1) that converts that argument to the corresponding<br>
// parameter of F.<br>
QualType ParamType = Proto->getParamType(ArgIdx);<br>
- Candidate.Conversions[ArgIdx + 1]<br>
+ Candidate.Conversions[ConvIdx]<br>
= TryCopyInitialization(*this, Args[ArgIdx], ParamType,<br>
SuppressUserConversions,<br>
/*InOverloadResolution=*/true,<br>
/*AllowObjCWritebackConversion=*/<br>
getLangOpts().ObjCAutoRefCount);<br>
- if (Candidate.Conversions[ArgIdx + 1].isBad()) {<br>
+ if (Candidate.Conversions[ConvIdx].isBad()) {<br>
Candidate.Viable = false;<br>
Candidate.FailureKind = ovl_fail_bad_conversion;<br>
return;<br>
@@ -6746,7 +6777,7 @@ Sema::AddMethodCandidate(CXXMethodDecl *<br>
// (C++ 13.3.2p2): For the purposes of overload resolution, any<br>
// argument for which there is no corresponding parameter is<br>
// considered to "match the ellipsis" (C+ 13.3.3.1.3).<br>
- Candidate.Conversions[ArgIdx + 1].setEllipsis();<br>
+ Candidate.Conversions[ConvIdx].setEllipsis();<br>
}<br>
}<br>
<br>
@@ -6767,18 +6798,14 @@ Sema::AddMethodCandidate(CXXMethodDecl *<br>
/// Add a C++ member function template as a candidate to the candidate<br>
/// set, using template argument deduction to produce an appropriate member<br>
/// function template specialization.<br>
-void<br>
-Sema::AddMethodTemplateCandidate(FunctionTemplateDecl *MethodTmpl,<br>
- DeclAccessPair FoundDecl,<br>
- CXXRecordDecl *ActingContext,<br>
- TemplateArgumentListInfo *ExplicitTemplateArgs,<br>
- QualType ObjectType,<br>
- Expr::Classification ObjectClassification,<br>
- ArrayRef<Expr *> Args,<br>
- OverloadCandidateSet& CandidateSet,<br>
- bool SuppressUserConversions,<br>
- bool PartialOverloading) {<br>
- if (!CandidateSet.isNewCandidate(MethodTmpl))<br>
+void Sema::AddMethodTemplateCandidate(<br>
+ FunctionTemplateDecl *MethodTmpl, DeclAccessPair FoundDecl,<br>
+ CXXRecordDecl *ActingContext,<br>
+ TemplateArgumentListInfo *ExplicitTemplateArgs, QualType ObjectType,<br>
+ Expr::Classification ObjectClassification, ArrayRef<Expr *> Args,<br>
+ OverloadCandidateSet &CandidateSet, bool SuppressUserConversions,<br>
+ bool PartialOverloading, OverloadCandidateParamOrder PO) {<br>
+ if (!CandidateSet.isNewCandidate(MethodTmpl, PO))<br>
return;<br>
<br>
// C++ [over.match.funcs]p7:<br>
@@ -6799,13 +6826,15 @@ Sema::AddMethodTemplateCandidate(Functio<br>
return CheckNonDependentConversions(<br>
MethodTmpl, ParamTypes, Args, CandidateSet, Conversions,<br>
SuppressUserConversions, ActingContext, ObjectType,<br>
- ObjectClassification);<br>
+ ObjectClassification, PO);<br>
})) {<br>
OverloadCandidate &Candidate =<br>
CandidateSet.addCandidate(Conversions.size(), Conversions);<br>
Candidate.FoundDecl = FoundDecl;<br>
Candidate.Function = MethodTmpl->getTemplatedDecl();<br>
Candidate.Viable = false;<br>
+ Candidate.RewriteKind =<br>
+ CandidateSet.getRewriteInfo().getRewriteKind(Candidate.Function, PO);<br>
Candidate.IsSurrogate = false;<br>
Candidate.IgnoreObjectArgument =<br>
cast<CXXMethodDecl>(Candidate.Function)->isStatic() ||<br>
@@ -6829,7 +6858,7 @@ Sema::AddMethodTemplateCandidate(Functio<br>
AddMethodCandidate(cast<CXXMethodDecl>(Specialization), FoundDecl,<br>
ActingContext, ObjectType, ObjectClassification, Args,<br>
CandidateSet, SuppressUserConversions, PartialOverloading,<br>
- Conversions);<br>
+ Conversions, PO);<br>
}<br>
<br>
/// Add a C++ function template specialization as a candidate<br>
@@ -6839,8 +6868,9 @@ void Sema::AddTemplateOverloadCandidate(<br>
FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,<br>
TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *> Args,<br>
OverloadCandidateSet &CandidateSet, bool SuppressUserConversions,<br>
- bool PartialOverloading, bool AllowExplicit, ADLCallKind IsADLCandidate) {<br>
- if (!CandidateSet.isNewCandidate(FunctionTemplate))<br>
+ bool PartialOverloading, bool AllowExplicit, ADLCallKind IsADLCandidate,<br>
+ OverloadCandidateParamOrder PO) {<br>
+ if (!CandidateSet.isNewCandidate(FunctionTemplate, PO))<br>
return;<br>
<br>
// C++ [over.match.funcs]p7:<br>
@@ -6858,15 +6888,17 @@ void Sema::AddTemplateOverloadCandidate(<br>
if (TemplateDeductionResult Result = DeduceTemplateArguments(<br>
FunctionTemplate, ExplicitTemplateArgs, Args, Specialization, Info,<br>
PartialOverloading, [&](ArrayRef<QualType> ParamTypes) {<br>
- return CheckNonDependentConversions(FunctionTemplate, ParamTypes,<br>
- Args, CandidateSet, Conversions,<br>
- SuppressUserConversions);<br>
+ return CheckNonDependentConversions(<br>
+ FunctionTemplate, ParamTypes, Args, CandidateSet, Conversions,<br>
+ SuppressUserConversions, nullptr, QualType(), {}, PO);<br>
})) {<br>
OverloadCandidate &Candidate =<br>
CandidateSet.addCandidate(Conversions.size(), Conversions);<br>
Candidate.FoundDecl = FoundDecl;<br>
Candidate.Function = FunctionTemplate->getTemplatedDecl();<br>
Candidate.Viable = false;<br>
+ Candidate.RewriteKind =<br>
+ CandidateSet.getRewriteInfo().getRewriteKind(Candidate.Function, PO);<br>
Candidate.IsSurrogate = false;<br>
Candidate.IsADLCandidate = IsADLCandidate;<br>
// Ignore the object argument if there is one, since we don't have an object<br>
@@ -6891,7 +6923,7 @@ void Sema::AddTemplateOverloadCandidate(<br>
AddOverloadCandidate(<br>
Specialization, FoundDecl, Args, CandidateSet, SuppressUserConversions,<br>
PartialOverloading, AllowExplicit,<br>
- /*AllowExplicitConversions*/ false, IsADLCandidate, Conversions);<br>
+ /*AllowExplicitConversions*/ false, IsADLCandidate, Conversions, PO);<br>
}<br>
<br>
/// Check that implicit conversion sequences can be formed for each argument<br>
@@ -6902,7 +6934,7 @@ bool Sema::CheckNonDependentConversions(<br>
ArrayRef<Expr *> Args, OverloadCandidateSet &CandidateSet,<br>
ConversionSequenceList &Conversions, bool SuppressUserConversions,<br>
CXXRecordDecl *ActingContext, QualType ObjectType,<br>
- Expr::Classification ObjectClassification) {<br>
+ Expr::Classification ObjectClassification, OverloadCandidateParamOrder PO) {<br>
// FIXME: The cases in which we allow explicit conversions for constructor<br>
// arguments never consider calling a constructor template. It's not clear<br>
// that is correct.<br>
@@ -6925,10 +6957,11 @@ bool Sema::CheckNonDependentConversions(<br>
// overload resolution is permitted to sidestep instantiations.<br>
if (HasThisConversion && !cast<CXXMethodDecl>(FD)->isStatic() &&<br>
!ObjectType.isNull()) {<br>
- Conversions[0] = TryObjectArgumentInitialization(<br>
+ unsigned ConvIdx = PO == OverloadCandidateParamOrder::Reversed ? 1 : 0;<br>
+ Conversions[ConvIdx] = TryObjectArgumentInitialization(<br>
*this, CandidateSet.getLocation(), ObjectType, ObjectClassification,<br>
Method, ActingContext);<br>
- if (Conversions[0].isBad())<br>
+ if (Conversions[ConvIdx].isBad())<br>
return true;<br>
}<br>
<br>
@@ -6936,14 +6969,17 @@ bool Sema::CheckNonDependentConversions(<br>
++I) {<br>
QualType ParamType = ParamTypes[I];<br>
if (!ParamType->isDependentType()) {<br>
- Conversions[ThisConversions + I]<br>
+ unsigned ConvIdx = PO == OverloadCandidateParamOrder::Reversed<br>
+ ? 0<br>
+ : (ThisConversions + I);<br>
+ Conversions[ConvIdx]<br>
= TryCopyInitialization(*this, Args[I], ParamType,<br>
SuppressUserConversions,<br>
/*InOverloadResolution=*/true,<br>
/*AllowObjCWritebackConversion=*/<br>
getLangOpts().ObjCAutoRefCount,<br>
AllowExplicit);<br>
- if (Conversions[ThisConversions + I].isBad())<br>
+ if (Conversions[ConvIdx].isBad())<br>
return true;<br>
}<br>
}<br>
@@ -7331,6 +7367,48 @@ void Sema::AddSurrogateCandidate(CXXConv<br>
}<br>
}<br>
<br>
+/// Add all of the non-member operator function declarations in the given<br>
+/// function set to the overload candidate set.<br>
+void Sema::AddNonMemberOperatorCandidates(<br>
+ const UnresolvedSetImpl &Fns, ArrayRef<Expr *> Args,<br>
+ OverloadCandidateSet &CandidateSet,<br>
+ TemplateArgumentListInfo *ExplicitTemplateArgs) {<br>
+ for (UnresolvedSetIterator F = Fns.begin(), E = Fns.end(); F != E; ++F) {<br>
+ NamedDecl *D = F.getDecl()->getUnderlyingDecl();<br>
+ ArrayRef<Expr *> FunctionArgs = Args;<br>
+<br>
+ FunctionTemplateDecl *FunTmpl = dyn_cast<FunctionTemplateDecl>(D);<br>
+ FunctionDecl *FD =<br>
+ FunTmpl ? FunTmpl->getTemplatedDecl() : cast<FunctionDecl>(D);<br>
+<br>
+ // Don't consider rewritten functions if we're not rewriting.<br>
+ if (!CandidateSet.getRewriteInfo().isAcceptableCandidate(FD))<br>
+ continue;<br>
+<br>
+ assert(!isa<CXXMethodDecl>(FD) &&<br>
+ "unqualified operator lookup found a member function");<br>
+<br>
+ if (FunTmpl) {<br>
+ AddTemplateOverloadCandidate(FunTmpl, F.getPair(), ExplicitTemplateArgs,<br>
+ FunctionArgs, CandidateSet);<br>
+ if (CandidateSet.getRewriteInfo().shouldAddReversed(Context, FD))<br>
+ AddTemplateOverloadCandidate(<br>
+ FunTmpl, F.getPair(), ExplicitTemplateArgs,<br>
+ {FunctionArgs[1], FunctionArgs[0]}, CandidateSet, false, false,<br>
+ true, ADLCallKind::NotADL, OverloadCandidateParamOrder::Reversed);<br>
+ } else {<br>
+ if (ExplicitTemplateArgs)<br>
+ continue;<br>
+ AddOverloadCandidate(FD, F.getPair(), FunctionArgs, CandidateSet);<br>
+ if (CandidateSet.getRewriteInfo().shouldAddReversed(Context, FD))<br>
+ AddOverloadCandidate(FD, F.getPair(),<br>
+ {FunctionArgs[1], FunctionArgs[0]}, CandidateSet,<br>
+ false, false, true, false, ADLCallKind::NotADL,<br>
+ None, OverloadCandidateParamOrder::Reversed);<br>
+ }<br>
+ }<br>
+}<br>
+<br>
/// Add overload candidates for overloaded operators that are<br>
/// member functions.<br>
///<br>
@@ -7342,8 +7420,8 @@ void Sema::AddSurrogateCandidate(CXXConv<br>
void Sema::AddMemberOperatorCandidates(OverloadedOperatorKind Op,<br>
SourceLocation OpLoc,<br>
ArrayRef<Expr *> Args,<br>
- OverloadCandidateSet& CandidateSet,<br>
- SourceRange OpRange) {<br>
+ OverloadCandidateSet &CandidateSet,<br>
+ OverloadCandidateParamOrder PO) {<br>
DeclarationName OpName = Context.DeclarationNames.getCXXOperatorName(Op);<br>
<br>
// C++ [over.match.oper]p3:<br>
@@ -7378,7 +7456,7 @@ void Sema::AddMemberOperatorCandidates(O<br>
++Oper)<br>
AddMethodCandidate(Oper.getPair(), Args[0]->getType(),<br>
Args[0]->Classify(Context), Args.slice(1),<br>
- CandidateSet, /*SuppressUserConversion=*/false);<br>
+ CandidateSet, /*SuppressUserConversion=*/false, PO);<br>
}<br>
}<br>
<br>
@@ -8183,10 +8261,16 @@ public:<br>
if (C->Function->isFunctionTemplateSpecialization())<br>
continue;<br>
<br>
- QualType FirstParamType =<br>
- C->Function->getParamDecl(0)->getType().getUnqualifiedType();<br>
- QualType SecondParamType =<br>
- C->Function->getParamDecl(1)->getType().getUnqualifiedType();<br>
+ // We interpret "same parameter-type-list" as applying to the<br>
+ // "synthesized candidate, with the order of the two parameters<br>
+ // reversed", not to the original function.<br>
+ bool Reversed = C->RewriteKind & CRK_Reversed;<br>
+ QualType FirstParamType = C->Function->getParamDecl(Reversed ? 1 : 0)<br>
+ ->getType()<br>
+ .getUnqualifiedType();<br>
+ QualType SecondParamType = C->Function->getParamDecl(Reversed ? 0 : 1)<br>
+ ->getType()<br>
+ .getUnqualifiedType();<br>
<br>
// Skip if either parameter isn't of enumeral type.<br>
if (!FirstParamType->isEnumeralType() ||<br>
@@ -9240,6 +9324,7 @@ bool clang::isBetterOverloadCandidate(<br>
// A viable function F1 is defined to be a better function than another<br>
// viable function F2 if for all arguments i, ICSi(F1) is not a worse<br>
// conversion sequence than ICSi(F2), and then...<br>
+ bool HasWorseConversion = false;<br>
for (unsigned ArgIdx = StartArg; ArgIdx < NumArgs; ++ArgIdx) {<br>
switch (CompareImplicitConversionSequences(S, Loc,<br>
Cand1.Conversions[ArgIdx],<br>
@@ -9250,6 +9335,24 @@ bool clang::isBetterOverloadCandidate(<br>
break;<br>
<br>
case ImplicitConversionSequence::Worse:<br>
+ if (Cand1.Function && Cand1.Function == Cand2.Function &&<br>
+ (Cand2.RewriteKind & CRK_Reversed) != 0) {<br>
+ // Work around large-scale breakage caused by considering reversed<br>
+ // forms of operator== in C++20:<br>
+ //<br>
+ // When comparing a function against its reversed form, if we have a<br>
+ // better conversion for one argument and a worse conversion for the<br>
+ // other, we prefer the non-reversed form.<br>
+ //<br>
+ // This prevents a conversion function from being considered ambiguous<br>
+ // with its own reversed form in various where it's only incidentally<br>
+ // heterogeneous.<br>
+ //<br>
+ // We diagnose this as an extension from CreateOverloadedBinOp.<br>
+ HasWorseConversion = true;<br>
+ break;<br>
+ }<br>
+<br>
// Cand1 can't be better than Cand2.<br>
return false;<br>
<br>
@@ -9263,6 +9366,8 @@ bool clang::isBetterOverloadCandidate(<br>
// ICSj(F2), or, if not that,<br>
if (HasBetterConversion)<br>
return true;<br>
+ if (HasWorseConversion)<br>
+ return false;<br>
<br>
// -- the context is an initialization by user-defined conversion<br>
// (see 8.5, 13.3.1.5) and the standard conversion sequence<br>
@@ -9330,8 +9435,10 @@ bool clang::isBetterOverloadCandidate(<br>
return BetterTemplate == Cand1.Function->getPrimaryTemplate();<br>
}<br>
<br>
- // FIXME: Work around a defect in the C++17 inheriting constructor wording.<br>
- // A derived-class constructor beats an (inherited) base class constructor.<br>
+ // -- F1 is a constructor for a class D, F2 is a constructor for a base<br>
+ // class B of D, and for all arguments the corresponding parameters of<br>
+ // F1 and F2 have the same type.<br>
+ // FIXME: Implement the "all parameters have the same type" check.<br>
bool Cand1IsInherited =<br>
dyn_cast_or_null<ConstructorUsingShadowDecl>(Cand1.FoundDecl.getDecl());<br>
bool Cand2IsInherited =<br>
@@ -9349,6 +9456,16 @@ bool clang::isBetterOverloadCandidate(<br>
// Inherited from sibling base classes: still ambiguous.<br>
}<br>
<br>
+ // -- F2 is a rewritten candidate (12.4.1.2) and F1 is not<br>
+ // -- F1 and F2 are rewritten candidates, and F2 is a synthesized candidate<br>
+ // with reversed order of parameters and F1 is not<br>
+ //<br>
+ // We rank reversed + different operator as worse than just reversed, but<br>
+ // that comparison can never happen, because we only consider reversing for<br>
+ // the maximally-rewritten operator (== or <=>).<br>
+ if (Cand1.RewriteKind != Cand2.RewriteKind)<br>
+ return Cand1.RewriteKind < Cand2.RewriteKind;<br>
+<br>
// Check C++17 tie-breakers for deduction guides.<br>
{<br>
auto *Guide1 = dyn_cast_or_null<CXXDeductionGuideDecl>(Cand1.Function);<br>
@@ -9544,6 +9661,7 @@ namespace {<br>
enum OverloadCandidateKind {<br>
oc_function,<br>
oc_method,<br>
+ oc_reversed_binary_operator,<br>
oc_constructor,<br>
oc_implicit_default_constructor,<br>
oc_implicit_copy_constructor,<br>
@@ -9561,6 +9679,7 @@ enum OverloadCandidateSelect {<br>
<br>
static std::pair<OverloadCandidateKind, OverloadCandidateSelect><br>
ClassifyOverloadCandidate(Sema &S, NamedDecl *Found, FunctionDecl *Fn,<br>
+ OverloadCandidateRewriteKind CRK,<br>
std::string &Description) {<br>
<br>
bool isTemplate = Fn->isTemplateDecl() || Found->isTemplateDecl();<br>
@@ -9577,6 +9696,9 @@ ClassifyOverloadCandidate(Sema &S, Named<br>
}();<br>
<br>
OverloadCandidateKind Kind = [&]() {<br>
+ if (CRK & CRK_Reversed)<br>
+ return oc_reversed_binary_operator;<br>
+<br>
if (CXXConstructorDecl *Ctor = dyn_cast<CXXConstructorDecl>(Fn)) {<br>
if (!Ctor->isImplicit()) {<br>
if (isa<ConstructorUsingShadowDecl>(Found))<br>
@@ -9701,6 +9823,7 @@ bool Sema::checkAddressOfFunctionIsAvail<br>
<br>
// Notes the location of an overload candidate.<br>
void Sema::NoteOverloadCandidate(NamedDecl *Found, FunctionDecl *Fn,<br>
+ OverloadCandidateRewriteKind RewriteKind,<br>
QualType DestType, bool TakingAddress) {<br>
if (TakingAddress && !checkAddressOfCandidateIsAvailable(*this, Fn))<br>
return;<br>
@@ -9710,7 +9833,7 @@ void Sema::NoteOverloadCandidate(NamedDe<br>
<br>
std::string FnDesc;<br>
std::pair<OverloadCandidateKind, OverloadCandidateSelect> KSPair =<br>
- ClassifyOverloadCandidate(*this, Found, Fn, FnDesc);<br>
+ ClassifyOverloadCandidate(*this, Found, Fn, RewriteKind, FnDesc);<br>
PartialDiagnostic PD = PDiag(diag::note_ovl_candidate)<br>
<< (unsigned)KSPair.first << (unsigned)KSPair.second<br>
<< Fn << FnDesc;<br>
@@ -9734,11 +9857,11 @@ void Sema::NoteAllOverloadCandidates(Exp<br>
I != IEnd; ++I) {<br>
if (FunctionTemplateDecl *FunTmpl =<br>
dyn_cast<FunctionTemplateDecl>((*I)->getUnderlyingDecl()) ) {<br>
- NoteOverloadCandidate(*I, FunTmpl->getTemplatedDecl(), DestType,<br>
+ NoteOverloadCandidate(*I, FunTmpl->getTemplatedDecl(), CRK_None, DestType,<br>
TakingAddress);<br>
} else if (FunctionDecl *Fun<br>
= dyn_cast<FunctionDecl>((*I)->getUnderlyingDecl()) ) {<br>
- NoteOverloadCandidate(*I, Fun, DestType, TakingAddress);<br>
+ NoteOverloadCandidate(*I, Fun, CRK_None, DestType, TakingAddress);<br>
}<br>
}<br>
}<br>
@@ -9788,7 +9911,8 @@ static void DiagnoseBadConversion(Sema &<br>
<br>
std::string FnDesc;<br>
std::pair<OverloadCandidateKind, OverloadCandidateSelect> FnKindPair =<br>
- ClassifyOverloadCandidate(S, Cand->FoundDecl, Fn, FnDesc);<br>
+ ClassifyOverloadCandidate(S, Cand->FoundDecl, Fn, Cand->RewriteKind,<br>
+ FnDesc);<br>
<br>
Expr *FromExpr = Conv.Bad.FromExpr;<br>
QualType FromTy = Conv.Bad.getFromType();<br>
@@ -10060,7 +10184,7 @@ static void DiagnoseArityMismatch(Sema &<br>
<br>
std::string Description;<br>
std::pair<OverloadCandidateKind, OverloadCandidateSelect> FnKindPair =<br>
- ClassifyOverloadCandidate(S, Found, Fn, Description);<br>
+ ClassifyOverloadCandidate(S, Found, Fn, CRK_None, Description);<br>
<br>
if (modeCount == 1 && Fn->getParamDecl(0)->getDeclName())<br>
S.Diag(Fn->getLocation(), diag::note_ovl_candidate_arity_one)<br>
@@ -10357,7 +10481,8 @@ static void DiagnoseBadTarget(Sema &S, O<br>
<br>
std::string FnDesc;<br>
std::pair<OverloadCandidateKind, OverloadCandidateSelect> FnKindPair =<br>
- ClassifyOverloadCandidate(S, Cand->FoundDecl, Callee, FnDesc);<br>
+ ClassifyOverloadCandidate(S, Cand->FoundDecl, Callee, Cand->RewriteKind,<br>
+ FnDesc);<br>
<br>
S.Diag(Callee->getLocation(), diag::note_ovl_candidate_bad_target)<br>
<< (unsigned)FnKindPair.first << (unsigned)ocs_non_template<br>
@@ -10475,7 +10600,8 @@ static void NoteFunctionCandidate(Sema &<br>
if (Fn->isDeleted()) {<br>
std::string FnDesc;<br>
std::pair<OverloadCandidateKind, OverloadCandidateSelect> FnKindPair =<br>
- ClassifyOverloadCandidate(S, Cand->FoundDecl, Fn, FnDesc);<br>
+ ClassifyOverloadCandidate(S, Cand->FoundDecl, Fn, Cand->RewriteKind,<br>
+ FnDesc);<br>
<br>
S.Diag(Fn->getLocation(), diag::note_ovl_candidate_deleted)<br>
<< (unsigned)FnKindPair.first << (unsigned)FnKindPair.second << FnDesc<br>
@@ -10485,7 +10611,7 @@ static void NoteFunctionCandidate(Sema &<br>
}<br>
<br>
// We don't really have anything else to say about viable candidates.<br>
- S.NoteOverloadCandidate(Cand->FoundDecl, Fn);<br>
+ S.NoteOverloadCandidate(Cand->FoundDecl, Fn, Cand->RewriteKind);<br>
return;<br>
}<br>
<br>
@@ -10518,7 +10644,7 @@ static void NoteFunctionCandidate(Sema &<br>
case ovl_fail_trivial_conversion:<br>
case ovl_fail_bad_final_conversion:<br>
case ovl_fail_final_conversion_not_exact:<br>
- return S.NoteOverloadCandidate(Cand->FoundDecl, Fn);<br>
+ return S.NoteOverloadCandidate(Cand->FoundDecl, Fn, Cand->RewriteKind);<br>
<br>
case ovl_fail_bad_conversion: {<br>
unsigned I = (Cand->IgnoreObjectArgument ? 1 : 0);<br>
@@ -10529,7 +10655,7 @@ static void NoteFunctionCandidate(Sema &<br>
// FIXME: this currently happens when we're called from SemaInit<br>
// when user-conversion overload fails. Figure out how to handle<br>
// those conditions and diagnose them well.<br>
- return S.NoteOverloadCandidate(Cand->FoundDecl, Fn);<br>
+ return S.NoteOverloadCandidate(Cand->FoundDecl, Fn, Cand->RewriteKind);<br>
}<br>
<br>
case ovl_fail_bad_target:<br>
@@ -10805,8 +10931,10 @@ struct CompareOverloadCandidatesForDispl<br>
/// CompleteNonViableCandidate - Normally, overload resolution only<br>
/// computes up to the first bad conversion. Produces the FixIt set if<br>
/// possible.<br>
-static void CompleteNonViableCandidate(Sema &S, OverloadCandidate *Cand,<br>
- ArrayRef<Expr *> Args) {<br>
+static void<br>
+CompleteNonViableCandidate(Sema &S, OverloadCandidate *Cand,<br>
+ ArrayRef<Expr *> Args,<br>
+ OverloadCandidateSet::CandidateSetKind CSK) {<br>
assert(!Cand->Viable);<br>
<br>
// Don't do anything on failures other than bad conversion.<br>
@@ -10834,6 +10962,7 @@ static void CompleteNonViableCandidate(S<br>
bool SuppressUserConversions = false;<br>
<br>
unsigned ConvIdx = 0;<br>
+ unsigned ArgIdx = 0;<br>
ArrayRef<QualType> ParamTypes;<br>
<br>
if (Cand->IsSurrogate) {<br>
@@ -10842,15 +10971,18 @@ static void CompleteNonViableCandidate(S<br>
if (const PointerType *ConvPtrType = ConvType->getAs<PointerType>())<br>
ConvType = ConvPtrType->getPointeeType();<br>
ParamTypes = ConvType->castAs<FunctionProtoType>()->getParamTypes();<br>
- // Conversion 0 is 'this', which doesn't have a corresponding argument.<br>
+ // Conversion 0 is 'this', which doesn't have a corresponding parameter.<br>
ConvIdx = 1;<br>
} else if (Cand->Function) {<br>
ParamTypes =<br>
Cand->Function->getType()->castAs<FunctionProtoType>()->getParamTypes();<br>
if (isa<CXXMethodDecl>(Cand->Function) &&<br>
!isa<CXXConstructorDecl>(Cand->Function)) {<br>
- // Conversion 0 is 'this', which doesn't have a corresponding argument.<br>
+ // Conversion 0 is 'this', which doesn't have a corresponding parameter.<br>
ConvIdx = 1;<br>
+ if (CSK == OverloadCandidateSet::CSK_Operator)<br>
+ // Argument 0 is 'this', which doesn't have a corresponding parameter.<br>
+ ArgIdx = 1;<br>
}<br>
} else {<br>
// Builtin operator.<br>
@@ -10859,16 +10991,19 @@ static void CompleteNonViableCandidate(S<br>
}<br>
<br>
// Fill in the rest of the conversions.<br>
- for (unsigned ArgIdx = 0; ConvIdx != ConvCount; ++ConvIdx, ++ArgIdx) {<br>
+ bool Reversed = Cand->RewriteKind & CRK_Reversed;<br>
+ for (unsigned ParamIdx = Reversed ? ParamTypes.size() - 1 : 0;<br>
+ ConvIdx != ConvCount;<br>
+ ++ConvIdx, ++ArgIdx, ParamIdx += (Reversed ? -1 : 1)) {<br>
if (Cand->Conversions[ConvIdx].isInitialized()) {<br>
// We've already checked this conversion.<br>
} else if (ArgIdx < ParamTypes.size()) {<br>
- if (ParamTypes[ArgIdx]->isDependentType())<br>
+ if (ParamTypes[ParamIdx]->isDependentType())<br>
Cand->Conversions[ConvIdx].setAsIdentityConversion(<br>
Args[ArgIdx]->getType());<br>
else {<br>
Cand->Conversions[ConvIdx] =<br>
- TryCopyInitialization(S, Args[ArgIdx], ParamTypes[ArgIdx],<br>
+ TryCopyInitialization(S, Args[ArgIdx], ParamTypes[ParamIdx],<br>
SuppressUserConversions,<br>
/*InOverloadResolution=*/true,<br>
/*AllowObjCWritebackConversion=*/<br>
@@ -10896,7 +11031,7 @@ SmallVector<OverloadCandidate *, 32> Ove<br>
if (Cand->Viable)<br>
Cands.push_back(Cand);<br>
else if (OCD == OCD_AllCandidates) {<br>
- CompleteNonViableCandidate(S, Cand, Args);<br>
+ CompleteNonViableCandidate(S, Cand, Args, Kind);<br>
if (Cand->Function || Cand->IsSurrogate)<br>
Cands.push_back(Cand);<br>
// Otherwise, this a non-viable builtin candidate. We do not, in general,<br>
@@ -11453,7 +11588,7 @@ public:<br>
if (FunctionDecl *Fun =<br>
dyn_cast<FunctionDecl>((*I)->getUnderlyingDecl()))<br>
if (!functionHasPassObjectSizeParams(Fun))<br>
- S.NoteOverloadCandidate(*I, Fun, TargetFunctionType,<br>
+ S.NoteOverloadCandidate(*I, Fun, CRK_None, TargetFunctionType,<br>
/*TakingAddress=*/true);<br>
FailedCandidates.NoteCandidates(S, OvlExpr->getBeginLoc());<br>
}<br>
@@ -12403,7 +12538,7 @@ Sema::CreateOverloadedUnaryOp(SourceLoca<br>
OverloadCandidateSet CandidateSet(OpLoc, OverloadCandidateSet::CSK_Operator);<br>
<br>
// Add the candidates from the given function set.<br>
- AddFunctionCandidates(Fns, ArgsArray, CandidateSet);<br>
+ AddNonMemberOperatorCandidates(Fns, ArgsArray, CandidateSet);<br>
<br>
// Add operator candidates that are member functions.<br>
AddMemberOperatorCandidates(Op, OpLoc, ArgsArray, CandidateSet);<br>
@@ -12548,14 +12683,17 @@ Sema::CreateOverloadedUnaryOp(SourceLoca<br>
///<br>
/// \param LHS Left-hand argument.<br>
/// \param RHS Right-hand argument.<br>
-ExprResult<br>
-Sema::CreateOverloadedBinOp(SourceLocation OpLoc,<br>
- BinaryOperatorKind Opc,<br>
- const UnresolvedSetImpl &Fns,<br>
- Expr *LHS, Expr *RHS, bool PerformADL) {<br>
+ExprResult Sema::CreateOverloadedBinOp(SourceLocation OpLoc,<br>
+ BinaryOperatorKind Opc,<br>
+ const UnresolvedSetImpl &Fns, Expr *LHS,<br>
+ Expr *RHS, bool PerformADL,<br>
+ bool AllowRewrittenCandidates) {<br>
Expr *Args[2] = { LHS, RHS };<br>
LHS=RHS=nullptr; // Please use only Args instead of LHS/RHS couple<br>
<br>
+ if (!getLangOpts().CPlusPlus2a)<br>
+ AllowRewrittenCandidates = false;<br>
+<br>
OverloadedOperatorKind Op = BinaryOperator::getOverloadedOperator(Opc);<br>
DeclarationName OpName = Context.DeclarationNames.getCXXOperatorName(Op);<br>
<br>
@@ -12613,23 +12751,61 @@ Sema::CreateOverloadedBinOp(SourceLocati<br>
return CreateBuiltinBinOp(OpLoc, Opc, Args[0], Args[1]);<br>
<br>
// Build an empty overload set.<br>
- OverloadCandidateSet CandidateSet(OpLoc, OverloadCandidateSet::CSK_Operator);<br>
+ OverloadCandidateSet CandidateSet(<br>
+ OpLoc, OverloadCandidateSet::CSK_Operator,<br>
+ OverloadCandidateSet::OperatorRewriteInfo(Op, AllowRewrittenCandidates));<br>
<br>
- // Add the candidates from the given function set.<br>
- AddFunctionCandidates(Fns, Args, CandidateSet);<br>
+ OverloadedOperatorKind ExtraOp =<br>
+ AllowRewrittenCandidates ? getRewrittenOverloadedOperator(Op) : OO_None;<br>
+<br>
+ // Add the candidates from the given function set. This also adds the<br>
+ // rewritten candidates using these functions if necessary.<br>
+ AddNonMemberOperatorCandidates(Fns, Args, CandidateSet);<br>
<br>
// Add operator candidates that are member functions.<br>
AddMemberOperatorCandidates(Op, OpLoc, Args, CandidateSet);<br>
+ if (CandidateSet.getRewriteInfo().shouldAddReversed(Op))<br>
+ AddMemberOperatorCandidates(Op, OpLoc, {Args[1], Args[0]}, CandidateSet,<br>
+ OverloadCandidateParamOrder::Reversed);<br>
+<br>
+ // In C++20, also add any rewritten member candidates.<br>
+ if (ExtraOp) {<br>
+ AddMemberOperatorCandidates(ExtraOp, OpLoc, Args, CandidateSet);<br>
+ if (CandidateSet.getRewriteInfo().shouldAddReversed(ExtraOp))<br>
+ AddMemberOperatorCandidates(ExtraOp, OpLoc, {Args[1], Args[0]},<br>
+ CandidateSet,<br>
+ OverloadCandidateParamOrder::Reversed);<br>
+ }<br>
<br>
// Add candidates from ADL. Per [over.match.oper]p2, this lookup is not<br>
// performed for an assignment operator (nor for operator[] nor operator->,<br>
// which don't get here).<br>
- if (Opc != BO_Assign && PerformADL)<br>
+ if (Opc != BO_Assign && PerformADL) {<br>
AddArgumentDependentLookupCandidates(OpName, OpLoc, Args,<br>
/*ExplicitTemplateArgs*/ nullptr,<br>
CandidateSet);<br>
+ if (ExtraOp) {<br>
+ DeclarationName ExtraOpName =<br>
+ Context.DeclarationNames.getCXXOperatorName(ExtraOp);<br>
+ AddArgumentDependentLookupCandidates(ExtraOpName, OpLoc, Args,<br>
+ /*ExplicitTemplateArgs*/ nullptr,<br>
+ CandidateSet);<br>
+ }<br>
+ }<br>
<br>
// Add builtin operator candidates.<br>
+ //<br>
+ // FIXME: We don't add any rewritten candidates here. This is strictly<br>
+ // incorrect; a builtin candidate could be hidden by a non-viable candidate,<br>
+ // resulting in our selecting a rewritten builtin candidate. For example:<br>
+ //<br>
+ // enum class E { e };<br>
+ // bool operator!=(E, E) requires false;<br>
+ // bool k = E::e != E::e;<br>
+ //<br>
+ // ... should select the rewritten builtin candidate 'operator==(E, E)'. But<br>
+ // it seems unreasonable to consider rewritten builtin candidates. A core<br>
+ // issue has been filed proposing to removed this requirement.<br>
AddBuiltinOperatorCandidates(Op, OpLoc, Args, CandidateSet);<br>
<br>
bool HadMultipleCandidates = (CandidateSet.size() > 1);<br>
@@ -12641,11 +12817,57 @@ Sema::CreateOverloadedBinOp(SourceLocati<br>
// We found a built-in operator or an overloaded operator.<br>
FunctionDecl *FnDecl = Best->Function;<br>
<br>
+ bool IsReversed = (Best->RewriteKind & CRK_Reversed);<br>
+ if (IsReversed)<br>
+ std::swap(Args[0], Args[1]);<br>
+<br>
if (FnDecl) {<br>
Expr *Base = nullptr;<br>
// We matched an overloaded operator. Build a call to that<br>
// operator.<br>
<br>
+ OverloadedOperatorKind ChosenOp =<br>
+ FnDecl->getDeclName().getCXXOverloadedOperator();<br>
+<br>
+ // C++2a [over.match.oper]p9:<br>
+ // If a rewritten operator== candidate is selected by overload<br>
+ // resolution for an operator@, its return type shall be cv bool<br>
+ if (Best->RewriteKind && ChosenOp == OO_EqualEqual &&<br>
+ !FnDecl->getReturnType()->isBooleanType()) {<br>
+ Diag(OpLoc, diag::err_ovl_rewrite_equalequal_not_bool)<br>
+ << FnDecl->getReturnType() << BinaryOperator::getOpcodeStr(Opc)<br>
+ << Args[0]->getSourceRange() << Args[1]->getSourceRange();<br>
+ Diag(FnDecl->getLocation(), diag::note_declared_at);<br>
+ return ExprError();<br>
+ }<br>
+<br>
+ if (AllowRewrittenCandidates && !IsReversed &&<br>
+ CandidateSet.getRewriteInfo().shouldAddReversed(ChosenOp)) {<br>
+ // We could have reversed this operator, but didn't. Check if the<br>
+ // reversed form was a viable candidate, and if so, if it had a<br>
+ // better conversion for either parameter. If so, this call is<br>
+ // formally ambiguous, and allowing it is an extension.<br>
+ for (OverloadCandidate &Cand : CandidateSet) {<br>
+ if (Cand.Viable && Cand.Function == FnDecl &&<br>
+ Cand.RewriteKind & CRK_Reversed) {<br>
+ for (unsigned ArgIdx = 0; ArgIdx < 2; ++ArgIdx) {<br>
+ if (CompareImplicitConversionSequences(<br>
+ *this, OpLoc, Cand.Conversions[ArgIdx],<br>
+ Best->Conversions[ArgIdx]) ==<br>
+ ImplicitConversionSequence::Better) {<br>
+ Diag(OpLoc, diag::ext_ovl_ambiguous_oper_binary_reversed)<br>
+ << BinaryOperator::getOpcodeStr(Opc)<br>
+ << Args[0]->getType() << Args[1]->getType()<br>
+ << Args[0]->getSourceRange() << Args[1]->getSourceRange();<br>
+ Diag(FnDecl->getLocation(),<br>
+ diag::note_ovl_ambiguous_oper_binary_reversed_candidate);<br>
+ }<br>
+ }<br>
+ break;<br>
+ }<br>
+ }<br>
+ }<br>
+<br>
// Convert the arguments.<br>
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(FnDecl)) {<br>
// Best->Access is only meaningful for class members.<br>
@@ -12699,8 +12921,8 @@ Sema::CreateOverloadedBinOp(SourceLocati<br>
ResultTy = ResultTy.getNonLValueExprType(Context);<br>
<br>
CXXOperatorCallExpr *TheCall = CXXOperatorCallExpr::Create(<br>
- Context, Op, FnExpr.get(), Args, ResultTy, VK, OpLoc, FPFeatures,<br>
- Best->IsADLCandidate);<br>
+ Context, ChosenOp, FnExpr.get(), Args, ResultTy, VK, OpLoc,<br>
+ FPFeatures, Best->IsADLCandidate);<br>
<br>
if (CheckCallReturnType(FnDecl->getReturnType(), OpLoc, TheCall,<br>
FnDecl))<br>
@@ -12722,7 +12944,46 @@ Sema::CreateOverloadedBinOp(SourceLocati<br>
isa<CXXMethodDecl>(FnDecl), OpLoc, TheCall->getSourceRange(),<br>
VariadicDoesNotApply);<br>
<br>
- return MaybeBindToTemporary(TheCall);<br>
+ ExprResult R = MaybeBindToTemporary(TheCall);<br>
+ if (R.isInvalid())<br>
+ return ExprError();<br>
+<br>
+ // For a rewritten candidate, we've already reversed the arguments<br>
+ // if needed. Perform the rest of the rewrite now.<br>
+ if ((Best->RewriteKind & CRK_DifferentOperator) ||<br>
+ (Op == OO_Spaceship && IsReversed)) {<br>
+ if (Op == OO_ExclaimEqual) {<br>
+ assert(ChosenOp == OO_EqualEqual && "unexpected operator name");<br>
+ R = CreateBuiltinUnaryOp(OpLoc, UO_LNot, R.get());<br>
+ } else {<br>
+ assert(ChosenOp == OO_Spaceship && "unexpected operator name");<br>
+ llvm::APSInt Zero(Context.getTypeSize(Context.IntTy), false);<br>
+ Expr *ZeroLiteral =<br>
+ IntegerLiteral::Create(Context, Zero, Context.IntTy, OpLoc);<br>
+<br>
+ Sema::CodeSynthesisContext Ctx;<br>
+ Ctx.Kind = Sema::CodeSynthesisContext::RewritingOperatorAsSpaceship;<br>
+ Ctx.Entity = FnDecl;<br>
+ pushCodeSynthesisContext(Ctx);<br>
+<br>
+ R = CreateOverloadedBinOp(<br>
+ OpLoc, Opc, Fns, IsReversed ? ZeroLiteral : R.get(),<br>
+ IsReversed ? R.get() : ZeroLiteral, PerformADL,<br>
+ /*AllowRewrittenCandidates=*/false);<br>
+<br>
+ popCodeSynthesisContext();<br>
+ }<br>
+ if (R.isInvalid())<br>
+ return ExprError();<br>
+ } else {<br>
+ assert(ChosenOp == Op && "unexpected operator name");<br>
+ }<br>
+<br>
+ // Make a note in the AST if we did any rewriting.<br>
+ if (Best->RewriteKind != CRK_None)<br>
+ R = new (Context) CXXRewrittenBinaryOperator(R.get(), IsReversed);<br>
+<br>
+ return R;<br>
} else {<br>
// We matched a built-in operator. Convert the arguments, then<br>
// break out so that we will build the appropriate built-in<br>
@@ -12812,10 +13073,12 @@ Sema::CreateOverloadedBinOp(SourceLocati<br>
return ExprError();<br>
}<br>
CandidateSet.NoteCandidates(<br>
- PartialDiagnosticAt(OpLoc, PDiag(diag::err_ovl_deleted_oper)<br>
- << BinaryOperator::getOpcodeStr(Opc)<br>
- << Args[0]->getSourceRange()<br>
- << Args[1]->getSourceRange()),<br>
+ PartialDiagnosticAt(<br>
+ OpLoc, PDiag(diag::err_ovl_deleted_oper)<br>
+ << getOperatorSpelling(Best->Function->getDeclName()<br>
+ .getCXXOverloadedOperator())<br>
+ << Args[0]->getSourceRange()<br>
+ << Args[1]->getSourceRange()),<br>
*this, OCD_AllCandidates, Args, BinaryOperator::getOpcodeStr(Opc),<br>
OpLoc);<br>
return ExprError();<br>
@@ -13692,8 +13955,8 @@ ExprResult Sema::BuildLiteralOperatorCal<br>
<br>
OverloadCandidateSet CandidateSet(UDSuffixLoc,<br>
OverloadCandidateSet::CSK_Normal);<br>
- AddFunctionCandidates(R.asUnresolvedSet(), Args, CandidateSet, TemplateArgs,<br>
- /*SuppressUserConversions=*/true);<br>
+ AddNonMemberOperatorCandidates(R.asUnresolvedSet(), Args, CandidateSet,<br>
+ TemplateArgs);<br>
<br>
bool HadMultipleCandidates = (CandidateSet.size() > 1);<br>
<br>
<br>
Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=375306&r1=375305&r2=375306&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=375306&r1=375305&r2=375306&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)<br>
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Fri Oct 18 17:04:43 2019<br>
@@ -24,6 +24,7 @@<br>
#include "clang/Basic/TargetInfo.h"<br>
#include "clang/Sema/DeclSpec.h"<br>
#include "clang/Sema/Lookup.h"<br>
+#include "clang/Sema/Overload.h"<br>
#include "clang/Sema/ParsedTemplate.h"<br>
#include "clang/Sema/Scope.h"<br>
#include "clang/Sema/SemaInternal.h"<br>
@@ -8488,7 +8489,7 @@ bool Sema::CheckFunctionTemplateSpeciali<br>
// candidates at once, to get proper sorting and limiting.<br>
for (auto *OldND : Previous) {<br>
if (auto *OldFD = dyn_cast<FunctionDecl>(OldND->getUnderlyingDecl()))<br>
- NoteOverloadCandidate(OldND, OldFD, FD->getType(), false);<br>
+ NoteOverloadCandidate(OldND, OldFD, CRK_None, FD->getType(), false);<br>
}<br>
FailedCandidates.NoteCandidates(*this, FD->getLocation());<br>
return true;<br>
<br>
Modified: cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp?rev=375306&r1=375305&r2=375306&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp?rev=375306&r1=375305&r2=375306&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp (original)<br>
+++ cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp Fri Oct 18 17:04:43 2019<br>
@@ -206,6 +206,7 @@ bool Sema::CodeSynthesisContext::isInsta<br>
case DefiningSynthesizedFunction:<br>
case ExceptionSpecEvaluation:<br>
case ConstraintSubstitution:<br>
+ case RewritingOperatorAsSpaceship:<br>
return false;<br>
<br>
// This function should never be called when Kind's value is Memoization.<br>
@@ -682,6 +683,11 @@ void Sema::PrintInstantiationStack() {<br>
break;<br>
}<br>
<br>
+ case CodeSynthesisContext::RewritingOperatorAsSpaceship:<br>
+ Diags.Report(Active->Entity->getLocation(),<br>
+ diag::note_rewriting_operator_as_spaceship);<br>
+ break;<br>
+<br>
case CodeSynthesisContext::Memoization:<br>
break;<br>
<br>
@@ -754,6 +760,7 @@ Optional<TemplateDeductionInfo *> Sema::<br>
<br>
case CodeSynthesisContext::DeclaringSpecialMember:<br>
case CodeSynthesisContext::DefiningSynthesizedFunction:<br>
+ case CodeSynthesisContext::RewritingOperatorAsSpaceship:<br>
// This happens in a context unrelated to template instantiation, so<br>
// there is no SFINAE.<br>
return None;<br>
<br>
Modified: cfe/trunk/lib/Sema/TreeTransform.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/TreeTransform.h?rev=375306&r1=375305&r2=375306&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/TreeTransform.h?rev=375306&r1=375305&r2=375306&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/Sema/TreeTransform.h (original)<br>
+++ cfe/trunk/lib/Sema/TreeTransform.h Fri Oct 18 17:04:43 2019<br>
@@ -2355,13 +2355,13 @@ public:<br>
<br>
/// Build a new rewritten operator expression.<br>
///<br>
- /// By default, builds the rewritten operator without performing any semantic<br>
- /// analysis. Subclasses may override this routine to provide different<br>
- /// behavior.<br>
- ExprResult RebuildCXXRewrittenBinaryOperator(Expr *SemanticForm,<br>
- bool IsReversed) {<br>
- return new (getSema().Context)<br>
- CXXRewrittenBinaryOperator(SemanticForm, IsReversed);<br>
+ /// By default, performs semantic analysis to build the new expression.<br>
+ /// Subclasses may override this routine to provide different behavior.<br>
+ ExprResult RebuildCXXRewrittenBinaryOperator(<br>
+ SourceLocation OpLoc, BinaryOperatorKind Opcode,<br>
+ const UnresolvedSetImpl &UnqualLookups, Expr *LHS, Expr *RHS) {<br>
+ return getSema().CreateOverloadedBinOp(OpLoc, Opcode, UnqualLookups, LHS,<br>
+ RHS, /*RequiresADL*/false);<br>
}<br>
<br>
/// Build a new conditional operator expression.<br>
@@ -9783,24 +9783,45 @@ TreeTransform<Derived>::TransformBinaryO<br>
template <typename Derived><br>
ExprResult TreeTransform<Derived>::TransformCXXRewrittenBinaryOperator(<br>
CXXRewrittenBinaryOperator *E) {<br>
- // FIXME: C++ [temp.deduct]p7 "The substitution proceeds in lexical order and<br>
- // stops when a condition that causes deduction to fail is encountered."<br>
- // requires us to substitute into the LHS before the RHS, even in a rewrite<br>
- // that reversed the operand order.<br>
- //<br>
- // We can't decompose back to a binary operator here, because that would lose<br>
- // the unqualified lookup results from the phase 1 name lookup.<br>
+ CXXRewrittenBinaryOperator::DecomposedForm Decomp = E->getDecomposedForm();<br>
<br>
- ExprResult SemanticForm = getDerived().TransformExpr(E->getSemanticForm());<br>
- if (SemanticForm.isInvalid())<br>
+ ExprResult LHS = getDerived().TransformExpr(const_cast<Expr*>(Decomp.LHS));<br>
+ if (LHS.isInvalid())<br>
+ return ExprError();<br>
+<br>
+ ExprResult RHS = getDerived().TransformExpr(const_cast<Expr*>(Decomp.RHS));<br>
+ if (RHS.isInvalid())<br>
return ExprError();<br>
<br>
if (!getDerived().AlwaysRebuild() &&<br>
- SemanticForm.get() == E->getSemanticForm())<br>
+ LHS.get() == Decomp.LHS &&<br>
+ RHS.get() == Decomp.RHS)<br>
return E;<br>
<br>
- return getDerived().RebuildCXXRewrittenBinaryOperator(SemanticForm.get(),<br>
- E->isReversed());<br>
+ // Extract the already-resolved callee declarations so that we can restrict<br>
+ // ourselves to using them as the unqualified lookup results when rebuilding.<br>
+ UnresolvedSet<2> UnqualLookups;<br>
+ Expr *PossibleBinOps[] = {E->getSemanticForm(),<br>
+ const_cast<Expr *>(Decomp.InnerBinOp)};<br>
+ for (Expr *PossibleBinOp : PossibleBinOps) {<br>
+ auto *Op = dyn_cast<CXXOperatorCallExpr>(PossibleBinOp->IgnoreImplicit());<br>
+ if (!Op)<br>
+ continue;<br>
+ auto *Callee = dyn_cast<DeclRefExpr>(Op->getCallee()->IgnoreImplicit());<br>
+ if (!Callee || isa<CXXMethodDecl>(Callee->getDecl()))<br>
+ continue;<br>
+<br>
+ // Transform the callee in case we built a call to a local extern<br>
+ // declaration.<br>
+ NamedDecl *Found = cast_or_null<NamedDecl>(getDerived().TransformDecl(<br>
+ E->getOperatorLoc(), Callee->getFoundDecl()));<br>
+ if (!Found)<br>
+ return ExprError();<br>
+ UnqualLookups.addDecl(Found);<br>
+ }<br>
+<br>
+ return getDerived().RebuildCXXRewrittenBinaryOperator(<br>
+ E->getOperatorLoc(), Decomp.Opcode, UnqualLookups, LHS.get(), RHS.get());<br>
}<br>
<br>
template<typename Derived><br>
<br>
Added: cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp?rev=375306&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp?rev=375306&view=auto</a><br>
==============================================================================<br>
--- cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp (added)<br>
+++ cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp Fri Oct 18 17:04:43 2019<br>
@@ -0,0 +1,172 @@<br>
+// RUN: %clang_cc1 -std=c++2a -verify %s<br>
+// RUN: %clang_cc1 -std=c++2a -verify -Wall -DNO_ERRORS %s<br>
+<br>
+#ifndef NO_ERRORS<br>
+namespace bullet3 {<br>
+ // the built-in candidates include all of the candidate operator fnuctions<br>
+ // [...] that, compared to the given operator<br>
+<br>
+ // - do not have the same parameter-type-list as any non-member candidate<br>
+<br>
+ enum E { e };<br>
+<br>
+ // Suppress both builtin operator<=>(E, E) and operator<(E, E).<br>
+ void operator<=>(E, E); // expected-note {{while rewriting}}<br>
+ bool cmp = e < e; // expected-error {{invalid operands to binary expression ('void' and 'int')}}<br>
+<br>
+ // None of the other bullets have anything to test here. In principle we<br>
+ // need to suppress both builtin operator@(A, B) and operator@(B, A) when we<br>
+ // see a user-declared reversible operator@(A, B), and we do, but that's<br>
+ // untestable because the only built-in reversible candidates are<br>
+ // operator<=>(E, E) and operator==(E, E) for E an enumeration type, and<br>
+ // those are both symmetric anyway.<br>
+}<br>
+<br>
+namespace bullet4 {<br>
+ // The rewritten candidate set is determined as follows:<br>
+<br>
+ template<int> struct X {};<br>
+ X<1> x1;<br>
+ X<2> x2;<br>
+<br>
+ struct Y {<br>
+ int operator<=>(X<2>) = delete; // #1member<br>
+ bool operator==(X<2>) = delete; // #2member<br>
+ };<br>
+ Y y;<br>
+<br>
+ // - For the relational operators, the rewritten candidates include all<br>
+ // non-rewritten candidates for the expression x <=> y.<br>
+ int operator<=>(X<1>, X<2>) = delete; // #1<br>
+<br>
+ // expected-note@#1 5{{candidate function has been explicitly deleted}}<br>
+ // expected-note@#1 5{{candidate function (with reversed parameter order) not viable: no known conversion from 'X<1>' to 'X<2>' for 1st argument}}<br>
+ bool lt = x1 < x2; // expected-error {{selected deleted operator '<=>'}}<br>
+ bool le = x1 <= x2; // expected-error {{selected deleted operator '<=>'}}<br>
+ bool gt = x1 > x2; // expected-error {{selected deleted operator '<=>'}}<br>
+ bool ge = x1 >= x2; // expected-error {{selected deleted operator '<=>'}}<br>
+ bool cmp = x1 <=> x2; // expected-error {{selected deleted operator '<=>'}}<br>
+<br>
+ // expected-note@#1member 5{{candidate function has been explicitly deleted}}<br>
+ // expected-note@#1 5{{candidate function not viable: no known conversion from 'bullet4::Y' to 'X<1>' for 1st argument}}<br>
+ // expected-note@#1 5{{candidate function (with reversed parameter order) not viable: no known conversion from 'bullet4::Y' to 'X<2>' for 1st argument}}<br>
+ bool mem_lt = y < x2; // expected-error {{selected deleted operator '<=>'}}<br>
+ bool mem_le = y <= x2; // expected-error {{selected deleted operator '<=>'}}<br>
+ bool mem_gt = y > x2; // expected-error {{selected deleted operator '<=>'}}<br>
+ bool mem_ge = y >= x2; // expected-error {{selected deleted operator '<=>'}}<br>
+ bool mem_cmp = y <=> x2; // expected-error {{selected deleted operator '<=>'}}<br>
+<br>
+ // - For the relational and three-way comparison operators, the rewritten<br>
+ // candidates also include a synthesized candidate, with the order of the<br>
+ // two parameters reversed, for each non-rewritten candidate for the<br>
+ // expression y <=> x.<br>
+<br>
+ // expected-note@#1 5{{candidate function (with reversed parameter order) has been explicitly deleted}}<br>
+ // expected-note@#1 5{{candidate function not viable: no known conversion from 'X<2>' to 'X<1>' for 1st argument}}<br>
+ bool rlt = x2 < x1; // expected-error {{selected deleted operator '<=>'}}<br>
+ bool rle = x2 <= x1; // expected-error {{selected deleted operator '<=>'}}<br>
+ bool rgt = x2 > x1; // expected-error {{selected deleted operator '<=>'}}<br>
+ bool rge = x2 >= x1; // expected-error {{selected deleted operator '<=>'}}<br>
+ bool rcmp = x2 <=> x1; // expected-error {{selected deleted operator '<=>'}}<br>
+<br>
+ // expected-note@#1member 5{{candidate function (with reversed parameter order) has been explicitly deleted}}<br>
+ // expected-note@#1 5{{candidate function not viable: no known conversion from 'X<2>' to 'X<1>' for 1st argument}}<br>
+ // expected-note@#1 5{{candidate function (with reversed parameter order) not viable: no known conversion from 'bullet4::Y' to 'X<1>' for 2nd argument}}<br>
+ bool mem_rlt = x2 < y; // expected-error {{selected deleted operator '<=>'}}<br>
+ bool mem_rle = x2 <= y; // expected-error {{selected deleted operator '<=>'}}<br>
+ bool mem_rgt = x2 > y; // expected-error {{selected deleted operator '<=>'}}<br>
+ bool mem_rge = x2 >= y; // expected-error {{selected deleted operator '<=>'}}<br>
+ bool mem_rcmp = x2 <=> y; // expected-error {{selected deleted operator '<=>'}}<br>
+<br>
+ // For the != operator, the rewritten candidates include all non-rewritten<br>
+ // candidates for the expression x == y<br>
+ int operator==(X<1>, X<2>) = delete; // #2<br>
+<br>
+ // expected-note@#2 2{{candidate function has been explicitly deleted}}<br>
+ // expected-note@#2 2{{candidate function (with reversed parameter order) not viable: no known conversion from 'X<1>' to 'X<2>' for 1st argument}}<br>
+ bool eq = x1 == x2; // expected-error {{selected deleted operator '=='}}<br>
+ bool ne = x1 != x2; // expected-error {{selected deleted operator '=='}}<br>
+<br>
+ // expected-note@#2member 2{{candidate function has been explicitly deleted}}<br>
+ // expected-note@#2 2{{candidate function not viable: no known conversion from 'bullet4::Y' to 'X<1>' for 1st argument}}<br>
+ // expected-note@#2 2{{candidate function (with reversed parameter order) not viable: no known conversion from 'bullet4::Y' to 'X<2>' for 1st argument}}<br>
+ bool mem_eq = y == x2; // expected-error {{selected deleted operator '=='}}<br>
+ bool mem_ne = y != x2; // expected-error {{selected deleted operator '=='}}<br>
+<br>
+ // For the equality operators, the rewritten candidates also include a<br>
+ // synthesized candidate, with the order of the two parameters reversed, for<br>
+ // each non-rewritten candidate for the expression y == x<br>
+<br>
+ // expected-note@#2 2{{candidate function (with reversed parameter order) has been explicitly deleted}}<br>
+ // expected-note@#2 2{{candidate function not viable: no known conversion from 'X<2>' to 'X<1>' for 1st argument}}<br>
+ bool req = x2 == x1; // expected-error {{selected deleted operator '=='}}<br>
+ bool rne = x2 != x1; // expected-error {{selected deleted operator '=='}}<br>
+<br>
+ // expected-note@#2member 2{{candidate function (with reversed parameter order) has been explicitly deleted}}<br>
+ // expected-note@#2 2{{candidate function not viable: no known conversion from 'X<2>' to 'X<1>' for 1st argument}}<br>
+ // expected-note@#2 2{{candidate function (with reversed parameter order) not viable: no known conversion from 'bullet4::Y' to 'X<1>' for 2nd argument}}<br>
+ bool mem_req = x2 == y; // expected-error {{selected deleted operator '=='}}<br>
+ bool mem_rne = x2 != y; // expected-error {{selected deleted operator '=='}}<br>
+<br>
+ // For all other operators, the rewritten candidate set is empty.<br>
+ X<3> operator+(X<1>, X<2>) = delete; // expected-note {{no known conversion from 'X<2>' to 'X<1>'}}<br>
+ X<3> reversed_add = x2 + x1; // expected-error {{invalid operands}}<br>
+}<br>
+<br>
+// Various C++17 cases that are known to be broken by the C++20 rules.<br>
+namespace problem_cases {<br>
+ // We can have an ambiguity between an operator and its reversed form. This<br>
+ // wasn't intended by the original "consistent comparison" proposal, and we<br>
+ // allow it as extension, picking the non-reversed form.<br>
+ struct A {<br>
+ bool operator==(const A&); // expected-note {{ambiguity is between a regular call to this operator and a call with the argument order reversed}}<br>
+ };<br>
+ bool cmp_non_const = A() == A(); // expected-warning {{ambiguous}}<br>
+<br>
+ struct B {<br>
+ virtual bool operator==(const B&) const;<br>
+ };<br>
+ struct D : B {<br>
+ bool operator==(const B&) const override; // expected-note {{operator}}<br>
+ };<br>
+ bool cmp_base_derived = D() == D(); // expected-warning {{ambiguous}}<br>
+<br>
+ template<typename T> struct CRTPBase {<br>
+ bool operator==(const T&) const; // expected-note {{operator}}<br>
+ };<br>
+ struct CRTP : CRTPBase<CRTP> {};<br>
+ bool cmp_crtp = CRTP() == CRTP(); // expected-warning {{ambiguous}}<br>
+<br>
+ // We can select a non-rewriteable operator== for a != comparison, when there<br>
+ // was a viable operator!= candidate we could have used instead.<br>
+ //<br>
+ // Rejecting this seems OK on balance.<br>
+ using UBool = signed char; // ICU uses this.<br>
+ struct ICUBase {<br>
+ virtual UBool operator==(const ICUBase&) const;<br>
+ UBool operator!=(const ICUBase &arg) const { return !operator==(arg); }<br>
+ };<br>
+ struct ICUDerived : ICUBase {<br>
+ UBool operator==(const ICUBase&) const override; // expected-note {{declared here}}<br>
+ };<br>
+ bool cmp_icu = ICUDerived() != ICUDerived(); // expected-error {{not 'bool'}}<br>
+}<br>
+<br>
+#else // NO_ERRORS<br>
+<br>
+namespace problem_cases {<br>
+ // We can select a reversed candidate where we used to select a non-reversed<br>
+ // one, and in the worst case this can dramatically change the meaning of the<br>
+ // program. Make sure we at least warn on the worst cases under -Wall.<br>
+ struct iterator;<br>
+ struct const_iterator {<br>
+ const_iterator(iterator);<br>
+ bool operator==(const const_iterator&) const;<br>
+ };<br>
+ struct iterator {<br>
+ bool operator==(const const_iterator &o) const { // expected-warning {{all paths through this function will call itself}}<br>
+ return o == *this;<br>
+ }<br>
+ };<br>
+}<br>
+#endif // NO_ERRORS<br>
<br>
Added: cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p8-2a.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p8-2a.cpp?rev=375306&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p8-2a.cpp?rev=375306&view=auto</a><br>
==============================================================================<br>
--- cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p8-2a.cpp (added)<br>
+++ cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p8-2a.cpp Fri Oct 18 17:04:43 2019<br>
@@ -0,0 +1,70 @@<br>
+// RUN: %clang_cc1 -std=c++2a -verify %s<br>
+<br>
+template<typename L, typename R> struct Op { L l; const char *op; R r; };<br>
+// FIXME: Remove once we implement P1816R0.<br>
+template<typename L, typename R> Op(L, R) -> Op<L, R>;<br>
+<br>
+struct A {};<br>
+struct B {};<br>
+constexpr Op<A, B> operator<=>(A a, B b) { return {a, "<=>", b}; }<br>
+<br>
+template<typename T, typename U, typename V> constexpr Op<Op<T, U>, V> operator< (Op<T, U> a, V b) { return {a, "<", b}; }<br>
+template<typename T, typename U, typename V> constexpr Op<Op<T, U>, V> operator<= (Op<T, U> a, V b) { return {a, "<=", b}; }<br>
+template<typename T, typename U, typename V> constexpr Op<Op<T, U>, V> operator> (Op<T, U> a, V b) { return {a, ">", b}; }<br>
+template<typename T, typename U, typename V> constexpr Op<Op<T, U>, V> operator>= (Op<T, U> a, V b) { return {a, ">=", b}; }<br>
+template<typename T, typename U, typename V> constexpr Op<Op<T, U>, V> operator<=>(Op<T, U> a, V b) { return {a, "<=>", b}; }<br>
+<br>
+template<typename T, typename U, typename V> constexpr Op<T, Op<U, V>> operator< (T a, Op<U, V> b) { return {a, "<", b}; }<br>
+template<typename T, typename U, typename V> constexpr Op<T, Op<U, V>> operator<= (T a, Op<U, V> b) { return {a, "<=", b}; }<br>
+template<typename T, typename U, typename V> constexpr Op<T, Op<U, V>> operator> (T a, Op<U, V> b) { return {a, ">", b}; }<br>
+template<typename T, typename U, typename V> constexpr Op<T, Op<U, V>> operator>= (T a, Op<U, V> b) { return {a, ">=", b}; }<br>
+template<typename T, typename U, typename V> constexpr Op<T, Op<U, V>> operator<=>(T a, Op<U, V> b) { return {a, "<=>", b}; }<br>
+<br>
+constexpr bool same(A, A) { return true; }<br>
+constexpr bool same(B, B) { return true; }<br>
+constexpr bool same(int a, int b) { return a == b; }<br>
+template<typename T, typename U><br>
+constexpr bool same(Op<T, U> x, Op<T, U> y) {<br>
+ return same(x.l, y.l) && __builtin_strcmp(x.op, y.op) == 0 && same(x.r, y.r);<br>
+}<br>
+<br>
+// x @ y is interpreted as:<br>
+void f(A x, B y) {<br>
+ // -- (x <=> y) @ 0 if not reversed<br>
+ static_assert(same(x < y, (x <=> y) < 0));<br>
+ static_assert(same(x <= y, (x <=> y) <= 0));<br>
+ static_assert(same(x > y, (x <=> y) > 0));<br>
+ static_assert(same(x >= y, (x <=> y) >= 0));<br>
+ static_assert(same(x <=> y, x <=> y)); // (not rewritten)<br>
+}<br>
+<br>
+void g(B x, A y) {<br>
+ // -- 0 @ (y <=> x) if reversed<br>
+ static_assert(same(x < y, 0 < (y <=> x)));<br>
+ static_assert(same(x <= y, 0 <= (y <=> x)));<br>
+ static_assert(same(x > y, 0 > (y <=> x)));<br>
+ static_assert(same(x >= y, 0 >= (y <=> x)));<br>
+ static_assert(same(x <=> y, 0 <=> (y <=> x)));<br>
+}<br>
+<br>
+<br>
+// We can rewrite into a call involving a builtin operator.<br>
+struct X { int result; };<br>
+struct Y {};<br>
+constexpr int operator<=>(X x, Y) { return x.result; }<br>
+static_assert(X{-1} < Y{});<br>
+static_assert(X{0} < Y{}); // expected-error {{failed}}<br>
+static_assert(X{0} <= Y{});<br>
+static_assert(X{1} <= Y{}); // expected-error {{failed}}<br>
+static_assert(X{1} > Y{});<br>
+static_assert(X{0} > Y{}); // expected-error {{failed}}<br>
+static_assert(X{0} >= Y{});<br>
+static_assert(X{-1} >= Y{}); // expected-error {{failed}}<br>
+static_assert(Y{} < X{1});<br>
+static_assert(Y{} < X{0}); // expected-error {{failed}}<br>
+static_assert(Y{} <= X{0});<br>
+static_assert(Y{} <= X{-1}); // expected-error {{failed}}<br>
+static_assert(Y{} > X{-1});<br>
+static_assert(Y{} > X{0}); // expected-error {{failed}}<br>
+static_assert(Y{} >= X{0});<br>
+static_assert(Y{} >= X{1}); // expected-error {{failed}}<br>
<br>
Added: cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p9-2a.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p9-2a.cpp?rev=375306&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p9-2a.cpp?rev=375306&view=auto</a><br>
==============================================================================<br>
--- cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p9-2a.cpp (added)<br>
+++ cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p9-2a.cpp Fri Oct 18 17:04:43 2019<br>
@@ -0,0 +1,38 @@<br>
+// RUN: %clang_cc1 -std=c++2a -verify %s<br>
+<br>
+// ... return type shall be cv bool ...<br>
+namespace not_bool {<br>
+ struct X {} x;<br>
+ struct Y {} y;<br>
+ int operator==(X, Y); // expected-note 4{{here}}<br>
+ bool a = x == y; // ok<br>
+ bool b = y == x; // expected-error {{return type 'int' of selected 'operator==' function for rewritten '==' comparison is not 'bool'}}<br>
+ bool c = x != y; // expected-error {{return type 'int' of selected 'operator==' function for rewritten '!=' comparison is not 'bool'}}<br>
+ bool d = y != x; // expected-error {{return type 'int' of selected 'operator==' function for rewritten '!=' comparison is not 'bool'}}<br>
+<br>
+ // cv-qualifiers are OK<br>
+ const bool operator==(Y, X);<br>
+ bool e = y != x; // ok<br>
+<br>
+ // We don't prefer a function with bool return type over one witn non-bool return type.<br>
+ bool f = x != y; // expected-error {{return type 'int' of selected 'operator==' function for rewritten '!=' comparison is not 'bool'}}<br>
+}<br>
+<br>
+struct X { bool equal; };<br>
+struct Y {};<br>
+constexpr bool operator==(X x, Y) { return x.equal; }<br>
+<br>
+static_assert(X{true} == Y{});<br>
+static_assert(X{false} == Y{}); // expected-error {{failed}}<br>
+<br>
+// x == y -> y == x<br>
+static_assert(Y{} == X{true});<br>
+static_assert(Y{} == X{false}); // expected-error {{failed}}<br>
+<br>
+// x != y -> !(x == y)<br>
+static_assert(X{true} != Y{}); // expected-error {{failed}}<br>
+static_assert(X{false} != Y{});<br>
+<br>
+// x != y -> !(y == x)<br>
+static_assert(Y{} != X{true}); // expected-error {{failed}}<br>
+static_assert(Y{} != X{false});<br>
<br>
Modified: cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/p7.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/p7.cpp?rev=375306&r1=375305&r2=375306&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/p7.cpp?rev=375306&r1=375305&r2=375306&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/p7.cpp (original)<br>
+++ cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/p7.cpp Fri Oct 18 17:04:43 2019<br>
@@ -1,4 +1,5 @@<br>
// RUN: %clang_cc1 -std=c++11 -verify %s<br>
+// RUN: %clang_cc1 -std=c++2a -verify %s<br>
<br>
struct Q { typedef int type; };<br>
<br>
@@ -20,3 +21,36 @@ template<typename T, template<typename T<br>
int &c(...);<br>
int &c_disabled = c(0);<br>
int &c_enabled = c(Q()); // expected-error {{cannot bind to a temporary of type 'void'}}<br>
+<br>
+// The substitution proceeds in lexical order and stops when a condition that<br>
+// causes deduction to fail is encountered.<br>
+#if __cplusplus > 201702L<br>
+namespace reversed_operator_substitution_order {<br>
+ struct X { X(int); };<br>
+ struct Y { Y(int); };<br>
+ struct Cat {};<br>
+ namespace no_adl {<br>
+ Cat operator<=>(Y, X);<br>
+ bool operator<(int, Cat);<br>
+<br>
+ template<typename T> struct indirect_sizeof {<br>
+ static_assert(sizeof(T) != 0);<br>
+ static const auto value = sizeof(T);<br>
+ };<br>
+<br>
+ // We should substitute into the construction of the X object before the<br>
+ // construction of the Y object, so this is a SFINAE case rather than a<br>
+ // hard error. This requires substitution to proceed in lexical order<br>
+ // despite the prior rewrite to<br>
+ // 0 < (Y(...) <=> X(...))<br>
+ template<typename T> float &f(<br>
+ decltype(<br>
+ X(sizeof(T)) < Y(indirect_sizeof<T>::value)<br>
+ )<br>
+ );<br>
+ template<typename T> int &f(...);<br>
+ }<br>
+ int &r = no_adl::f<void>(true);<br>
+ float &s = no_adl::f<int>(true);<br>
+}<br>
+#endif<br>
<br>
Added: cfe/trunk/test/CodeGenCXX/mangle-cxx2a.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/mangle-cxx2a.cpp?rev=375306&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/mangle-cxx2a.cpp?rev=375306&view=auto</a><br>
==============================================================================<br>
--- cfe/trunk/test/CodeGenCXX/mangle-cxx2a.cpp (added)<br>
+++ cfe/trunk/test/CodeGenCXX/mangle-cxx2a.cpp Fri Oct 18 17:04:43 2019<br>
@@ -0,0 +1,11 @@<br>
+// RUN: %clang_cc1 -emit-llvm %s -o - -triple=x86_64-linux-gnu -std=c++2a | FileCheck %s<br>
+<br>
+namespace spaceship {<br>
+ struct X {};<br>
+ struct Y {};<br>
+ int operator<=>(X, Y);<br>
+<br>
+ // CHECK-LABEL: define {{.*}} @_ZN9spaceship1fIiEEvDTcmltcvNS_1YE_EcvNS_1XE_EcvT__EE<br>
+ template<typename T> void f(decltype(Y() < X(), T()) x) {}<br>
+ template void f<int>(int);<br>
+}<br>
<br>
Modified: cfe/trunk/test/PCH/cxx2a-compare.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/PCH/cxx2a-compare.cpp?rev=375306&r1=375305&r2=375306&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/PCH/cxx2a-compare.cpp?rev=375306&r1=375305&r2=375306&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/PCH/cxx2a-compare.cpp (original)<br>
+++ cfe/trunk/test/PCH/cxx2a-compare.cpp Fri Oct 18 17:04:43 2019<br>
@@ -15,6 +15,16 @@ inline auto bar(int x) {<br>
return (1 <=> x);<br>
}<br>
<br>
+struct X {<br>
+ int a;<br>
+ friend constexpr std::strong_ordering operator<=>(const X &x, const X &y) {<br>
+ return x.a <=> y.a;<br>
+ }<br>
+};<br>
+constexpr auto baz(int x) {<br>
+ return X{3} < X{x};<br>
+}<br>
+<br>
#else<br>
<br>
// expected-no-diagnostics<br>
@@ -25,4 +35,7 @@ auto bar2(int x) {<br>
return bar(x);<br>
}<br>
<br>
+static_assert(!baz(3));<br>
+static_assert(baz(4));<br>
+<br>
#endif<br>
<br>
Modified: cfe/trunk/test/SemaCXX/compare-cxx2a.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/compare-cxx2a.cpp?rev=375306&r1=375305&r2=375306&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/compare-cxx2a.cpp?rev=375306&r1=375305&r2=375306&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/SemaCXX/compare-cxx2a.cpp (original)<br>
+++ cfe/trunk/test/SemaCXX/compare-cxx2a.cpp Fri Oct 18 17:04:43 2019<br>
@@ -298,11 +298,11 @@ void test_enum_enum_compare_no_builtin()<br>
<br>
template <int><br>
struct Tag {};<br>
-// expected-note@+1 {{candidate}}<br>
-Tag<0> operator<=>(EnumA, EnumA) {<br>
+Tag<0> operator<=>(EnumA, EnumA) { // expected-note {{not viable}}<br>
return {};<br>
}<br>
-Tag<1> operator<=>(EnumA, EnumB) {<br>
+// expected-note@+1 {{while rewriting comparison as call to 'operator<=>' declared here}}<br>
+Tag<1> operator<=>(EnumA, EnumB) { // expected-note {{not viable}}<br>
return {};<br>
}<br>
<br>
@@ -311,7 +311,7 @@ void test_enum_ovl_provided() {<br>
ASSERT_EXPR_TYPE(r1, Tag<0>);<br>
auto r2 = (EnumA::A <=> EnumB::B);<br>
ASSERT_EXPR_TYPE(r2, Tag<1>);<br>
- (void)(EnumB::B <=> EnumA::A); // expected-error {{invalid operands to binary expression ('EnumCompareTests::EnumB' and 'EnumCompareTests::EnumA')}}<br>
+ (void)(EnumB::B <=> EnumA::A); // expected-error {{invalid operands to binary expression ('int' and 'Tag<1>')}}<br>
}<br>
<br>
void enum_float_test() {<br>
<br>
Modified: cfe/trunk/test/SemaCXX/self-comparison.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/self-comparison.cpp?rev=375306&r1=375305&r2=375306&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/self-comparison.cpp?rev=375306&r1=375305&r2=375306&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/SemaCXX/self-comparison.cpp (original)<br>
+++ cfe/trunk/test/SemaCXX/self-comparison.cpp Fri Oct 18 17:04:43 2019<br>
@@ -5,7 +5,7 @@ int foo(int x) {<br>
}<br>
<br>
struct X {<br>
- bool operator==(const X &x);<br>
+ bool operator==(const X &x) const;<br>
};<br>
<br>
struct A {<br>
<br>
Modified: cfe/trunk/www/cxx_status.html<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/www/cxx_status.html?rev=375306&r1=375305&r2=375306&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/www/cxx_status.html?rev=375306&r1=375305&r2=375306&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/www/cxx_status.html (original)<br>
+++ cfe/trunk/www/cxx_status.html Fri Oct 18 17:04:43 2019<br>
@@ -920,23 +920,26 @@ as the draft C++2a standard evolves.<br>
<tr><br>
<td rowspan="6">Consistent comparison (<tt>operator<=></tt>)</td><br>
<td><a href="<a href="http://wg21.link/p0515r3" rel="noreferrer" target="_blank">http://wg21.link/p0515r3</a>">P0515R3</a></td><br>
- <td rowspan="3" class="partial" align="center">Partial</td><br>
+ <td class="partial" align="center">Partial</td><br>
</tr><br>
<tr> <!-- from Jacksonville --><br>
<td><a href="<a href="http://wg21.link/p0905r1" rel="noreferrer" target="_blank">http://wg21.link/p0905r1</a>">P0905R1</a></td><br>
+ <td class="svn" align="center">Clang 10</td><br>
</tr><br>
<tr> <!-- from Rapperswil --><br>
<td><a href="<a href="http://wg21.link/p1120r0" rel="noreferrer" target="_blank">http://wg21.link/p1120r0</a>">P1120R0</a></td><br>
+ <td rowspan="2" class="partial" align="center">Partial</td><br>
</tr><br>
<tr> <!-- from Kona 2019 --><br>
<td><a href="<a href="http://wg21.link/p1185r2" rel="noreferrer" target="_blank">http://wg21.link/p1185r2</a>">P1185R2</a></td><br>
- <td rowspan="3" class="none" align="center">No</td><br>
</tr><br>
<tr> <!-- from Cologne --><br>
<td><a href="<a href="http://wg21.link/p1186r3" rel="noreferrer" target="_blank">http://wg21.link/p1186r3</a>">P1186R3</a></td><br>
+ <td class="none" align="center">No</td><br>
</tr><br>
<tr><br>
<td><a href="<a href="http://wg21.link/p1630r1" rel="noreferrer" target="_blank">http://wg21.link/p1630r1</a>">P1630R1</a></td><br>
+ <td class="partial" align="center">Partial</td><br>
</tr><br>
<tr><br>
<td>Access checking on specializations</td><br>
<br>
<br>
_______________________________________________<br>
cfe-commits mailing list<br>
<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a><br>
<a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits</a><br>
</blockquote></div><br clear="all"><div><br></div>-- <br><div dir="ltr"><div dir="ltr">-- <div>Peter</div></div></div></div>
_______________________________________________<br>
cfe-commits mailing list<br>
<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a><br>
<a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits</a><br>
</blockquote></div></div>
</blockquote></div><br clear="all"><div><br></div>-- <br><div dir="ltr"><div dir="ltr">-- <div>Peter</div></div></div></div>