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