Index: include/clang/AST/ComparisonCategories.h =================================================================== --- include/clang/AST/ComparisonCategories.h +++ include/clang/AST/ComparisonCategories.h @@ -15,6 +15,7 @@ #ifndef LLVM_CLANG_AST_COMPARISONCATEGORIES_H #define LLVM_CLANG_AST_COMPARISONCATEGORIES_H +#include "clang/AST/OperationKinds.h" #include "clang/Basic/LLVM.h" #include "llvm/ADT/APSInt.h" #include "llvm/ADT/DenseMap.h" @@ -65,6 +66,17 @@ Last = Unordered }; +/// OperatorOverloadCandidateKind - The kind of the operator candidate in +/// accordance with [over.match.oper]. +enum RewrittenOverloadCandidateKind : unsigned char { + /// Not a rewritten candidate. + ROC_None, + /// Rewritten but not synthesized. + ROC_AsThreeWay, + /// Both rewritten and synthesized. + ROC_AsReversedThreeWay +}; + class ComparisonCategoryInfo { friend class ComparisonCategories; friend class Sema; @@ -127,10 +139,14 @@ } /// True iff the comparison category is an equality comparison. - bool isEquality() const { return !isOrdered(); } + bool isEquality() const { return isEquality(Kind); } + static bool isEquality(ComparisonCategoryType Kind) { + return !isOrdered(Kind); + } /// True iff the comparison category is a relational comparison. - bool isOrdered() const { + bool isOrdered() const { return isOrdered(Kind); } + static bool isOrdered(ComparisonCategoryType Kind) { using CCK = ComparisonCategoryType; return Kind == CCK::PartialOrdering || Kind == CCK::WeakOrdering || Kind == CCK::StrongOrdering; @@ -138,17 +154,27 @@ /// True iff the comparison is "strong". i.e. it checks equality and /// not equivalence. - bool isStrong() const { + bool isStrong() const { return isStrong(Kind); } + static bool isStrong(ComparisonCategoryType Kind) { using CCK = ComparisonCategoryType; return Kind == CCK::StrongEquality || Kind == CCK::StrongOrdering; } /// True iff the comparison is not totally ordered. - bool isPartial() const { + bool isPartial() const { return isPartial(Kind); } + static bool isPartial(ComparisonCategoryType Kind) { using CCK = ComparisonCategoryType; return Kind == CCK::PartialOrdering; } + /// Return whether the specified comparison category type can be used as + /// an operand to the specified relational binary operator. + static bool isUsableWithOperator(ComparisonCategoryType CompKind, + BinaryOperatorKind Opcode); + bool isUsableWithOperator(BinaryOperatorKind Opc) const { + return isUsableWithOperator(Kind, Opc); + } + /// Converts the specified result kind into the the correct result kind /// for this category. Specifically it lowers strong equality results to /// weak equivalence if needed. @@ -189,6 +215,28 @@ static StringRef getCategoryString(ComparisonCategoryType Kind); static StringRef getResultString(ComparisonCategoryResult Kind); + /// Return the comparison category information for the + /// "common comparison type" for a specified list of types. If there is no + /// such common comparison type, or if any of the specified types are not + /// comparison category types, null is returned. + const ComparisonCategoryInfo * + computeCommonComparisonType(ArrayRef Types) const; + static Optional + computeCommonComparisonType(ArrayRef Types); + + /// Return the comparison category type which would be returned + /// for a builtin comparison operator taking the specified type, or None if no + /// such type exists. + /// + /// \param Ty The composite comparison type + /// \param IsMixedNullCompare True if exactly one of the operands is a null + /// pointer constant. + static Optional + computeComparisonTypeForBuiltin(QualType Ty, bool IsMixedNullCompare = false); + + static Optional + computeComparisonTypeForBuiltin(QualType LHSTy, QualType RHSTy); + /// Return the list of results which are valid for the specified /// comparison category type. static std::vector @@ -209,6 +257,7 @@ /// NOTE: Lookup is expected to succeed. Use lookupInfo if failure is /// possible. const ComparisonCategoryInfo &getInfoForType(QualType Ty) const; + const ComparisonCategoryInfo *lookupInfoForType(QualType Ty) const; public: /// Return the cached comparison category information for the @@ -223,9 +272,6 @@ } private: - const ComparisonCategoryInfo *lookupInfoForType(QualType Ty) const; - -private: friend class ASTContext; explicit ComparisonCategories(const ASTContext &Ctx) : Ctx(Ctx) {} Index: include/clang/AST/Expr.h =================================================================== --- include/clang/AST/Expr.h +++ include/clang/AST/Expr.h @@ -886,6 +886,9 @@ setIsUnique(false); } + /// Create an OpaqueValueExpr representing the specified source expression + static OpaqueValueExpr *Create(const ASTContext &Ctx, Expr *Source); + /// Given an expression which invokes a copy constructor --- i.e. a /// CXXConstructExpr, possibly wrapped in an ExprWithCleanups --- /// find the OpaqueValueExpr that's the source of the construction. Index: include/clang/AST/ExprCXX.h =================================================================== --- include/clang/AST/ExprCXX.h +++ include/clang/AST/ExprCXX.h @@ -15,6 +15,7 @@ #ifndef LLVM_CLANG_AST_EXPRCXX_H #define LLVM_CLANG_AST_EXPRCXX_H +#include "clang/AST/ComparisonCategories.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" #include "clang/AST/DeclCXX.h" @@ -87,6 +88,8 @@ SourceRange getSourceRangeImpl() const LLVM_READONLY; public: + friend class ASTReader; + friend class ASTWriter; friend class ASTStmtReader; friend class ASTStmtWriter; @@ -4207,6 +4210,65 @@ child_range children() { return child_range(SubExprs, SubExprs + 2); } }; +// FIXME(EricWF): Document this +class CXXRewrittenOperatorExpr : public Expr { +public: + using RewrittenOpKind = RewrittenOverloadCandidateKind; + typedef BinaryOperatorKind Opcode; + +private: + friend class ASTReader; + friend class ASTStmtReader; + friend class ASTStmtWriter; + + Stmt *Rewritten; + + CXXRewrittenOperatorExpr(EmptyShell Empty) + : Expr(CXXRewrittenOperatorExprClass, Empty) {} + +public: + CXXRewrittenOperatorExpr(RewrittenOpKind Kind, Expr *Rewritten) + : Expr(CXXRewrittenOperatorExprClass, Rewritten->getType(), + Rewritten->getValueKind(), Rewritten->getObjectKind(), + /*Dependent*/ Rewritten->isTypeDependent(), + Rewritten->isValueDependent(), + Rewritten->isInstantiationDependent(), + Rewritten->containsUnexpandedParameterPack()), + Rewritten(Rewritten) { + CXXRewrittenOperatorBits.Kind = Kind; + } + + RewrittenOpKind getRewrittenKind() const { + return static_cast(CXXRewrittenOperatorBits.Kind); + } + void setRewrittenKind(RewrittenOpKind Kind) { + CXXRewrittenOperatorBits.Kind = Kind; + } + + Expr *getRewrittenExpr() const { return static_cast(Rewritten); } + Opcode getOpcode() const; + + bool isRewrittenAsReversed() const { + return getRewrittenKind() == ROC_AsReversedThreeWay; + } + bool isRewrittenAsThreeWay() const { return getOriginalOpcode() != BO_Cmp; } + + SourceLocation getLocStart() const { return Rewritten->getLocStart(); } + SourceLocation getLocEnd() const { return Rewritten->getLocEnd(); } + SourceLocation getExprLoc() const { return getRewrittenExpr()->getExprLoc(); } + SourceLocation getOperatorLoc() const; + + Opcode getOriginalOpcode() const; + Expr *getOriginalLHS() const; + Expr *getOriginalRHS() const; + + child_range children() { return child_range(&Rewritten, &Rewritten + 1); } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CXXRewrittenOperatorExprClass; + } +}; + /// Represents an expression that might suspend coroutine execution; /// either a co_await or co_yield expression. /// Index: include/clang/AST/RecursiveASTVisitor.h =================================================================== --- include/clang/AST/RecursiveASTVisitor.h +++ include/clang/AST/RecursiveASTVisitor.h @@ -2548,6 +2548,7 @@ DEF_TRAVERSE_STMT(MaterializeTemporaryExpr, {}) DEF_TRAVERSE_STMT(CXXFoldExpr, {}) DEF_TRAVERSE_STMT(AtomicExpr, {}) +DEF_TRAVERSE_STMT(CXXRewrittenOperatorExpr, {}) // For coroutines expressions, traverse either the operand // as written or the implied calls, depending on what the Index: include/clang/AST/Stmt.h =================================================================== --- include/clang/AST/Stmt.h +++ include/clang/AST/Stmt.h @@ -247,6 +247,14 @@ unsigned IsUnique : 1; }; + class CXXRewrittenOperatorExprBitfields { + friend class CXXRewrittenOperatorExpr; + + unsigned : NumExprBits; + + unsigned Kind : 2; + }; + class ObjCIndirectCopyRestoreExprBitfields { friend class ObjCIndirectCopyRestoreExpr; @@ -309,6 +317,7 @@ InitListExprBitfields InitListExprBits; TypeTraitExprBitfields TypeTraitExprBits; CoawaitExprBitfields CoawaitBits; + CXXRewrittenOperatorExprBitfields CXXRewrittenOperatorBits; }; public: Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -3534,7 +3534,11 @@ "is the implicit copy assignment operator|" "is the implicit move assignment operator|" "inherited constructor|" - "inherited constructor }0%2" + "inherited constructor |" + "rewritten comparison operator|" + "synthesized comparison operator|" + "rewritten comparison operator |" + "synthesized comparison operator }0%2" "%select{| has different class%diff{ (expected $ but has $)|}4,5" "| has different number of parameters (expected %4 but has %5)" "| has type mismatch at %ordinal4 parameter" @@ -3622,7 +3626,9 @@ "function (the implicit copy assignment operator)|" "function (the implicit move assignment operator)|" "inherited constructor|" - "inherited constructor}0 %select{|template }1" + "inherited constructor|" + "rewritten comparison operator|sythesized comparison operator|" + "rewritten comparison operator|synthesized comparison operator}0 %select{|template }1" "not viable: requires%select{ at least| at most|}2 %3 argument%s3, but %4 " "%plural{1:was|:were}4 provided">; @@ -3634,7 +3640,10 @@ "function (the implicit copy assignment operator)|" "function (the implicit move assignment operator)|" "inherited constructor|" - "inherited constructor}0 %select{|template }1not viable: " + "inherited constructor|" + "rewritten comparison operator|synthesized comparison operator|" + "rewritten comparison operator|sythesized comparison operator}0 " + "%select{|template }1not viable: " "%select{requires at least|allows at most single|requires single}2 " "argument %3, but %plural{0:no|:%4}4 arguments were provided">; @@ -3647,7 +3656,9 @@ "function (the implicit copy assignment operator)|" "function (the implicit move assignment operator)|" "inherited constructor|" - "inherited constructor }0%1 has been " + "inherited constructor |" + "rewritten comparison operator|synthesized comparison operator|" + "rewritten comparison operator |synthesized comparison operator }0%1 has been " "%select{explicitly made unavailable|explicitly deleted|" "implicitly deleted}2">; @@ -3665,7 +3676,9 @@ "function (the implicit copy assignment operator)|" "function (the implicit move assignment operator)|" "inherited constructor|" - "inherited constructor }0%1 " + "inherited constructor |" + "rewritten comparison operator|synthesized comparison operator|" + "rewritten comparison operator |synthesized comparison operator }0%1 " "not viable: cannot convert argument of incomplete type " "%diff{$ to $|to parameter type}2,3 for " "%select{%ordinal5 argument|object argument}4" @@ -3682,7 +3695,9 @@ "function (the implicit copy assignment operator)|" "function (the implicit move assignment operator)|" "inherited constructor|" - "inherited constructor }0%1 " + "inherited constructor |" + "rewritten comparison operator|synthesized comparison operator|" + "rewritten comparison operator |synthesized comparison operator }0%1 " "not viable: cannot convert initializer list argument to %3">; def note_ovl_candidate_bad_overload : Note<"candidate " "%select{function|function|constructor|" @@ -3693,7 +3708,9 @@ "function (the implicit copy assignment operator)|" "function (the implicit move assignment operator)|" "inherited constructor|" - "inherited constructor }0%1" + "inherited constructor " + "rewritten comparison operator|synthesized comparison operator|" + "rewritten comparison operator |synthesized comparison operator }0%1" " not viable: no overload of %3 matching %2 for %ordinal4 argument">; def note_ovl_candidate_bad_conv : Note<"candidate " "%select{function|function|constructor|" @@ -3704,7 +3721,9 @@ "function (the implicit copy assignment operator)|" "function (the implicit move assignment operator)|" "inherited constructor|" - "inherited constructor }0%1" + "inherited constructor |" + "rewritten comparison operator|synthesized comparison operator|" + "rewritten comparison operator |synthesized comparison operator }0%1" " not viable: no known conversion " "%diff{from $ to $|from argument type to parameter type}2,3 for " "%select{%ordinal5 argument|object argument}4" @@ -3721,7 +3740,9 @@ "function (the implicit copy assignment operator)|" "function (the implicit move assignment operator)|" "inherited constructor|" - "inherited constructor }0%1" + "inherited constructor |" + "rewritten comparison operator|synthesized comparison operator|" + "rewritten comparison operator |synthesized comparison operator }0%1" " not viable: cannot implicitly convert argument " "%diff{of type $ to $|type to parameter type}2,3 for " "%select{%ordinal5 argument|object argument}4 under ARC">; @@ -3734,7 +3755,9 @@ "function (the implicit copy assignment operator)|" "function (the implicit move assignment operator)|" "inherited constructor|" - "inherited constructor }0%1" + "inherited constructor |" + "rewritten comparison operator|synthesized comparison operator|" + "rewritten comparison operator |synthesized comparison operator }0%1" " not viable: expects an l-value for " "%select{%ordinal3 argument|object argument}2">; def note_ovl_candidate_bad_addrspace : Note<"candidate " @@ -3746,8 +3769,10 @@ "function (the implicit copy assignment operator)|" "function (the implicit move assignment operator)|" "inherited constructor|" - "inherited constructor }0%1 not viable: " - "%select{%ordinal6|'this'}5 argument (%2) is in " + "inherited constructor |" + "rewritten comparison operator|synthesized comparison operator|" + "rewritten comparison operator |synthesized comparison operator }0%1" + " not viable: %select{%ordinal6|'this'}5 argument (%2) is in " "address space %3, but parameter must be in address space %4">; def note_ovl_candidate_bad_gc : Note<"candidate " "%select{function|function|constructor|" @@ -3758,7 +3783,8 @@ "function (the implicit copy assignment operator)|" "function (the implicit move assignment operator)|" "inherited constructor|" - "inherited constructor }0%1 not viable: " + "inherited constructor }0%1" + " not viable: " "%select{%ordinal6|'this'}5 argument (%2) has %select{no|__weak|__strong}3 " "ownership, but parameter has %select{no|__weak|__strong}4 ownership">; def note_ovl_candidate_bad_ownership : Note<"candidate " @@ -3770,7 +3796,9 @@ "function (the implicit copy assignment operator)|" "function (the implicit move assignment operator)|" "inherited constructor|" - "inherited constructor }0%1 not viable: " + "inherited constructor |" + "rewritten comparison operator|synthesized comparison operator|" + "rewritten comparison operator |synthesized comparison operator }0%1 not viable: " "%select{%ordinal6|'this'}5 argument (%2) has " "%select{no|__unsafe_unretained|__strong|__weak|__autoreleasing}3 ownership," " but parameter has %select{no|__unsafe_unretained|__strong|__weak|" @@ -3791,7 +3819,9 @@ "function (the implicit copy assignment operator)|" "function (the implicit move assignment operator)|" "inherited constructor|" - "inherited constructor }0%1 not viable: " + "inherited constructor |" + "rewritten comparison operator|synthesized comparison operator|" + "rewritten comparison operator |synthesized comparison operator }0%1 not viable: " "%ordinal4 argument (%2) would lose " "%select{const|restrict|const and restrict|volatile|const and volatile|" "volatile and restrict|const, volatile, and restrict}3 qualifier" @@ -3805,7 +3835,9 @@ "function (the implicit copy assignment operator)|" "function (the implicit move assignment operator)|" "inherited constructor|" - "inherited constructor }0%1 not viable: " + "inherited constructor |" + "rewritten comparison operator|synthesized comparison operator|" + "rewritten comparison operator |synthesized comparison operator }0%1 not viable: " "%ordinal4 argument (%2) would lose __unaligned qualifier">; def note_ovl_candidate_bad_base_to_derived_conv : Note<"candidate " "%select{function|function|constructor|" @@ -3816,7 +3848,9 @@ "function (the implicit copy assignment operator)|" "function (the implicit move assignment operator)|" "inherited constructor|" - "inherited constructor }0%1 not viable: " + "inherited constructor |" + "rewritten comparison operator|synthesized comparison operator|" + "rewritten comparison operator |synthesized comparison operator }0%1 not viable: " "cannot %select{convert from|convert from|bind}2 " "%select{base class pointer|superclass|base class object of type}2 %3 to " "%select{derived class pointer|subclass|derived class reference}2 %4 for " @@ -3830,7 +3864,9 @@ "function (the implicit copy assignment operator)|" "function (the implicit move assignment operator)|" "inherited constructor|" - "inherited constructor}0 not viable: " + "inherited constructor|" + "rewritten comparison operator|synthesized comparison operator|" + "rewritten comparison operator|synthesized comparison operator}0 not viable: " "call to " "%select{__device__|__global__|__host__|__host__ __device__|invalid}1 function from" " %select{__device__|__global__|__host__|__host__ __device__|invalid}2 function">; Index: include/clang/Basic/StmtNodes.td =================================================================== --- include/clang/Basic/StmtNodes.td +++ include/clang/Basic/StmtNodes.td @@ -146,6 +146,7 @@ def MaterializeTemporaryExpr : DStmt; def LambdaExpr : DStmt; def CXXFoldExpr : DStmt; +def CXXRewrittenOperatorExpr : DStmt; // C++ Coroutines TS expressions def CoroutineSuspendExpr : DStmt; Index: include/clang/Sema/Overload.h =================================================================== --- include/clang/Sema/Overload.h +++ include/clang/Sema/Overload.h @@ -719,6 +719,10 @@ /// This candidate was not viable because it is a non-default multiversioned /// function. ovl_non_default_multiversion_function, + + // Thes candidate was not viable because the return type of the rewritten + // expression was not a valid operand to the original binary operator. + ovl_rewritten_operand_non_valid_for_operator }; /// A list of implicit conversion sequences for the arguments of an @@ -755,12 +759,12 @@ ConversionFixItGenerator Fix; /// Viable - True to indicate that this overload candidate is viable. - bool Viable; + bool Viable : 1; /// IsSurrogate - True to indicate that this candidate is a /// surrogate for a conversion to a function pointer or reference /// (C++ [over.call.object]). - bool IsSurrogate; + bool IsSurrogate : 1; /// IgnoreObjectArgument - True to indicate that the first /// argument's conversion, which for this function represents the @@ -769,7 +773,11 @@ /// implicit object argument is just a placeholder) or a /// non-static member function when the call doesn't have an /// object argument. - bool IgnoreObjectArgument; + bool IgnoreObjectArgument : 1; + + /// RewrittenKind - For rewritten operator candidates, the kind of rewritten + /// candidate it is: rewritten or synthesized. + unsigned char RewrittenOpKind : 2; /// FailureKind - The reason why this candidate is not viable. /// Actually an OverloadFailureKind. @@ -812,6 +820,24 @@ return CanFix; } + /// \brief Return the index of the conversion corresponding to the specified + /// argument index. If this is not a synthesized candidate, 'Idx' is + /// returned. Otherwise the index corresponding to the reversed parameter + /// is returned. + unsigned getConversionIndexForArgIndex(unsigned Idx) const; + + /// \brief Return the conversion sequence for the specified argument index. + /// If this is a synthesized candidate, the argument index is reversed. + const ImplicitConversionSequence &getConversion(unsigned ArgIdx) const; + + /// \brief Returns the parameter type corresponding to the specified index. + /// (The index is not reversed for synthesized candidates). + QualType getParamType(unsigned Idx) const { + if (Function) + return Function->getParamDecl(Idx)->getType(); + return BuiltinParamTypes[Idx]; + } + unsigned getNumParams() const { if (IsSurrogate) { auto STy = Surrogate->getConversionType(); @@ -823,6 +849,10 @@ return Function->getNumParams(); return ExplicitCallArguments; } + + RewrittenOverloadCandidateKind getRewrittenKind() const { + return static_cast(RewrittenOpKind); + } }; /// OverloadCandidateSet - A set of overload candidates, used in C++ @@ -853,8 +883,13 @@ private: SmallVector Candidates; - llvm::SmallPtrSet Functions; + using DeclKindPair = + llvm::PointerIntPair; + using DeclKindSet = llvm::SmallSet; + DeclKindSet Functions; + + private: // Allocator for ConversionSequenceLists. We store the first few of these // inline to avoid allocation for small sets. llvm::BumpPtrAllocator SlabAllocator; @@ -862,6 +897,8 @@ SourceLocation Loc; CandidateSetKind Kind; + RewrittenOverloadCandidateKind AddingOverloadKind; + constexpr static unsigned NumInlineBytes = 24 * sizeof(ImplicitConversionSequence); unsigned NumInlineBytesUsed = 0; @@ -896,19 +933,40 @@ void destroyCandidates(); public: + friend struct AddingRewrittenCandidateGuard; + struct AddingRewrittenCandidateGuard { + AddingRewrittenCandidateGuard( + OverloadCandidateSet &Candidates, + RewrittenOverloadCandidateKind NewOverloadKind) + : Candidates(Candidates) { + OldRewrittenKind = Candidates.AddingOverloadKind; + Candidates.AddingOverloadKind = NewOverloadKind; + } + ~AddingRewrittenCandidateGuard() { + Candidates.AddingOverloadKind = OldRewrittenKind; + } + OverloadCandidateSet &Candidates; + RewrittenOverloadCandidateKind OldRewrittenKind; + }; + + public: OverloadCandidateSet(SourceLocation Loc, CandidateSetKind CSK) - : Loc(Loc), Kind(CSK) {} + : Loc(Loc), Kind(CSK), AddingOverloadKind(ROC_None) {} OverloadCandidateSet(const OverloadCandidateSet &) = delete; OverloadCandidateSet &operator=(const OverloadCandidateSet &) = delete; ~OverloadCandidateSet() { destroyCandidates(); } SourceLocation getLocation() const { return Loc; } CandidateSetKind getKind() const { return Kind; } + RewrittenOverloadCandidateKind getRewrittenKind() const { + return AddingOverloadKind; + } /// Determine when this overload candidate will be new to the /// overload set. bool isNewCandidate(Decl *F) { - return Functions.insert(F->getCanonicalDecl()).second; + DeclKindPair P(F->getCanonicalDecl(), AddingOverloadKind); + return Functions.insert(P).second; } /// Clear out all of the candidates. @@ -948,6 +1006,7 @@ C.Conversions = Conversions.empty() ? allocateConversionSequences(NumConversions) : Conversions; + C.RewrittenOpKind = AddingOverloadKind; return C; } Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -43,6 +43,7 @@ #include "clang/Sema/ExternalSemaSource.h" #include "clang/Sema/IdentifierResolver.h" #include "clang/Sema/ObjCMethodList.h" +#include "clang/Sema/Overload.h" #include "clang/Sema/Ownership.h" #include "clang/Sema/Scope.h" #include "clang/Sema/TypoCorrection.h" @@ -2784,11 +2785,18 @@ TemplateArgumentListInfo *ExplicitTemplateArgs, OverloadCandidateSet& CandidateSet, bool PartialOverloading = false); - + void AddRewrittenOperatorCandidates(OverloadedOperatorKind Op, + SourceLocation OpLoc, + ArrayRef InputArgs, + const UnresolvedSetImpl &Fns, + OverloadCandidateSet &CandidateSet, + bool PerformADL); // Emit as a 'note' the specific overload candidate void NoteOverloadCandidate(NamedDecl *Found, FunctionDecl *Fn, QualType DestType = QualType(), - bool TakingAddress = false); + bool TakingAddress = false, + RewrittenOverloadCandidateKind ROC = ROC_None); + void NoteOverloadCandidate(OverloadCandidate *Ovl); // Emit as a series of 'note's all template and non-templates identified by // the expression Expr @@ -2920,16 +2928,21 @@ const UnresolvedSetImpl &Fns, Expr *input, bool RequiresADL = true); - ExprResult CreateOverloadedBinOp(SourceLocation OpLoc, - BinaryOperatorKind Opc, - const UnresolvedSetImpl &Fns, - Expr *LHS, Expr *RHS, - bool RequiresADL = true); + ExprResult CreateOverloadedBinOp(SourceLocation OpLoc, BinaryOperatorKind Opc, + const UnresolvedSetImpl &Fns, Expr *LHS, + Expr *RHS, bool RequiresADL = true, + bool AllowRewrittenCandidates = true); ExprResult CreateOverloadedArraySubscriptExpr(SourceLocation LLoc, SourceLocation RLoc, Expr *Base,Expr *Idx); + ExprResult BuildBinaryOperatorCandidate(SourceLocation OpLoc, + BinaryOperatorKind Opc, + const OverloadCandidate &Ovl, + Expr *LHS, Expr *RHS, + bool HadMultipleCandidates); + ExprResult BuildCallToMemberFunction(Scope *S, Expr *MemExpr, SourceLocation LParenLoc, Index: include/clang/Serialization/ASTBitCodes.h =================================================================== --- include/clang/Serialization/ASTBitCodes.h +++ include/clang/Serialization/ASTBitCodes.h @@ -1811,6 +1811,7 @@ EXPR_FUNCTION_PARM_PACK, // FunctionParmPackExpr EXPR_MATERIALIZE_TEMPORARY, // MaterializeTemporaryExpr EXPR_CXX_FOLD, // CXXFoldExpr + EXPR_CXX_REWRITTEN_OPERATOR, // CXXRewrittenOperatorExpr // CUDA EXPR_CUDA_KERNEL_CALL, // CUDAKernelCallExpr Index: lib/AST/ComparisonCategories.cpp =================================================================== --- lib/AST/ComparisonCategories.cpp +++ lib/AST/ComparisonCategories.cpp @@ -209,3 +209,132 @@ Values.push_back(CCR::Unordered); return Values; } + +Optional +ComparisonCategories::computeComparisonTypeForBuiltin(QualType Ty, + bool IsMixedNullCompare) { + using CCT = ComparisonCategoryType; + if (const ComplexType *CT = Ty->getAs()) { + if (CT->getElementType()->hasFloatingRepresentation()) + return CCT::WeakEquality; + return CCT::StrongEquality; + } + if (Ty->isIntegralOrEnumerationType()) + return CCT::StrongOrdering; + if (Ty->hasFloatingRepresentation()) + return CCT::PartialOrdering; + // C++2a [expr.spaceship]p7: If the composite pointer type is a function + // pointer type, a pointer-to-member type, or std::nullptr_t, the + // result is of type std::strong_equality + if (Ty->isFunctionPointerType() || Ty->isMemberPointerType() || + Ty->isNullPtrType()) + // FIXME: consider making the function pointer case produce + // strong_ordering not strong_equality, per P0946R0-Jax18 discussion + // and direction polls + return CCT::StrongEquality; + // C++2a [expr.spaceship]p8: If the composite pointer type is an object + // pointer type, p <=> q is of type std::strong_ordering. + if (Ty->isPointerType()) { + // P0946R0: Comparisons between a null pointer constant and an object + // pointer result in std::strong_equality + if (IsMixedNullCompare) + return ComparisonCategoryType::StrongEquality; + return CCT::StrongOrdering; + } + return None; +} + +// FIXME(EricWF): The rules to deduce the composite argument type are actually +// quite complicated. However the rules to compute the comparison type for a +// single builtin argument type are simple, and so are the rules to compute the +// common comparison type. So we do that to determine the "likely" comparison +// category type for the specified set of parameter types for a builtin +// function. +// +// This information is used during overload resolution to determine if the +// rewritten expression '(LHS <=> RHS) @ 0' (or the reversed candidate) is +// valid for a given '@'. For example `(MemPtr <=> MemPtr) < 0` is ill-formed +// because `std::strong_equality` cannot be used in a relational operator. +Optional +ComparisonCategories::computeComparisonTypeForBuiltin(QualType LHSTy, + QualType RHSTy) { + QualType Args[2] = {LHSTy, RHSTy}; + SmallVector TypeKinds; + for (auto QT : Args) { + Optional CompType = + computeComparisonTypeForBuiltin(QT); + if (!CompType) + return None; + TypeKinds.push_back(*CompType); + } + return computeCommonComparisonType(TypeKinds); +} + +bool ComparisonCategoryInfo::isUsableWithOperator( + ComparisonCategoryType CompKind, BinaryOperatorKind Opcode) { + assert(BinaryOperator::isComparisonOp(Opcode)); + if (BinaryOperator::isRelationalOp(Opcode)) + return isOrdered(CompKind); + // We either have an equality or three-way opcode. These are all OK for + // any comparison category type. + return true; +} + +/// C++2a [class.spaceship]p4 - compute the common category type. +const ComparisonCategoryInfo *ComparisonCategories::computeCommonComparisonType( + ArrayRef Types) const { + SmallVector Kinds; + // If any type is not a comparison category, return nullptr. + for (auto Ty : Types) { + const ComparisonCategoryInfo *Info = lookupInfoForType(Ty); + // --- If any T is not a comparison category type, U is void. + if (!Info) + return nullptr; + Kinds.push_back(Info->Kind); + } + Optional CommonType = + computeCommonComparisonType(Kinds); + if (!CommonType) + return nullptr; + return lookupInfo(*CommonType); +} + +Optional +ComparisonCategories::computeCommonComparisonType( + ArrayRef Types) { + using CCT = ComparisonCategoryType; + std::array(CCT::Last) + 1> Seen = {}; + auto Count = [&](CCT T) { return Seen[static_cast(T)]; }; + + // Count the number of times each comparison category type occurs in the + // specified type list. + for (auto TyKind : Types) + Seen[static_cast(TyKind)]++; + + // --- Otherwise, if at least one Ti is std::weak_equality, or at least one + // Ti is std::strong_equality and at least one Tj is + // std::partial_ordering or std::weak_ordering, U is + // std::weak_equality. + if (Count(CCT::WeakEquality) || + (Count(CCT::StrongEquality) && + (Count(CCT::PartialOrdering) || Count(CCT::WeakOrdering)))) + return CCT::WeakEquality; + + // --- Otherwise, if at least one Ti is std::strong_equality, U is + // std::strong_equality + if (Count(CCT::StrongEquality)) + return CCT::StrongEquality; + + // --- Otherwise, if at least one Ti is std::partial_ordering, U is + // std::partial_ordering. + if (Count(CCT::PartialOrdering)) + return CCT::PartialOrdering; + + // --- Otherwise, if at least one Ti is std::weak_ordering, U is + // std::weak_ordering. + if (Count(CCT::WeakOrdering)) + return CCT::WeakOrdering; + + // --- Otherwise, U is std::strong_ordering. + return CCT::StrongOrdering; +} Index: lib/AST/Expr.cpp =================================================================== --- lib/AST/Expr.cpp +++ lib/AST/Expr.cpp @@ -3188,6 +3188,11 @@ return false; } + case CXXRewrittenOperatorExprClass: { + const auto *RO = cast(this); + return RO->getRewrittenExpr()->HasSideEffects(Ctx, IncludePossibleEffects); + } + case PseudoObjectExprClass: { // Only look for side-effects in the semantic form, and look past // OpaqueValueExpr bindings in that form. @@ -3898,6 +3903,11 @@ } } +OpaqueValueExpr *OpaqueValueExpr::Create(const ASTContext &Ctx, Expr *E) { + return new (Ctx) OpaqueValueExpr(E->getExprLoc(), E->getType(), + E->getValueKind(), E->getObjectKind(), E); +} + const OpaqueValueExpr *OpaqueValueExpr::findInCopyConstruct(const Expr *e) { if (const ExprWithCleanups *ewc = dyn_cast(e)) e = ewc->getSubExpr(); Index: lib/AST/ExprCXX.cpp =================================================================== --- lib/AST/ExprCXX.cpp +++ lib/AST/ExprCXX.cpp @@ -1433,3 +1433,100 @@ } void ArrayTypeTraitExpr::anchor() {} + +namespace { +struct ArgumentExtractor { + Expr *Rewritten; + bool IsReverseOrder; + bool IsThreeWay; + + ArgumentExtractor(const CXXRewrittenOperatorExpr *E) + : Rewritten(E->getRewrittenExpr()) { + IsReverseOrder = E->getRewrittenKind() == ROC_AsReversedThreeWay; + IsThreeWay = getOpcode(Rewritten) == BO_Cmp; + } + + static CXXRewrittenOperatorExpr::Opcode getOpcode(Expr *E) { + if (BinaryOperator *BO = dyn_cast(E)) + return BO->getOpcode(); + if (auto *CE = dyn_cast(E)) + return BinaryOperator::getOverloadedOpcode(CE->getOperator()); + llvm_unreachable("unhandled case"); + } + + CXXRewrittenOperatorExpr::Opcode getOriginalOpcode() const { + if (IsThreeWay) + return BO_Cmp; + if (IsReverseOrder) + return getOpcode(getRHS(Rewritten)); + return getOpcode(Rewritten); + } + + static Expr *getLHS(Expr *E) { + if (BinaryOperator *BO = dyn_cast(E)) + return BO->getLHS(); + if (auto *CE = dyn_cast(E)) + return CE->getArg(0); + llvm_unreachable("unhandled case"); + } + + static Expr *getRHS(Expr *E) { + if (BinaryOperator *BO = dyn_cast(E)) + return BO->getRHS(); + if (auto *CE = dyn_cast(E)) + return CE->getArg(1); + llvm_unreachable("unhandled case"); + } + + Expr *getOriginalLHS() const { + if (IsThreeWay) { + if (IsReverseOrder) + return getRHS(Rewritten); + return getLHS(Rewritten); + } + if (IsReverseOrder) + return getRHS(getRHS(Rewritten)); + return getLHS(getLHS(Rewritten)); + } + + Expr *getOriginalRHS() const { + if (IsThreeWay) { + if (IsReverseOrder) + return getLHS(Rewritten); + return getRHS(Rewritten); + } + if (IsReverseOrder) + return getRHS(getLHS(Rewritten)); + return getLHS(getRHS(Rewritten)); + } + + static SourceLocation getOperatorLoc(Expr *E) { + if (BinaryOperator *BO = dyn_cast(E)) + return BO->getOperatorLoc(); + if (auto *CE = dyn_cast(E)) + return CE->getOperatorLoc(); + llvm_unreachable("unhandled case"); + } +}; +} // namespace + +SourceLocation CXXRewrittenOperatorExpr::getOperatorLoc() const { + return ArgumentExtractor::getOperatorLoc(this->getRewrittenExpr()); +} + +CXXRewrittenOperatorExpr::Opcode CXXRewrittenOperatorExpr::getOpcode() const { + return ArgumentExtractor::getOpcode(getRewrittenExpr()); +} + +CXXRewrittenOperatorExpr::Opcode +CXXRewrittenOperatorExpr::getOriginalOpcode() const { + return ArgumentExtractor{this}.getOriginalOpcode(); +} + +Expr *CXXRewrittenOperatorExpr::getOriginalLHS() const { + return ArgumentExtractor{this}.getOriginalLHS(); +} + +Expr *CXXRewrittenOperatorExpr::getOriginalRHS() const { + return ArgumentExtractor{this}.getOriginalRHS(); +} Index: lib/AST/ExprClassification.cpp =================================================================== --- lib/AST/ExprClassification.cpp +++ lib/AST/ExprClassification.cpp @@ -287,7 +287,9 @@ if (cast(E)->isResultDependent()) return Cl::CL_PRValue; return ClassifyInternal(Ctx,cast(E)->getResultExpr()); - + case Expr::CXXRewrittenOperatorExprClass: + return ClassifyInternal( + Ctx, cast(E)->getRewrittenExpr()); case Expr::BinaryOperatorClass: case Expr::CompoundAssignOperatorClass: // C doesn't have any binary expressions that are lvalues. Index: lib/AST/ExprConstant.cpp =================================================================== --- lib/AST/ExprConstant.cpp +++ lib/AST/ExprConstant.cpp @@ -5059,6 +5059,10 @@ return; VisitIgnoredValue(E); } + + bool VisitCXXRewrittenOperatorExpr(const CXXRewrittenOperatorExpr *E) { + return StmtVisitorTy::Visit(E->getRewrittenExpr()); + } }; } // namespace @@ -10859,6 +10863,8 @@ case Expr::ChooseExprClass: { return CheckICE(cast(E)->getChosenSubExpr(), Ctx); } + case Expr::CXXRewrittenOperatorExprClass: + return CheckICE(cast(E)->getRewrittenExpr(), Ctx); } llvm_unreachable("Invalid StmtClass!"); Index: lib/AST/ItaniumMangle.cpp =================================================================== --- lib/AST/ItaniumMangle.cpp +++ lib/AST/ItaniumMangle.cpp @@ -3516,9 +3516,13 @@ } // These are used for internal purposes and cannot be meaningfully mangled. - case Expr::OpaqueValueExprClass: - llvm_unreachable("cannot mangle opaque value; mangling wrong thing?"); - + case Expr::OpaqueValueExprClass: { + const OpaqueValueExpr *OVE = cast(E); + assert(OVE->getSourceExpr() && "cannot mangle opaque value without a " + "source expression; mangling wrong thing?"); + mangleExpression(OVE->getSourceExpr()); + break; + } case Expr::InitListExprClass: { Out << "il"; mangleInitListElements(cast(E)); @@ -3559,6 +3563,17 @@ mangleExpression(cast(E)->getSubExpr(), Arity); break; + case Expr::CXXRewrittenOperatorExprClass: { + const CXXRewrittenOperatorExpr *RE = cast(E); + const unsigned NumArgs = 2; + auto Operator = + BinaryOperator::getOverloadedOperator(RE->getOriginalOpcode()); + mangleOperatorName(Operator, NumArgs); + mangleExpression(RE->getOriginalLHS()); + mangleExpression(RE->getOriginalRHS()); + break; + } + case Expr::SubstNonTypeTemplateParmExprClass: mangleExpression(cast(E)->getReplacement(), Arity); Index: lib/AST/StmtPrinter.cpp =================================================================== --- lib/AST/StmtPrinter.cpp +++ lib/AST/StmtPrinter.cpp @@ -2588,6 +2588,15 @@ OS << ")"; } +void StmtPrinter::VisitCXXRewrittenOperatorExpr(CXXRewrittenOperatorExpr *E) { + // FIXME(EricWF): Are there ever cases where we want to display the rewritten + // code? For example when producing diagnostics on implicitly generated + // expressions? + PrintExpr(E->getOriginalLHS()); + OS << ' ' << BinaryOperator::getOpcodeStr(E->getOriginalOpcode()) << ' '; + PrintExpr(E->getOriginalRHS()); +} + // C++ Coroutines TS void StmtPrinter::VisitCoroutineBodyStmt(CoroutineBodyStmt *S) { Index: lib/AST/StmtProfile.cpp =================================================================== --- lib/AST/StmtProfile.cpp +++ lib/AST/StmtProfile.cpp @@ -1816,6 +1816,13 @@ ID.AddInteger(S->getOperator()); } +void StmtProfiler::VisitCXXRewrittenOperatorExpr( + const CXXRewrittenOperatorExpr *S) { + VisitExpr(S); + ID.AddInteger(S->getRewrittenKind()); + VisitExpr(S->getRewrittenExpr()); +} + void StmtProfiler::VisitCoroutineBodyStmt(const CoroutineBodyStmt *S) { VisitStmt(S); } Index: lib/CodeGen/CGExprScalar.cpp =================================================================== --- lib/CodeGen/CGExprScalar.cpp +++ lib/CodeGen/CGExprScalar.cpp @@ -541,6 +541,10 @@ return EmitScalarPrePostIncDec(E, LV, true, true); } + Value *VisitCXXRewrittenOperatorExpr(const CXXRewrittenOperatorExpr *E) { + return Visit(E->getRewrittenExpr()); + } + llvm::Value *EmitIncDecConsiderOverflowBehavior(const UnaryOperator *E, llvm::Value *InVal, bool IsInc); Index: lib/Sema/SemaExceptionSpec.cpp =================================================================== --- lib/Sema/SemaExceptionSpec.cpp +++ lib/Sema/SemaExceptionSpec.cpp @@ -1174,6 +1174,9 @@ case Expr::VAArgExprClass: return canSubExprsThrow(*this, E); + case Expr::CXXRewrittenOperatorExprClass: + return canThrow(cast(E)->getRewrittenExpr()); + // Some might be dependent for other reasons. case Expr::ArraySubscriptExprClass: case Expr::OMPArraySectionExprClass: Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -9797,8 +9797,6 @@ ExprResult &LHS, ExprResult &RHS, SourceLocation Loc) { - using CCT = ComparisonCategoryType; - QualType LHSType = LHS.get()->getType(); QualType RHSType = RHS.get()->getType(); // Dig out the original argument type and expression before implicit casts @@ -9865,22 +9863,11 @@ if (HasNarrowing) return QualType(); - assert(!Type.isNull() && "composite type for <=> has not been set"); + Optional TypeKind = + ComparisonCategories::computeComparisonTypeForBuiltin(Type); + assert(TypeKind && "composite type for <=> has not been set"); - auto TypeKind = [&]() { - if (const ComplexType *CT = Type->getAs()) { - if (CT->getElementType()->hasFloatingRepresentation()) - return CCT::WeakEquality; - return CCT::StrongEquality; - } - if (Type->isIntegralOrEnumerationType()) - return CCT::StrongOrdering; - if (Type->hasFloatingRepresentation()) - return CCT::PartialOrdering; - llvm_unreachable("other types are unimplemented"); - }(); - - return S.CheckComparisonCategoryType(TypeKind, Loc); + return S.CheckComparisonCategoryType(TypeKind.getValue(), Loc); } static QualType checkArithmeticOrEnumeralCompare(Sema &S, ExprResult &LHS, @@ -9975,32 +9962,12 @@ QualType CompositeTy = LHS.get()->getType(); assert(!CompositeTy->isReferenceType()); - auto buildResultTy = [&](ComparisonCategoryType Kind) { - return CheckComparisonCategoryType(Kind, Loc); - }; - - // C++2a [expr.spaceship]p7: If the composite pointer type is a function - // pointer type, a pointer-to-member type, or std::nullptr_t, the - // result is of type std::strong_equality - if (CompositeTy->isFunctionPointerType() || - CompositeTy->isMemberPointerType() || CompositeTy->isNullPtrType()) - // FIXME: consider making the function pointer case produce - // strong_ordering not strong_equality, per P0946R0-Jax18 discussion - // and direction polls - return buildResultTy(ComparisonCategoryType::StrongEquality); - - // C++2a [expr.spaceship]p8: If the composite pointer type is an object - // pointer type, p <=> q is of type std::strong_ordering. - if (CompositeTy->isPointerType()) { - // P0946R0: Comparisons between a null pointer constant and an object - // pointer result in std::strong_equality - if (LHSIsNull != RHSIsNull) - return buildResultTy(ComparisonCategoryType::StrongEquality); - return buildResultTy(ComparisonCategoryType::StrongOrdering); - } - // C++2a [expr.spaceship]p9: Otherwise, the program is ill-formed. - // TODO: Extend support for operator<=> to ObjC types. + Optional TypeKind = + ComparisonCategories::computeComparisonTypeForBuiltin( + CompositeTy, LHSIsNull != RHSIsNull); + if (!TypeKind) return InvalidOperands(Loc, LHS, RHS); + return CheckComparisonCategoryType(TypeKind.getValue(), Loc); }; @@ -12370,7 +12337,12 @@ if (Sc && OverOp != OO_None && OverOp != OO_Equal) S.LookupOverloadedOperatorName(OverOp, Sc, LHS->getType(), RHS->getType(), Functions); - + if (S.getLangOpts().CPlusPlus2a) { + if (Sc && Opc != BO_Cmp && BinaryOperator::isRelationalOp(Opc)) { + S.LookupOverloadedOperatorName(OO_Spaceship, Sc, LHS->getType(), + RHS->getType(), Functions); + } + } // Build the (potentially-overloaded, potentially-dependent) // binary operation. return S.CreateOverloadedBinOp(OpLoc, Opc, Functions, LHS, RHS); Index: lib/Sema/SemaOverload.cpp =================================================================== --- lib/Sema/SemaOverload.cpp +++ lib/Sema/SemaOverload.cpp @@ -831,6 +831,19 @@ } } +const ImplicitConversionSequence & +OverloadCandidate::getConversion(unsigned ArgIdx) const { + return Conversions[getConversionIndexForArgIndex(ArgIdx)]; +} + +unsigned OverloadCandidate::getConversionIndexForArgIndex(unsigned Idx) const { + if (getRewrittenKind() != ROC_AsReversedThreeWay) + return Idx; + // FIXME(EricWF): Handle these cases. + assert(Idx < 2); + return Idx == 0 ? 1 : 0; +} + void OverloadCandidateSet::destroyCandidates() { for (iterator i = begin(), e = end(); i != e; ++i) { for (auto &C : i->Conversions) @@ -5942,13 +5955,15 @@ // (possibly cv-qualified) T2", when T2 is an enumeration type, are // candidate functions. if (CandidateSet.getKind() == OverloadCandidateSet::CSK_Operator && - !IsAcceptableNonMemberOperatorCandidate(Context, Function, Args)) + !IsAcceptableNonMemberOperatorCandidate(Context, Function, Args)) { return; + } // C++11 [class.copy]p11: [DR1402] // A defaulted move constructor that is defined as deleted is ignored by // overload resolution. CXXConstructorDecl *Constructor = dyn_cast(Function); + if (Constructor && Constructor->isDefaulted() && Constructor->isDeleted() && Constructor->isMoveConstructor()) return; @@ -8862,6 +8877,114 @@ } } + +/// Add the rewritten and synthesized candidates for binary comparison +/// operators. No additional semantic checking is done to see if the candidate +/// is well formed. +void Sema::AddRewrittenOperatorCandidates(OverloadedOperatorKind Op, + SourceLocation OpLoc, + ArrayRef InputArgs, + const UnresolvedSetImpl &Fns, + OverloadCandidateSet &CandidateSet, + bool PerformADL) { + auto Opc = BinaryOperator::getOverloadedOpcode(Op); + bool IsEquality = BinaryOperator::isEqualityOp(Opc); + bool IsRelational = BinaryOperator::isRelationalOp(Opc); + bool IsRelationalOrEquality = IsEquality || IsRelational; + if (!IsRelationalOrEquality && Opc != BO_Cmp) + return; + assert(InputArgs.size() == 2); + + OverloadedOperatorKind CmpOp = OO_Spaceship; + DeclarationName OpName = Context.DeclarationNames.getCXXOperatorName(CmpOp); + + // AddCandidates - Add operator<=> candidates for the specified set of args, + // and mark all newly generated candidates as having the specified + // 'RewrittenOverloadCandidateKind'. + auto AddCandidates = [&](ArrayRef Args, + RewrittenOverloadCandidateKind Kind) { + OverloadCandidateSet::AddingRewrittenCandidateGuard Guard(CandidateSet, + Kind); + + unsigned InitialSize = CandidateSet.size(); + AddFunctionCandidates(Fns, Args, CandidateSet); + AddMemberOperatorCandidates(CmpOp, OpLoc, Args, CandidateSet); + if (PerformADL) + AddArgumentDependentLookupCandidates(OpName, OpLoc, Args, + /*ExplicitTemplateArgs*/ nullptr, + CandidateSet); + AddBuiltinOperatorCandidates(CmpOp, OpLoc, Args, CandidateSet); + + for (auto It = std::next(CandidateSet.begin(), InitialSize); + It != CandidateSet.end(); ++It) { + OverloadCandidate &Ovl = *It; + if (!Ovl.getRewrittenKind()) { + if (Ovl.Function) + Ovl.Function->dumpColor(); + } + assert(Ovl.getRewrittenKind()); + if (IsRelationalOrEquality) { + if (FunctionDecl *FD = Ovl.Function) { + if (FD->getReturnType()->isUndeducedType()) { + // FIXME(EricWF): Must the return type already be deduced? + // Attempt to write a test case to hit this, otherwise remove it. + assert(false); + if (DeduceReturnType(FD, OpLoc)) { + // FIXME(EricWF): Does deduction failure actually make this + // non-vialble? probably not? + assert(false); + Ovl.Viable = false; + continue; + } + } + QualType RetTy = FD->getReturnType(); + assert(!RetTy->isDependentType()); + if (const ComparisonCategoryInfo *Info = + Context.CompCategories.lookupInfoForType(RetTy)) { + if (!Info->isUsableWithOperator(Opc)) { + Ovl.Viable = false; + Ovl.FailureKind = ovl_rewritten_operand_non_valid_for_operator; + continue; + } + } else { + // FIXME(EricWF): Check that the return type can be used with + // the specified relational operator + } + } else { + // Attempt to compute the comparison category type for a selecetd + // builtin function. + Optional CompType = + ComparisonCategories::computeComparisonTypeForBuiltin( + Ovl.BuiltinParamTypes[0], Ovl.BuiltinParamTypes[1]); + if (!CompType || + !ComparisonCategoryInfo::isUsableWithOperator(*CompType, Opc)) { + Ovl.Viable = false; + Ovl.FailureKind = ovl_rewritten_operand_non_valid_for_operator; + continue; + } + } + } + } + }; + + // If we have a relational or equality operation, add the rewritten candidates + // of the form: (LHS <=> RHS) @ 0 + if (IsRelationalOrEquality) + AddCandidates(InputArgs, ROC_AsThreeWay); + + // TODO: We should be able to avoid adding synthesized candidates when LHS and + // RHS have the same type, value category, and other relevent properties. + // In that case synthesized candidates for <=> should be the same as the + // rewritten ones. Note: It's still possible for the result of operator<=> to + // be usable only on the left or right side of the expression (0 @ ) + // or ( @ 0). + + // For relational, equality, and three-way comparisons, add the rewritten and + // synthesized candidates of the form: 0 @ (RHS <=> LHS) + SmallVector ReverseArgs(InputArgs.rbegin(), InputArgs.rend()); + AddCandidates(ReverseArgs, ROC_AsReversedThreeWay); +} + /// Add function candidates found via argument-dependent lookup /// to the set of overloading candidates. /// @@ -8869,13 +8992,10 @@ /// given function name (which may also be an operator name) and adds /// all of the overload candidates found by ADL to the overload /// candidate set (C++ [basic.lookup.argdep]). -void -Sema::AddArgumentDependentLookupCandidates(DeclarationName Name, - SourceLocation Loc, - ArrayRef Args, +void Sema::AddArgumentDependentLookupCandidates( + DeclarationName Name, SourceLocation Loc, ArrayRef Args, TemplateArgumentListInfo *ExplicitTemplateArgs, - OverloadCandidateSet& CandidateSet, - bool PartialOverloading) { + OverloadCandidateSet &CandidateSet, bool PartialOverloading) { ADLResult Fns; // FIXME: This approach for uniquing ADL results (and removing @@ -9008,8 +9128,8 @@ assert(Cand2.Conversions.size() == NumArgs && "Overload candidate mismatch"); bool HasBetterConversion = false; for (unsigned ArgIdx = StartArg; ArgIdx < NumArgs; ++ArgIdx) { - bool Cand1Bad = IsIllFormedConversion(Cand1.Conversions[ArgIdx]); - bool Cand2Bad = IsIllFormedConversion(Cand2.Conversions[ArgIdx]); + bool Cand1Bad = IsIllFormedConversion(Cand1.getConversion(ArgIdx)); + bool Cand2Bad = IsIllFormedConversion(Cand2.getConversion(ArgIdx)); if (Cand1Bad != Cand2Bad) { if (Cand1Bad) return false; @@ -9026,8 +9146,8 @@ // conversion sequence than ICSi(F2), and then... for (unsigned ArgIdx = StartArg; ArgIdx < NumArgs; ++ArgIdx) { switch (CompareImplicitConversionSequences(S, Loc, - Cand1.Conversions[ArgIdx], - Cand2.Conversions[ArgIdx])) { + Cand1.getConversion(ArgIdx), + Cand2.getConversion(ArgIdx))) { case ImplicitConversionSequence::Better: // Cand1 has a better conversion sequence. HasBetterConversion = true; @@ -9133,6 +9253,55 @@ // Inherited from sibling base classes: still ambiguous. } + // Check C++2a tie-breakers for rewritten candidates + { + // --- F2 is a rewritten candidate ([over.match.oper]) and F1 is not. + RewrittenOverloadCandidateKind C1Roc = Cand1.getRewrittenKind(); + RewrittenOverloadCandidateKind C2Roc = Cand2.getRewrittenKind(); + if (C1Roc || C2Roc) { + if (!C1Roc || !C2Roc) + return !C1Roc; + // --- F1 and F2 are rewritten candidates, and F2 is a synthesized + // candidate with reversed order of parameters and F1 is not. + if ((C1Roc == ROC_AsReversedThreeWay || + C2Roc == ROC_AsReversedThreeWay) && + C1Roc != C2Roc) { + auto GetParamTypes = [&](const OverloadCandidate &Ovl) { + SmallVector Types; + // If the candidate is a method, compute the implicit object type. + if (const auto *MD = dyn_cast_or_null(Ovl.Function)) { + assert(Ovl.Conversions[0].isStandard()); + QualType Ty = Ovl.Conversions[0].Standard.getToType(2); + assert(!Ty->isReferenceType()); + const auto *FTP = MD->getType()->getAs(); + switch (FTP->getRefQualifier()) { + case RQ_LValue: + case RQ_None: + Types.push_back(S.Context.getLValueReferenceType(Ty)); + break; + case RQ_RValue: + Types.push_back(S.Context.getRValueReferenceType(Ty)); + break; + } + } + for (unsigned I = 0; I < Ovl.getNumParams(); ++I) + Types.push_back(Ovl.getParamType(I).getCanonicalType()); + + // Reverse the order of the parameter types if this is the + // reverse-ordered overload. + assert(Types.size() == 2); + if (Ovl.getRewrittenKind() == ROC_AsReversedThreeWay) + std::swap(Types[0], Types[1]); + + // Return the final list of types. + return Types; + }; + if (GetParamTypes(Cand1) == GetParamTypes(Cand2)) + return C2Roc == ROC_AsReversedThreeWay; + } + } + } + // Check C++17 tie-breakers for deduction guides. { auto *Guide1 = dyn_cast_or_null(Cand1.Function); @@ -9335,12 +9504,17 @@ oc_implicit_copy_assignment, oc_implicit_move_assignment, oc_inherited_constructor, - oc_inherited_constructor_template + oc_inherited_constructor_template, + oc_rewritten_comparison_operator, + oc_synthesized_comparison_operator, + oc_rewritten_comparison_operator_template, + oc_synthesized_comparison_operator_template, }; static OverloadCandidateKind ClassifyOverloadCandidate(Sema &S, NamedDecl *Found, FunctionDecl *Fn, - std::string &Description) { + std::string &Description, + RewrittenOverloadCandidateKind ROC = ROC_None) { bool isTemplate = false; if (FunctionTemplateDecl *FunTmpl = Fn->getPrimaryTemplate()) { @@ -9385,6 +9559,13 @@ return oc_method; } + if (ROC == ROC_AsThreeWay) + return isTemplate ? oc_rewritten_comparison_operator_template + : oc_rewritten_comparison_operator; + if (ROC == ROC_AsReversedThreeWay) + return isTemplate ? oc_synthesized_comparison_operator_template + : oc_synthesized_comparison_operator; + return isTemplate ? oc_function_template : oc_function; } @@ -9470,14 +9651,16 @@ // Notes the location of an overload candidate. void Sema::NoteOverloadCandidate(NamedDecl *Found, FunctionDecl *Fn, - QualType DestType, bool TakingAddress) { + QualType DestType, bool TakingAddress, + RewrittenOverloadCandidateKind ROC) { if (TakingAddress && !checkAddressOfCandidateIsAvailable(*this, Fn)) return; if (Fn->isMultiVersion() && !Fn->getAttr()->isDefaultVersion()) return; std::string FnDesc; - OverloadCandidateKind K = ClassifyOverloadCandidate(*this, Found, Fn, FnDesc); + OverloadCandidateKind K = + ClassifyOverloadCandidate(*this, Found, Fn, FnDesc, ROC); PartialDiagnostic PD = PDiag(diag::note_ovl_candidate) << (unsigned) K << Fn << FnDesc; @@ -9486,6 +9669,12 @@ MaybeEmitInheritedConstructorNote(*this, Found); } +void Sema::NoteOverloadCandidate(OverloadCandidate *Cand) { + assert(Cand->Function && Cand->FoundDecl); + NoteOverloadCandidate(Cand->FoundDecl, Cand->Function, QualType(), + /*TakingAddress*/ false, Cand->getRewrittenKind()); +} + // Notes the location of all overload candidates designated through // OverloadedExpr void Sema::NoteAllOverloadCandidates(Expr *OverloadedExpr, QualType DestType, @@ -9553,8 +9742,8 @@ } std::string FnDesc; - OverloadCandidateKind FnKind = - ClassifyOverloadCandidate(S, Cand->FoundDecl, Fn, FnDesc); + OverloadCandidateKind FnKind = ClassifyOverloadCandidate( + S, Cand->FoundDecl, Fn, FnDesc, Cand->getRewrittenKind()); Expr *FromExpr = Conv.Bad.FromExpr; QualType FromTy = Conv.Bad.getFromType(); @@ -10164,6 +10353,10 @@ } } +static void DiagnoseFailedRewrittenOperand(Sema &S, OverloadCandidate *Cand) { + // FIXME(EricWF): Do something here! +} + static void DiagnoseFailedEnableIfAttr(Sema &S, OverloadCandidate *Cand) { FunctionDecl *Callee = Cand->Function; EnableIfAttr *Attr = static_cast(Cand->DeductionFailure.Data); @@ -10213,7 +10406,7 @@ } // We don't really have anything else to say about viable candidates. - S.NoteOverloadCandidate(Cand->FoundDecl, Fn); + S.NoteOverloadCandidate(Cand); return; } @@ -10236,7 +10429,7 @@ 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); case ovl_fail_bad_conversion: { unsigned I = (Cand->IgnoreObjectArgument ? 1 : 0); @@ -10247,7 +10440,7 @@ // 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); } case ovl_fail_bad_target: @@ -10279,6 +10472,9 @@ case ovl_non_default_multiversion_function: // Do nothing, these should simply be ignored. break; + case ovl_rewritten_operand_non_valid_for_operator: + DiagnoseFailedRewrittenOperand(S, Cand); + break; } } @@ -12228,6 +12424,164 @@ return CreateBuiltinUnaryOp(OpLoc, Opc, Input); } +ExprResult Sema::BuildBinaryOperatorCandidate(SourceLocation OpLoc, + BinaryOperatorKind Opc, + const OverloadCandidate &Ovl, + Expr *LHSE, Expr *RHSE, + bool HadMultipleCandidates) { + Expr *Args[2] = {LHSE, RHSE}; + OverloadedOperatorKind Op = BinaryOperator::getOverloadedOperator(Opc); + // We found a built-in operator or an overloaded operator. + FunctionDecl *FnDecl = Ovl.Function; + + if (FnDecl) { + Expr *Base = nullptr; + // We matched an overloaded operator. Build a call to that + // operator. + + // Convert the arguments. + if (CXXMethodDecl *Method = dyn_cast(FnDecl)) { + // Ovl.Access is only meaningful for class members. + CheckMemberOperatorAccess(OpLoc, Args[0], Args[1], Ovl.FoundDecl); + + ExprResult Arg1 = + PerformCopyInitialization(InitializedEntity::InitializeParameter( + Context, FnDecl->getParamDecl(0)), + SourceLocation(), Args[1]); + if (Arg1.isInvalid()) + return ExprError(); + + ExprResult Arg0 = PerformObjectArgumentInitialization( + Args[0], /*Qualifier=*/nullptr, Ovl.FoundDecl, Method); + if (Arg0.isInvalid()) + return ExprError(); + Base = Args[0] = Arg0.getAs(); + Args[1] = Arg1.getAs(); + } else { + // Convert the arguments. + ExprResult Arg0 = + PerformCopyInitialization(InitializedEntity::InitializeParameter( + Context, FnDecl->getParamDecl(0)), + SourceLocation(), Args[0]); + if (Arg0.isInvalid()) + return ExprError(); + + ExprResult Arg1 = + PerformCopyInitialization(InitializedEntity::InitializeParameter( + Context, FnDecl->getParamDecl(1)), + SourceLocation(), Args[1]); + if (Arg1.isInvalid()) + return ExprError(); + Args[0] = Arg0.getAs(); + Args[1] = Arg1.getAs(); + } + + // Build the actual expression node. + ExprResult FnExpr = CreateFunctionRefExpr( + *this, FnDecl, Ovl.FoundDecl, Base, HadMultipleCandidates, OpLoc); + if (FnExpr.isInvalid()) + return ExprError(); + + // Determine the result type. + QualType ResultTy = FnDecl->getReturnType(); + ExprValueKind VK = Expr::getValueKindForType(ResultTy); + ResultTy = ResultTy.getNonLValueExprType(Context); + + CXXOperatorCallExpr *TheCall = new (Context) CXXOperatorCallExpr( + Context, Op, FnExpr.get(), Args, ResultTy, VK, OpLoc, FPFeatures); + + if (CheckCallReturnType(FnDecl->getReturnType(), OpLoc, TheCall, FnDecl)) + return ExprError(); + + ArrayRef ArgsArray(Args, 2); + const Expr *ImplicitThis = nullptr; + // Cut off the implicit 'this'. + if (isa(FnDecl)) { + ImplicitThis = ArgsArray[0]; + ArgsArray = ArgsArray.slice(1); + } + + // Check for a self move. + if (Op == OO_Equal) + DiagnoseSelfMove(Args[0], Args[1], OpLoc); + + checkCall(FnDecl, nullptr, ImplicitThis, ArgsArray, + isa(FnDecl), OpLoc, TheCall->getSourceRange(), + Sema::VariadicDoesNotApply); + + return MaybeBindToTemporary(TheCall); + + } else { + // We matched a built-in operator. Convert the arguments, then + // break out so that we will build the appropriate built-in + // operator node. + ExprResult ArgsRes0 = + PerformImplicitConversion(Args[0], Ovl.BuiltinParamTypes[0], + Ovl.getConversion(0), Sema::AA_Passing); + if (ArgsRes0.isInvalid()) + return ExprError(); + Args[0] = ArgsRes0.get(); + + ExprResult ArgsRes1 = + PerformImplicitConversion(Args[1], Ovl.BuiltinParamTypes[1], + Ovl.getConversion(1), Sema::AA_Passing); + if (ArgsRes1.isInvalid()) + return ExprError(); + Args[1] = ArgsRes1.get(); + } + // We matched a built-in operator; build it. + return CreateBuiltinBinOp(OpLoc, Opc, Args[0], Args[1]); +} + +static ExprResult BuildRewrittenCandidate(Sema &S, BinaryOperatorKind Opc, + const OverloadCandidate &Ovl, + ArrayRef Args, + const UnresolvedSetImpl &Fns, + SourceLocation OpLoc, + bool PerformADL) { + Expr *RewrittenArgs[2] = {Args[0], Args[1]}; + assert(Ovl.getRewrittenKind()); + bool IsSynthesized = Ovl.getRewrittenKind() == ROC_AsReversedThreeWay; + if (IsSynthesized) + std::swap(RewrittenArgs[0], RewrittenArgs[1]); + + // Supress diagnostics when building the expressions for the specified + // candidate. If evaluation fails the candidate will be marked non-viable + // and the best viable candidate re-computed. + Sema::TentativeAnalysisScope DiagnosticScopeGuard(S); + + // Build the '(LHS <=> RHS)' operand to the full expression. + ExprResult RewrittenRes = S.BuildBinaryOperatorCandidate( + OpLoc, BO_Cmp, Ovl, RewrittenArgs[0], RewrittenArgs[1], + /*HadMultipleCandidates*/ false); + if (RewrittenRes.isInvalid()) + return ExprError(); + + if (Opc != BO_Cmp) { + // Now attempt to build the full expression '(LHS <=> RHS) @ 0' using the + // evaluated operand and the literal 0. + llvm::APInt I = + llvm::APInt::getNullValue(S.Context.getIntWidth(S.Context.IntTy)); + Expr *Zero = + IntegerLiteral::Create(S.Context, I, S.Context.IntTy, SourceLocation()); + + Expr *NewLHS = RewrittenRes.get(); + Expr *NewRHS = Zero; + if (Ovl.getRewrittenKind() == ROC_AsReversedThreeWay) + std::swap(NewLHS, NewRHS); + + RewrittenRes = + S.CreateOverloadedBinOp(OpLoc, Opc, Fns, NewLHS, NewRHS, PerformADL, + /*AllowRewrittenCandidates*/ false); + if (RewrittenRes.isInvalid()) + return ExprError(); + } + Expr *Rewritten = RewrittenRes.get(); + + return new (S.Context) + CXXRewrittenOperatorExpr(Ovl.getRewrittenKind(), Rewritten); +} + /// Create a binary operation that may resolve to an overloaded /// operator. /// @@ -12244,11 +12598,11 @@ /// /// \param LHS Left-hand argument. /// \param RHS Right-hand argument. -ExprResult -Sema::CreateOverloadedBinOp(SourceLocation OpLoc, +ExprResult Sema::CreateOverloadedBinOp(SourceLocation OpLoc, BinaryOperatorKind Opc, - const UnresolvedSetImpl &Fns, - Expr *LHS, Expr *RHS, bool PerformADL) { + 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 @@ -12310,11 +12664,27 @@ if (Opc == BO_PtrMemD) return CreateBuiltinBinOp(OpLoc, Opc, Args[0], Args[1]); + UnresolvedSet<6> OrigFuncs; + UnresolvedSet<6> ThreeWayFuncs; + for (NamedDecl *D : Fns) { + FunctionDecl *FD = D->getAsFunction(); + if (FD) { + assert(FD->isOverloadedOperator()); + if (FD->getOverloadedOperator() == OO_Spaceship) { + ThreeWayFuncs.addDecl(D); + if (Op == OO_Spaceship) + OrigFuncs.addDecl(D); + } else + OrigFuncs.addDecl(D); + } else + OrigFuncs.addDecl(D); + } + // Build an empty overload set. OverloadCandidateSet CandidateSet(OpLoc, OverloadCandidateSet::CSK_Operator); // Add the candidates from the given function set. - AddFunctionCandidates(Fns, Args, CandidateSet); + AddFunctionCandidates(OrigFuncs, Args, CandidateSet); // Add operator candidates that are member functions. AddMemberOperatorCandidates(Op, OpLoc, Args, CandidateSet); @@ -12330,119 +12700,28 @@ // Add builtin operator candidates. AddBuiltinOperatorCandidates(Op, OpLoc, Args, CandidateSet); - bool HadMultipleCandidates = (CandidateSet.size() > 1); - - // Perform overload resolution. - OverloadCandidateSet::iterator Best; - switch (CandidateSet.BestViableFunction(*this, OpLoc, Best)) { - case OR_Success: { - // We found a built-in operator or an overloaded operator. - FunctionDecl *FnDecl = Best->Function; - - if (FnDecl) { - Expr *Base = nullptr; - // We matched an overloaded operator. Build a call to that - // operator. - - // Convert the arguments. - if (CXXMethodDecl *Method = dyn_cast(FnDecl)) { - // Best->Access is only meaningful for class members. - CheckMemberOperatorAccess(OpLoc, Args[0], Args[1], Best->FoundDecl); - - ExprResult Arg1 = - PerformCopyInitialization( - InitializedEntity::InitializeParameter(Context, - FnDecl->getParamDecl(0)), - SourceLocation(), Args[1]); - if (Arg1.isInvalid()) - return ExprError(); - - ExprResult Arg0 = - PerformObjectArgumentInitialization(Args[0], /*Qualifier=*/nullptr, - Best->FoundDecl, Method); - if (Arg0.isInvalid()) - return ExprError(); - Base = Args[0] = Arg0.getAs(); - Args[1] = RHS = Arg1.getAs(); - } else { - // Convert the arguments. - ExprResult Arg0 = PerformCopyInitialization( - InitializedEntity::InitializeParameter(Context, - FnDecl->getParamDecl(0)), - SourceLocation(), Args[0]); - if (Arg0.isInvalid()) - return ExprError(); - - ExprResult Arg1 = - PerformCopyInitialization( - InitializedEntity::InitializeParameter(Context, - FnDecl->getParamDecl(1)), - SourceLocation(), Args[1]); - if (Arg1.isInvalid()) - return ExprError(); - Args[0] = LHS = Arg0.getAs(); - Args[1] = RHS = Arg1.getAs(); + // C++2a Add rewritten and synthesized operator candidates. + bool HasRewrittenCandidates = false; + if (getLangOpts().CPlusPlus2a && AllowRewrittenCandidates && + BinaryOperator::isComparisonOp(Opc)) { + unsigned BeforeRewrittenSize = CandidateSet.size(); + AddRewrittenOperatorCandidates(Op, OpLoc, Args, ThreeWayFuncs, CandidateSet, + PerformADL); + HasRewrittenCandidates = BeforeRewrittenSize != CandidateSet.size(); } - // Build the actual expression node. - ExprResult FnExpr = CreateFunctionRefExpr(*this, FnDecl, - Best->FoundDecl, Base, - HadMultipleCandidates, OpLoc); - if (FnExpr.isInvalid()) - return ExprError(); - - // Determine the result type. - QualType ResultTy = FnDecl->getReturnType(); - ExprValueKind VK = Expr::getValueKindForType(ResultTy); - ResultTy = ResultTy.getNonLValueExprType(Context); - - CXXOperatorCallExpr *TheCall = - new (Context) CXXOperatorCallExpr(Context, Op, FnExpr.get(), - Args, ResultTy, VK, OpLoc, - FPFeatures); - - if (CheckCallReturnType(FnDecl->getReturnType(), OpLoc, TheCall, - FnDecl)) - return ExprError(); - - ArrayRef ArgsArray(Args, 2); - const Expr *ImplicitThis = nullptr; - // Cut off the implicit 'this'. - if (isa(FnDecl)) { - ImplicitThis = ArgsArray[0]; - ArgsArray = ArgsArray.slice(1); - } - - // Check for a self move. - if (Op == OO_Equal) - DiagnoseSelfMove(Args[0], Args[1], OpLoc); - - checkCall(FnDecl, nullptr, ImplicitThis, ArgsArray, - isa(FnDecl), OpLoc, TheCall->getSourceRange(), - VariadicDoesNotApply); - - return MaybeBindToTemporary(TheCall); - } else { - // We matched a built-in operator. Convert the arguments, then - // break out so that we will build the appropriate built-in - // operator node. - ExprResult ArgsRes0 = - PerformImplicitConversion(Args[0], Best->BuiltinParamTypes[0], - Best->Conversions[0], AA_Passing); - if (ArgsRes0.isInvalid()) - return ExprError(); - Args[0] = ArgsRes0.get(); - ExprResult ArgsRes1 = - PerformImplicitConversion(Args[1], Best->BuiltinParamTypes[1], - Best->Conversions[1], AA_Passing); - if (ArgsRes1.isInvalid()) - return ExprError(); - Args[1] = ArgsRes1.get(); - break; - } - } + // Perform final overload resolution. + bool HadMultipleCandidates = (CandidateSet.size() > 1); + OverloadCandidateSet::iterator Best; + switch (CandidateSet.BestViableFunction(*this, OpLoc, Best)) { + case OR_Success: + if (Best->getRewrittenKind()) + return BuildRewrittenCandidate(*this, Opc, *Best, Args, Fns, OpLoc, + PerformADL); + return BuildBinaryOperatorCandidate(OpLoc, Opc, *Best, Args[0], Args[1], + HadMultipleCandidates); case OR_No_Viable_Function: { // C++ [over.match.oper]p9: // If the operator is the operator , [...] and there are no @@ -12455,15 +12734,15 @@ // operator do not fall through to handling in built-in, but report that // no overloaded assignment operator found ExprResult Result = ExprError(); - if (Args[0]->getType()->isRecordType() && - Opc >= BO_Assign && Opc <= BO_OrAssign) { + if (Args[0]->getType()->isRecordType() && Opc >= BO_Assign && + Opc <= BO_OrAssign) { Diag(OpLoc, diag::err_ovl_no_viable_oper) - << BinaryOperator::getOpcodeStr(Opc) - << Args[0]->getSourceRange() << Args[1]->getSourceRange(); + << BinaryOperator::getOpcodeStr(Opc) << Args[0]->getSourceRange() + << Args[1]->getSourceRange(); if (Args[0]->getType()->isIncompleteType()) { Diag(OpLoc, diag::note_assign_lhs_incomplete) - << Args[0]->getType() - << Args[0]->getSourceRange() << Args[1]->getSourceRange(); + << Args[0]->getType() << Args[0]->getSourceRange() + << Args[1]->getSourceRange(); } } else { // This is an erroneous use of an operator which can be overloaded by @@ -12484,12 +12763,11 @@ BinaryOperator::getOpcodeStr(Opc), OpLoc); return Result; } - case OR_Ambiguous: Diag(OpLoc, diag::err_ovl_ambiguous_oper_binary) - << BinaryOperator::getOpcodeStr(Opc) - << Args[0]->getType() << Args[1]->getType() - << Args[0]->getSourceRange() << Args[1]->getSourceRange(); + << BinaryOperator::getOpcodeStr(Opc) << Args[0]->getType() + << Args[1]->getType() << Args[0]->getSourceRange() + << Args[1]->getSourceRange(); CandidateSet.NoteCandidates(*this, OCD_ViableCandidates, Args, BinaryOperator::getOpcodeStr(Opc), OpLoc); return ExprError(); @@ -12507,8 +12785,7 @@ return ExprError(); } else { Diag(OpLoc, diag::err_ovl_deleted_oper) - << Best->Function->isDeleted() - << BinaryOperator::getOpcodeStr(Opc) + << Best->Function->isDeleted() << BinaryOperator::getOpcodeStr(Opc) << getDeletedOrUnavailableSuffix(Best->Function) << Args[0]->getSourceRange() << Args[1]->getSourceRange(); } Index: lib/Sema/TreeTransform.h =================================================================== --- lib/Sema/TreeTransform.h +++ lib/Sema/TreeTransform.h @@ -3217,6 +3217,11 @@ return getSema().BuildEmptyCXXFoldExpr(EllipsisLoc, Operator); } + ExprResult RebuildCXXRewrittenOperatorExpr( + CXXRewrittenOperatorExpr::RewrittenOpKind Kind, Expr *Rewritten) { + return new (SemaRef.Context) CXXRewrittenOperatorExpr(Kind, Rewritten); + } + /// Build a new atomic operation expression. /// /// By default, performs semantic analysis to build the new expression. @@ -11516,6 +11521,25 @@ } template +ExprResult TreeTransform::TransformCXXRewrittenOperatorExpr( + CXXRewrittenOperatorExpr *E) { + + // FIXME(EricWF): Is there a case where the underlying expression has been + // transformed in such a way that we need to re-compute the rewritten + // expression? (and not just re-build it). + ExprResult RewrittenRes = getDerived().TransformExpr(E->getRewrittenExpr()); + if (RewrittenRes.isInvalid()) + return ExprError(); + Expr *Rewritten = RewrittenRes.get(); + + if (Rewritten == E->getRewrittenExpr() && !getDerived().AlwaysRebuild()) + return E; + + return getDerived().RebuildCXXRewrittenOperatorExpr(E->getRewrittenKind(), + Rewritten); +} + +template ExprResult TreeTransform::TransformCXXFoldExpr(CXXFoldExpr *E) { Expr *Pattern = E->getPattern(); Index: lib/Serialization/ASTReaderStmt.cpp =================================================================== --- lib/Serialization/ASTReaderStmt.cpp +++ lib/Serialization/ASTReaderStmt.cpp @@ -1705,6 +1705,13 @@ E->Opcode = (BinaryOperatorKind)Record.readInt(); } +void ASTStmtReader::VisitCXXRewrittenOperatorExpr(CXXRewrittenOperatorExpr *E) { + VisitExpr(E); + E->setRewrittenKind( + static_cast(Record.readInt())); + E->Rewritten = Record.readSubExpr(); +} + void ASTStmtReader::VisitOpaqueValueExpr(OpaqueValueExpr *E) { VisitExpr(E); E->SourceExpr = Record.readSubExpr(); @@ -4074,6 +4081,9 @@ S = new (Context) CXXFoldExpr(Empty); break; + case EXPR_CXX_REWRITTEN_OPERATOR: + S = new (Context) CXXRewrittenOperatorExpr(Empty); + case EXPR_OPAQUE_VALUE: S = new (Context) OpaqueValueExpr(Empty); break; Index: lib/Serialization/ASTWriterStmt.cpp =================================================================== --- lib/Serialization/ASTWriterStmt.cpp +++ lib/Serialization/ASTWriterStmt.cpp @@ -1695,6 +1695,13 @@ Code = serialization::EXPR_CXX_FOLD; } +void ASTStmtWriter::VisitCXXRewrittenOperatorExpr(CXXRewrittenOperatorExpr *E) { + VisitExpr(E); + Record.push_back(E->getRewrittenKind()); + Record.AddStmt(E->getRewrittenExpr()); + Code = serialization::EXPR_CXX_REWRITTEN_OPERATOR; +} + void ASTStmtWriter::VisitOpaqueValueExpr(OpaqueValueExpr *E) { VisitExpr(E); Record.AddStmt(E->getSourceExpr()); Index: lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngine.cpp +++ lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1289,6 +1289,7 @@ case Stmt::PackExpansionExprClass: case Stmt::SubstNonTypeTemplateParmPackExprClass: case Stmt::FunctionParmPackExprClass: + case Stmt::CXXRewrittenOperatorExprClass: case Stmt::CoroutineBodyStmtClass: case Stmt::CoawaitExprClass: case Stmt::DependentCoawaitExprClass: Index: test/CodeGenCXX/cxx2a-compare.cpp =================================================================== --- test/CodeGenCXX/cxx2a-compare.cpp +++ test/CodeGenCXX/cxx2a-compare.cpp @@ -186,3 +186,17 @@ } } // namespace ComplexTest + +namespace RewrittenTest { +struct U { + int x; + std::strong_ordering operator<=>(U const &) const; +}; +// FIXME(EricWF): Write this test +auto test(U t1, U t2) { + return (t1 < t2); +} + +} // namespace RewrittenTest + + Index: test/SemaCXX/compare-cxx2a.cpp =================================================================== --- test/SemaCXX/compare-cxx2a.cpp +++ test/SemaCXX/compare-cxx2a.cpp @@ -292,20 +292,21 @@ template struct Tag {}; -// expected-note@+1 {{candidate}} -Tag<0> operator<=>(EnumA, EnumA) { - return {}; +std::strong_ordering operator<=>(EnumA, EnumA) { + return std::strong_ordering::equal; } -Tag<1> operator<=>(EnumA, EnumB) { - return {}; +// expected-note@+1 {{candidate function}}, +std::strong_ordering operator<=>(EnumA a, EnumB b) { + return ((int)a <=> (int)b); } void test_enum_ovl_provided() { auto r1 = (EnumA::A <=> EnumA::A); - ASSERT_EXPR_TYPE(r1, Tag<0>); + ASSERT_EXPR_TYPE(r1, std::strong_ordering); 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')}} + ASSERT_EXPR_TYPE(r2, std::strong_ordering); + (void)(EnumB::B <=> EnumA::A); // OK, chooses reverse order synthesized candidate. + (void)(EnumB::B <=> EnumC::C); // expected-error {{invalid operands to binary expression ('EnumCompareTests::EnumB' and 'EnumCompareTests::EnumC')}} } void enum_float_test() { @@ -421,3 +422,114 @@ } } // namespace ComplexTest + +namespace TestRewritting { + +struct T { + int x; + // expected-note@+1 {{candidate}} + constexpr std::strong_ordering operator<=>(T y) const { + return (x <=> y.x); + } +}; + +struct U { + int x; + // FIXME: This diagnostic is wrong-ish. + // expected-note@+1 {{candidate function not viable: requires single argument 'y', but 2 arguments were provided}} + constexpr std::strong_equality operator<=>(T y) const { + if (x == y.x) + return std::strong_equality::equal; + return std::strong_equality::nonequal; + } +}; + +struct X { int x; }; +struct Y { int x; }; +template +struct Tag {}; + +Tag<0> operator<=>(X, Y) { + return {}; +} + +constexpr auto operator<=>(Y y, X x) { + return y.x <=> x.x; +} + +void foo() { + T t{42}; + T t2{0}; + U u{101}; + auto r1 = (t <=> u); + ASSERT_EXPR_TYPE(r1, std::strong_equality); + auto r2 = (t <=> t2); + ASSERT_EXPR_TYPE(r2, std::strong_ordering); + + auto r3 = t == u; + ASSERT_EXPR_TYPE(r3, bool); + + (void)(t < u); // expected-error {{invalid operands to binary expression ('TestRewritting::T' and 'TestRewritting::U')}} + + constexpr X x{1}; + constexpr Y y{2}; + constexpr auto r4 = (y < x); + static_assert(r4 == false); + constexpr auto r5 = (x < y); + static_assert(r5 == true); +} + +} // namespace TestRewritting + +// The implicit object parameter is not considered when performing partial +// ordering. That makes the reverse synthesized candidates ambiguous with the +// rewritten candidates if any of them resolve to a member function. +namespace TestOvlMatchingIgnoresImplicitObject { +struct U; +struct T { + std::strong_ordering operator<=>(U const &RHS) const; +}; +struct U { + std::strong_ordering operator<=>(T const &RHS) const = delete; +}; + +struct V { + int x; +}; +auto operator<=>(V const &LHS, V &&RHS) { // expected-note 4 {{candidate}} + return LHS.x <=> RHS.x; +} +auto operator<(V const &, V &&) { // expected-note {{candidate}} + return std::strong_equality::equal; +} + +void test() { + // OK. selects T::operator<=>(U) + (void)(T{} < U{}); + // expected-error@+1 {{use of overloaded operator '<' is ambiguous}} + (void)(V{} < V{}); + // expected-error@+1 {{use of overloaded operator '<=>' is ambiguous}} + (void)(V{} <=> V{}); +} + +} // namespace TestOvlMatchingIgnoresImplicitObject + +namespace TestRewrittenTemplate { + +template +auto test(T const &LHS, T const &RHS) { + // expected-error@+1 {{invalid operands to binary expression ('const TestRewrittenTemplate::None'}} + return LHS < RHS; +} +struct None {}; +template auto test(None const &, None const &); // expected-note {{requested here}} + +struct Relational {}; +bool operator<(Relational, Relational); +template auto test(Relational const &, Relational const &); + +struct ThreeWay {}; +std::strong_ordering operator<=>(ThreeWay, ThreeWay); +template auto test(ThreeWay const &, ThreeWay const &); + +} // namespace TestRewrittenTemplate