r375306 - [c++20] Add rewriting from comparison operators to <=> / ==.
Peter Collingbourne via cfe-commits
cfe-commits at lists.llvm.org
Mon Jan 13 15:12:14 PST 2020
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.
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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20200113/dcf9d827/attachment-0001.html>
More information about the cfe-commits
mailing list