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" @@ -127,10 +128,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 +143,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 +204,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 +246,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 +261,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 @@ -87,6 +87,8 @@ SourceRange getSourceRangeImpl() const LLVM_READONLY; public: + friend class ASTReader; + friend class ASTWriter; friend class ASTStmtReader; friend class ASTStmtWriter; @@ -4207,6 +4209,73 @@ child_range children() { return child_range(SubExprs, SubExprs + 2); } }; + +class CXXRewrittenExpr : public Expr { + +public: + enum RewrittenKind { Comparison }; + + struct ComparisonBits { + /// Whether this rewritten comparison expression has reverse-order + /// parameters. + unsigned IsSynthesized : 1; + }; + + union ExtraRewrittenBits { + ComparisonBits CompareBits; + }; + +private: + friend class ASTReader; + friend class ASTStmtReader; + friend class ASTStmtWriter; + + Stmt *SubExprs[2]; + ExtraRewrittenBits ExtraBits; + + CXXRewrittenExpr(EmptyShell Empty) : Expr(CXXRewrittenExprClass, Empty) {} + +public: + CXXRewrittenExpr(RewrittenKind Kind, Expr *Original, Expr *Rewritten, + ExtraRewrittenBits ExtraBits) + : Expr(CXXRewrittenExprClass, Rewritten->getType(), + Rewritten->getValueKind(), Rewritten->getObjectKind(), + /*Dependent*/ Rewritten->isTypeDependent(), + Rewritten->isValueDependent(), + Original->isInstantiationDependent(), + Rewritten->containsUnexpandedParameterPack()), + ExtraBits(ExtraBits) { + SubExprs[0] = Original; + SubExprs[1] = Rewritten; + } + + RewrittenKind getRewrittenKind() const { + return static_cast(CXXRewrittenBits.Kind); + } + void setRewrittenKind(RewrittenKind Kind) { CXXRewrittenBits.Kind = Kind; } + + ExtraRewrittenBits *getRewrittenInfo() { return &ExtraBits; } + const ExtraRewrittenBits *getRewrittenInfo() const { return &ExtraBits; } + + Expr *getOriginalExpr() const { return static_cast(SubExprs[0]); } + Expr *getRewrittenExpr() const { + return static_cast(SubExprs[1]); + } + + SourceLocation getLocStart() const { + return getOriginalExpr()->getLocStart(); + } + SourceLocation getLocEnd() const { return getOriginalExpr()->getLocEnd(); } + SourceLocation getExprLoc() const { return getOriginalExpr()->getExprLoc(); } + + child_range children() { return child_range(SubExprs, SubExprs + 2); } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CXXRewrittenExprClass; + } +}; + + /// 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 @@ -2549,6 +2549,14 @@ DEF_TRAVERSE_STMT(CXXFoldExpr, {}) DEF_TRAVERSE_STMT(AtomicExpr, {}) +DEF_TRAVERSE_STMT(CXXRewrittenExpr, { + TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getOriginalExpr()); + if (!getDerived().shouldVisitImplicitCode()) { + TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getRewrittenExpr()); + } + ShouldVisitChildren = false; +}) + // For coroutines expressions, traverse either the operand // as written or the implied calls, depending on what the // derived class requests. Index: include/clang/AST/Stmt.h =================================================================== --- include/clang/AST/Stmt.h +++ include/clang/AST/Stmt.h @@ -246,6 +246,13 @@ /// bit is set to true. unsigned IsUnique : 1; }; + class CXXRewrittenExprBitfields { + friend class CXXRewrittenExpr; + + unsigned : NumExprBits; + + unsigned Kind : 1; + }; class ObjCIndirectCopyRestoreExprBitfields { friend class ObjCIndirectCopyRestoreExpr; @@ -309,6 +316,7 @@ InitListExprBitfields InitListExprBits; TypeTraitExprBitfields TypeTraitExprBits; CoawaitExprBitfields CoawaitBits; + CXXRewrittenExprBitfields CXXRewrittenBits; }; public: 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 CXXRewrittenExpr : DStmt; // C++ Coroutines TS expressions def CoroutineSuspendExpr : DStmt; Index: include/clang/Sema/Overload.h =================================================================== --- include/clang/Sema/Overload.h +++ include/clang/Sema/Overload.h @@ -62,7 +62,7 @@ /// Succeeded, but refers to a deleted function. OR_Deleted }; - + enum OverloadCandidateDisplayKind { /// Requests that all candidates be shown. Viable candidates will /// be printed first. @@ -72,6 +72,17 @@ OCD_ViableCandidates }; + /// 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_Rewritten, + /// Both rewritten and synthesized. + ROC_Synthesized + }; + /// 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 @@ -107,7 +118,7 @@ /// Integral conversions (C++ [conv.integral]) ICK_Integral_Conversion, - /// Floating point conversions (C++ [conv.double] + /// Floating point conversions (C++ [conv.double] ICK_Floating_Conversion, /// Complex conversions (C99 6.3.1.6) @@ -252,7 +263,7 @@ /// Whether the qualification conversion involves a change in the /// Objective-C lifetime (for automatic reference counting). unsigned QualificationIncludesObjCLifetime : 1; - + /// IncompatibleObjC - Whether this is an Objective-C conversion /// that we should warn about (if we actually use it). unsigned IncompatibleObjC : 1; @@ -282,7 +293,7 @@ /// Whether this binds a reference to an object with a different /// Objective-C lifetime qualifier. unsigned ObjCLifetimeConversionBinding : 1; - + /// FromType - The type that this conversion is converting /// from. This is an opaque pointer that can be translated into a /// QualType. @@ -303,13 +314,13 @@ void setFromType(QualType T) { FromTypePtr = T.getAsOpaquePtr(); } - void setToType(unsigned Idx, QualType T) { + void setToType(unsigned Idx, QualType T) { assert(Idx < 3 && "To type index is out of range"); - ToTypePtrs[Idx] = T.getAsOpaquePtr(); + ToTypePtrs[Idx] = T.getAsOpaquePtr(); } void setAllToTypes(QualType T) { - ToTypePtrs[0] = T.getAsOpaquePtr(); + ToTypePtrs[0] = T.getAsOpaquePtr(); ToTypePtrs[1] = ToTypePtrs[0]; ToTypePtrs[2] = ToTypePtrs[0]; } @@ -324,11 +335,11 @@ } void setAsIdentityConversion(); - + bool isIdentityConversion() const { return Second == ICK_Identity && Third == ICK_Identity; } - + ImplicitConversionRank getRank() const; NarrowingKind getNarrowingKind(ASTContext &Context, const Expr *Converted, @@ -562,7 +573,7 @@ new (this) ImplicitConversionSequence(Other); return *this; } - + ~ImplicitConversionSequence() { destruct(); } @@ -571,7 +582,7 @@ assert(isInitialized() && "querying uninitialized conversion"); return Kind(ConversionKind); } - + /// Return a ranking of the implicit conversion sequence /// kind, where smaller ranks represent better conversion /// sequences. @@ -581,11 +592,11 @@ /// per C++ [over.best.ics]p10. unsigned getKindRank() const { switch (getKind()) { - case StandardConversion: + case StandardConversion: return 0; case UserDefinedConversion: - case AmbiguousConversion: + case AmbiguousConversion: return 1; case EllipsisConversion: @@ -755,12 +766,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 +780,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. @@ -781,7 +796,7 @@ union { DeductionFailureInfo DeductionFailure; - + /// FinalConversion - For a conversion function (where Function is /// a CXXConversionDecl), the standard conversion that occurs /// after the call to the overload candidate to convert the result @@ -812,6 +827,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 +856,10 @@ return Function->getNumParams(); return ExplicitCallArguments; } + + RewrittenOverloadCandidateKind getRewrittenKind() const { + return static_cast(RewrittenOpKind); + } }; /// OverloadCandidateSet - A set of overload candidates, used in C++ @@ -853,8 +890,10 @@ private: SmallVector Candidates; - llvm::SmallPtrSet Functions; + using DeclSet = llvm::SmallPtrSet; + DeclSet Functions; + private: // Allocator for ConversionSequenceLists. We store the first few of these // inline to avoid allocation for small sets. llvm::BumpPtrAllocator SlabAllocator; @@ -896,6 +935,31 @@ void destroyCandidates(); public: + /// \brief RewrittenCandidateContextGuard - Enter a context suitable for + /// adding rewritten overload candidates. Rewritten candidates can + /// re-consider previously seen functions, so save and clear the list of + /// considered functions, and restore it when the rewritten context is + /// exited. + struct RewrittenCandidateContextGuard { + RewrittenCandidateContextGuard(OverloadCandidateSet &CS) + : CandidateSet(CS) { + assert(CS.Kind == CSK_Operator && + "rewritten expressions can only occur for operators"); + OldFunctions = std::move(CandidateSet.Functions); + } + + ~RewrittenCandidateContextGuard() { + CandidateSet.Functions.insert(OldFunctions.begin(), OldFunctions.end()); + } + + private: + OverloadCandidateSet &CandidateSet; + DeclSet OldFunctions; + }; + + friend struct RewrittenCandidateContextGuard; + + public: OverloadCandidateSet(SourceLocation Loc, CandidateSetKind CSK) : Loc(Loc), Kind(CSK) {} OverloadCandidateSet(const OverloadCandidateSet &) = delete; @@ -953,7 +1017,7 @@ /// Find the best viable function on this overload set, if it exists. OverloadingResult BestViableFunction(Sema &S, SourceLocation Loc, - OverloadCandidateSet::iterator& Best); + OverloadCandidateSet::iterator &Best); void NoteCandidates(Sema &S, OverloadCandidateDisplayKind OCD, Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -2784,7 +2784,12 @@ 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(), @@ -2920,16 +2925,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 @@ -1666,7 +1666,7 @@ EXPR_OBJC_BOXED_EXPRESSION, EXPR_OBJC_ARRAY_LITERAL, EXPR_OBJC_DICTIONARY_LITERAL, - + /// An ObjCEncodeExpr record. EXPR_OBJC_ENCODE, @@ -1725,7 +1725,7 @@ EXPR_OBJC_AVAILABILITY_CHECK, // C++ - + /// A CXXCatchStmt record. STMT_CXX_CATCH, @@ -1774,59 +1774,60 @@ /// A CXXBoolLiteralExpr record. EXPR_CXX_BOOL_LITERAL, - EXPR_CXX_NULL_PTR_LITERAL, // CXXNullPtrLiteralExpr - EXPR_CXX_TYPEID_EXPR, // CXXTypeidExpr (of expr). - EXPR_CXX_TYPEID_TYPE, // CXXTypeidExpr (of type). - EXPR_CXX_THIS, // CXXThisExpr - EXPR_CXX_THROW, // CXXThrowExpr - EXPR_CXX_DEFAULT_ARG, // CXXDefaultArgExpr - EXPR_CXX_DEFAULT_INIT, // CXXDefaultInitExpr - EXPR_CXX_BIND_TEMPORARY, // CXXBindTemporaryExpr + EXPR_CXX_NULL_PTR_LITERAL, // CXXNullPtrLiteralExpr + EXPR_CXX_TYPEID_EXPR, // CXXTypeidExpr (of expr). + EXPR_CXX_TYPEID_TYPE, // CXXTypeidExpr (of type). + EXPR_CXX_THIS, // CXXThisExpr + EXPR_CXX_THROW, // CXXThrowExpr + EXPR_CXX_DEFAULT_ARG, // CXXDefaultArgExpr + EXPR_CXX_DEFAULT_INIT, // CXXDefaultInitExpr + EXPR_CXX_BIND_TEMPORARY, // CXXBindTemporaryExpr EXPR_CXX_SCALAR_VALUE_INIT, // CXXScalarValueInitExpr EXPR_CXX_NEW, // CXXNewExpr EXPR_CXX_DELETE, // CXXDeleteExpr EXPR_CXX_PSEUDO_DESTRUCTOR, // CXXPseudoDestructorExpr - - EXPR_EXPR_WITH_CLEANUPS, // ExprWithCleanups - + + EXPR_EXPR_WITH_CLEANUPS, // ExprWithCleanups + EXPR_CXX_DEPENDENT_SCOPE_MEMBER, // CXXDependentScopeMemberExpr EXPR_CXX_DEPENDENT_SCOPE_DECL_REF, // DependentScopeDeclRefExpr EXPR_CXX_UNRESOLVED_CONSTRUCT, // CXXUnresolvedConstructExpr EXPR_CXX_UNRESOLVED_MEMBER, // UnresolvedMemberExpr EXPR_CXX_UNRESOLVED_LOOKUP, // UnresolvedLookupExpr - EXPR_CXX_EXPRESSION_TRAIT, // ExpressionTraitExpr - EXPR_CXX_NOEXCEPT, // CXXNoexceptExpr + EXPR_CXX_EXPRESSION_TRAIT, // ExpressionTraitExpr + EXPR_CXX_NOEXCEPT, // CXXNoexceptExpr - EXPR_OPAQUE_VALUE, // OpaqueValueExpr - EXPR_BINARY_CONDITIONAL_OPERATOR, // BinaryConditionalOperator - EXPR_TYPE_TRAIT, // TypeTraitExpr - EXPR_ARRAY_TYPE_TRAIT, // ArrayTypeTraitIntExpr - - EXPR_PACK_EXPANSION, // PackExpansionExpr - EXPR_SIZEOF_PACK, // SizeOfPackExpr - EXPR_SUBST_NON_TYPE_TEMPLATE_PARM, // SubstNonTypeTemplateParmExpr - EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK,// SubstNonTypeTemplateParmPackExpr - EXPR_FUNCTION_PARM_PACK, // FunctionParmPackExpr - EXPR_MATERIALIZE_TEMPORARY, // MaterializeTemporaryExpr - EXPR_CXX_FOLD, // CXXFoldExpr + EXPR_OPAQUE_VALUE, // OpaqueValueExpr + EXPR_BINARY_CONDITIONAL_OPERATOR, // BinaryConditionalOperator + EXPR_TYPE_TRAIT, // TypeTraitExpr + EXPR_ARRAY_TYPE_TRAIT, // ArrayTypeTraitIntExpr + + EXPR_PACK_EXPANSION, // PackExpansionExpr + EXPR_SIZEOF_PACK, // SizeOfPackExpr + EXPR_SUBST_NON_TYPE_TEMPLATE_PARM, // SubstNonTypeTemplateParmExpr + EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK, // SubstNonTypeTemplateParmPackExpr + EXPR_FUNCTION_PARM_PACK, // FunctionParmPackExpr + EXPR_MATERIALIZE_TEMPORARY, // MaterializeTemporaryExpr + EXPR_CXX_FOLD, // CXXFoldExpr + EXPR_CXX_REWRITTEN_OPERATOR, // CXXRewrittenExpr // CUDA - EXPR_CUDA_KERNEL_CALL, // CUDAKernelCallExpr + EXPR_CUDA_KERNEL_CALL, // CUDAKernelCallExpr // OpenCL - EXPR_ASTYPE, // AsTypeExpr + EXPR_ASTYPE, // AsTypeExpr // Microsoft - EXPR_CXX_PROPERTY_REF_EXPR, // MSPropertyRefExpr + EXPR_CXX_PROPERTY_REF_EXPR, // MSPropertyRefExpr EXPR_CXX_PROPERTY_SUBSCRIPT_EXPR, // MSPropertySubscriptExpr - EXPR_CXX_UUIDOF_EXPR, // CXXUuidofExpr (of expr). - EXPR_CXX_UUIDOF_TYPE, // CXXUuidofExpr (of type). - STMT_SEH_LEAVE, // SEHLeaveStmt - STMT_SEH_EXCEPT, // SEHExceptStmt - STMT_SEH_FINALLY, // SEHFinallyStmt - STMT_SEH_TRY, // SEHTryStmt + EXPR_CXX_UUIDOF_EXPR, // CXXUuidofExpr (of expr). + EXPR_CXX_UUIDOF_TYPE, // CXXUuidofExpr (of type). + STMT_SEH_LEAVE, // SEHLeaveStmt + STMT_SEH_EXCEPT, // SEHExceptStmt + STMT_SEH_FINALLY, // SEHFinallyStmt + STMT_SEH_TRY, // SEHTryStmt // OpenMP directives STMT_OMP_PARALLEL_DIRECTIVE, @@ -1879,10 +1880,10 @@ EXPR_OMP_ARRAY_SECTION, // ARC - EXPR_OBJC_BRIDGED_CAST, // ObjCBridgedCastExpr + EXPR_OBJC_BRIDGED_CAST, // ObjCBridgedCastExpr - STMT_MS_DEPENDENT_EXISTS, // MSDependentExistsStmt - EXPR_LAMBDA, // LambdaExpr + STMT_MS_DEPENDENT_EXISTS, // MSDependentExistsStmt + EXPR_LAMBDA, // LambdaExpr STMT_COROUTINE_BODY, STMT_CORETURN, EXPR_COAWAIT, Index: lib/AST/ComparisonCategories.cpp =================================================================== --- lib/AST/ComparisonCategories.cpp +++ lib/AST/ComparisonCategories.cpp @@ -209,3 +209,127 @@ 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; +} + +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; + // Count the number of times each comparison category type occurs in the + // specified type list. 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. If any type is not a comparison category, return + // nullptr. + for (auto TyKind : Types) { + // --- If any T is not a comparison category type, U is void. + 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; + + // FIXME(EricWF): What if we don't find std::strong_ordering + // --- 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 CXXRewrittenExprClass: { + 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/ExprClassification.cpp =================================================================== --- lib/AST/ExprClassification.cpp +++ lib/AST/ExprClassification.cpp @@ -287,7 +287,8 @@ if (cast(E)->isResultDependent()) return Cl::CL_PRValue; return ClassifyInternal(Ctx,cast(E)->getResultExpr()); - + case Expr::CXXRewrittenExprClass: + 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 VisitCXXRewrittenExpr(const CXXRewrittenExpr *E) { + return StmtVisitorTy::Visit(E->getRewrittenExpr()); + } }; } // namespace @@ -10859,6 +10863,8 @@ case Expr::ChooseExprClass: { return CheckICE(cast(E)->getChosenSubExpr(), Ctx); } + case Expr::CXXRewrittenExprClass: + 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,10 @@ mangleExpression(cast(E)->getSubExpr(), Arity); break; + case Expr::CXXRewrittenExprClass: + mangleExpression(cast(E)->getOriginalExpr(), Arity); + 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,13 @@ OS << ")"; } +void StmtPrinter::VisitCXXRewrittenExpr(CXXRewrittenExpr *E) { + // FIXME(EricWF): Are there ever cases where we want to display the rewritten + // code? For example when producing diagnostics on implicitly generated + // expressions? + Visit(E->getOriginalExpr()); +} + // C++ Coroutines TS void StmtPrinter::VisitCoroutineBodyStmt(CoroutineBodyStmt *S) { Index: lib/AST/StmtProfile.cpp =================================================================== --- lib/AST/StmtProfile.cpp +++ lib/AST/StmtProfile.cpp @@ -1816,6 +1816,11 @@ ID.AddInteger(S->getOperator()); } +void StmtProfiler::VisitCXXRewrittenExpr(const CXXRewrittenExpr *S) { + VisitExpr(S); + 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 *VisitCXXRewrittenExpr(const CXXRewrittenExpr *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::CXXRewrittenExprClass: + 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"); - - 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"); - }(); + Optional TypeKind = + ComparisonCategories::computeComparisonTypeForBuiltin(Type); + assert(TypeKind && "composite type for <=> has not been set"); - 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. - return InvalidOperands(Loc, LHS, RHS); + 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_Synthesized) + 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; @@ -5964,6 +5979,7 @@ Candidate.Function = Function; Candidate.Viable = true; Candidate.IsSurrogate = false; + Candidate.RewrittenOpKind = ROC_None; Candidate.IgnoreObjectArgument = false; Candidate.ExplicitCallArguments = Args.size(); @@ -6654,6 +6670,7 @@ Candidate.Function = MethodTmpl->getTemplatedDecl(); Candidate.Viable = false; Candidate.IsSurrogate = false; + Candidate.RewrittenOpKind = ROC_None; Candidate.IgnoreObjectArgument = cast(Candidate.Function)->isStatic() || ObjectType.isNull(); @@ -6718,6 +6735,7 @@ Candidate.Function = FunctionTemplate->getTemplatedDecl(); Candidate.Viable = false; Candidate.IsSurrogate = false; + Candidate.RewrittenOpKind = ROC_None; // Ignore the object argument if there is one, since we don't have an object // type. Candidate.IgnoreObjectArgument = @@ -6889,6 +6907,7 @@ Candidate.FoundDecl = FoundDecl; Candidate.Function = Conversion; Candidate.IsSurrogate = false; + Candidate.RewrittenOpKind = ROC_None; Candidate.IgnoreObjectArgument = false; Candidate.FinalConversion.setAsIdentityConversion(); Candidate.FinalConversion.setFromType(ConvType); @@ -7050,6 +7069,7 @@ Candidate.Viable = false; Candidate.FailureKind = ovl_fail_bad_deduction; Candidate.IsSurrogate = false; + Candidate.RewrittenOpKind = ROC_None; Candidate.IgnoreObjectArgument = false; Candidate.ExplicitCallArguments = 1; Candidate.DeductionFailure = MakeDeductionFailureInfo(Context, Result, @@ -7090,6 +7110,7 @@ Candidate.Surrogate = Conversion; Candidate.Viable = true; Candidate.IsSurrogate = true; + Candidate.RewrittenOpKind = ROC_None; Candidate.IgnoreObjectArgument = false; Candidate.ExplicitCallArguments = Args.size(); @@ -7186,7 +7207,7 @@ void Sema::AddMemberOperatorCandidates(OverloadedOperatorKind Op, SourceLocation OpLoc, ArrayRef Args, - OverloadCandidateSet& CandidateSet, + OverloadCandidateSet &CandidateSet, SourceRange OpRange) { DeclarationName OpName = Context.DeclarationNames.getCXXOperatorName(Op); @@ -7247,6 +7268,7 @@ Candidate.FoundDecl = DeclAccessPair::make(nullptr, AS_none); Candidate.Function = nullptr; Candidate.IsSurrogate = false; + Candidate.RewrittenOpKind = ROC_None; Candidate.IgnoreObjectArgument = false; std::copy(ParamTys, ParamTys + Args.size(), Candidate.BuiltinParamTypes); @@ -8862,6 +8884,98 @@ } } + +/// 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::RewrittenCandidateContextGuard Guard(CandidateSet); + + 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; + Ovl.RewrittenOpKind = Kind; + if (IsRelationalOrEquality) { + if (FunctionDecl *FD = Ovl.Function) { + if (FD->getReturnType()->isUndeducedType()) { + if (DeduceReturnType(FD, OpLoc)) { + Ovl.Viable = false; + continue; + } + } + QualType RetTy = FD->getReturnType(); + if (const ComparisonCategoryInfo *Info = + Context.CompCategories.lookupInfoForType(RetTy)) { + if (!Info->isUsableWithOperator(Opc)) { + Ovl.Viable = false; + continue; + } + } else { + // FIXME(EricWF): Check that the return type can be used with + // the specified relational operator + } + } else { + Optional CompType = + ComparisonCategories::computeComparisonTypeForBuiltin( + Ovl.BuiltinParamTypes[0], Ovl.BuiltinParamTypes[1]); + if (!CompType || + !ComparisonCategoryInfo::isUsableWithOperator(*CompType, Opc)) { + Ovl.Viable = false; + continue; + } + } + } + } + }; + + // If we have a relational or equality operation, add the rewritten candidates + // of the form: (LHS <=> RHS) @ 0 + if (IsRelationalOrEquality) + AddCandidates(InputArgs, ROC_Rewritten); + + // 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_Synthesized); +} + /// Add function candidates found via argument-dependent lookup /// to the set of overloading candidates. /// @@ -8869,13 +8983,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, - TemplateArgumentListInfo *ExplicitTemplateArgs, - OverloadCandidateSet& CandidateSet, - bool PartialOverloading) { +void Sema::AddArgumentDependentLookupCandidates( + DeclarationName Name, SourceLocation Loc, ArrayRef Args, + TemplateArgumentListInfo *ExplicitTemplateArgs, + OverloadCandidateSet &CandidateSet, bool PartialOverloading) { ADLResult Fns; // FIXME: This approach for uniquing ADL results (and removing @@ -9008,8 +9119,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 +9137,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 +9244,50 @@ // 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_Synthesized || C2Roc == ROC_Synthesized) && + 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()); + if (Ovl.getRewrittenKind() == ROC_Synthesized) { + SmallVector RevTypes(Types.rbegin(), Types.rend()); + return RevTypes; + } + return Types; + }; + if (GetParamTypes(Cand1) == GetParamTypes(Cand2)) + return C2Roc == ROC_Synthesized; + } + } + } + // Check C++17 tie-breakers for deduction guides. { auto *Guide1 = dyn_cast_or_null(Cand1.Function); @@ -12228,6 +12383,175 @@ 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_Synthesized; + 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_Synthesized) + std::swap(NewLHS, NewRHS); + + RewrittenRes = + S.CreateOverloadedBinOp(OpLoc, Opc, Fns, NewLHS, NewRHS, PerformADL, + /*AllowRewrittenCandidates*/ false); + if (RewrittenRes.isInvalid()) + return ExprError(); + } + Expr *Rewritten = RewrittenRes.get(); + + // Create a dummy expression representing the original expression as written. + // FIXME(EricWF): This doesn't actually really represent the expression as + // written, because it may not result in a to a builtin operator. + Expr *Original = new (S.Context) + BinaryOperator(OpaqueValueExpr::Create(S.Context, Args[0]), + OpaqueValueExpr::Create(S.Context, Args[1]), Opc, + Rewritten->getType(), Rewritten->getValueKind(), + Rewritten->getObjectKind(), OpLoc, S.FPFeatures); + + CXXRewrittenExpr::ExtraRewrittenBits ExtraBits; + ExtraBits.CompareBits.IsSynthesized = IsSynthesized; + return new (S.Context) CXXRewrittenExpr(CXXRewrittenExpr::Comparison, + Original, Rewritten, ExtraBits); +} + /// Create a binary operation that may resolve to an overloaded /// operator. /// @@ -12244,11 +12568,11 @@ /// /// \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 @@ -12310,11 +12634,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,191 +12670,98 @@ // 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(); - } - - // 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); - } + // 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(); + } - // 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(); + // 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 + // viable functions, then the operator is assumed to be the + // built-in operator and interpreted according to clause 5. + if (Opc == BO_Comma) + break; - ExprResult ArgsRes1 = - PerformImplicitConversion(Args[1], Best->BuiltinParamTypes[1], - Best->Conversions[1], AA_Passing); - if (ArgsRes1.isInvalid()) - return ExprError(); - Args[1] = ArgsRes1.get(); - break; + // For class as left operand for assignment or compound assignment + // 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) { + Diag(OpLoc, diag::err_ovl_no_viable_oper) + << 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(); } - } - - case OR_No_Viable_Function: { - // C++ [over.match.oper]p9: - // If the operator is the operator , [...] and there are no - // viable functions, then the operator is assumed to be the - // built-in operator and interpreted according to clause 5. - if (Opc == BO_Comma) - break; - - // For class as left operand for assignment or compound assignment - // 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) { - Diag(OpLoc, diag::err_ovl_no_viable_oper) - << 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(); - } - } else { - // This is an erroneous use of an operator which can be overloaded by - // a non-member function. Check for non-member operators which were - // defined too late to be candidates. - if (DiagnoseTwoPhaseOperatorLookup(*this, Op, OpLoc, Args)) - // FIXME: Recover by calling the found function. - return ExprError(); + } else { + // This is an erroneous use of an operator which can be overloaded by + // a non-member function. Check for non-member operators which were + // defined too late to be candidates. + if (DiagnoseTwoPhaseOperatorLookup(*this, Op, OpLoc, Args)) + // FIXME: Recover by calling the found function. + return ExprError(); - // No viable function; try to create a built-in operation, which will - // produce an error. Then, show the non-viable candidates. - Result = CreateBuiltinBinOp(OpLoc, Opc, Args[0], Args[1]); - } - assert(Result.isInvalid() && - "C++ binary operator overloading is missing candidates!"); - if (Result.isInvalid()) - CandidateSet.NoteCandidates(*this, OCD_AllCandidates, Args, - BinaryOperator::getOpcodeStr(Opc), OpLoc); - return Result; + // No viable function; try to create a built-in operation, which will + // produce an error. Then, show the non-viable candidates. + Result = CreateBuiltinBinOp(OpLoc, Opc, Args[0], Args[1]); } - - 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(); - CandidateSet.NoteCandidates(*this, OCD_ViableCandidates, Args, + assert(Result.isInvalid() && + "C++ binary operator overloading is missing candidates!"); + if (Result.isInvalid()) + CandidateSet.NoteCandidates(*this, OCD_AllCandidates, Args, BinaryOperator::getOpcodeStr(Opc), OpLoc); - return ExprError(); + 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(); + CandidateSet.NoteCandidates(*this, OCD_ViableCandidates, Args, + BinaryOperator::getOpcodeStr(Opc), OpLoc); + return ExprError(); - case OR_Deleted: - if (isImplicitlyDeleted(Best->Function)) { - CXXMethodDecl *Method = cast(Best->Function); - Diag(OpLoc, diag::err_ovl_deleted_special_oper) + case OR_Deleted: + if (isImplicitlyDeleted(Best->Function)) { + CXXMethodDecl *Method = cast(Best->Function); + Diag(OpLoc, diag::err_ovl_deleted_special_oper) << Context.getRecordType(Method->getParent()) << getSpecialMember(Method); - // The user probably meant to call this special member. Just - // explain why it's deleted. - NoteDeletedFunction(Method); - return ExprError(); - } else { - Diag(OpLoc, diag::err_ovl_deleted_oper) - << Best->Function->isDeleted() - << BinaryOperator::getOpcodeStr(Opc) + // The user probably meant to call this special member. Just + // explain why it's deleted. + NoteDeletedFunction(Method); + return ExprError(); + } else { + Diag(OpLoc, diag::err_ovl_deleted_oper) + << Best->Function->isDeleted() << BinaryOperator::getOpcodeStr(Opc) << getDeletedOrUnavailableSuffix(Best->Function) << Args[0]->getSourceRange() << Args[1]->getSourceRange(); - } - CandidateSet.NoteCandidates(*this, OCD_AllCandidates, Args, - BinaryOperator::getOpcodeStr(Opc), OpLoc); - return ExprError(); + } + CandidateSet.NoteCandidates(*this, OCD_AllCandidates, Args, + BinaryOperator::getOpcodeStr(Opc), OpLoc); + return ExprError(); } // We matched a built-in operator; build it. Index: lib/Sema/TreeTransform.h =================================================================== --- lib/Sema/TreeTransform.h +++ lib/Sema/TreeTransform.h @@ -3217,6 +3217,14 @@ return getSema().BuildEmptyCXXFoldExpr(EllipsisLoc, Operator); } + ExprResult + RebuildCXXRewrittenExpr(CXXRewrittenExpr::RewrittenKind Kind, Expr *Original, + Expr *Rewritten, + CXXRewrittenExpr::ExtraRewrittenBits ExtraBits) { + return new (SemaRef.Context) + CXXRewrittenExpr(Kind, Original, Rewritten, ExtraBits); + } + /// Build a new atomic operation expression. /// /// By default, performs semantic analysis to build the new expression. @@ -11515,6 +11523,68 @@ return getDerived().TransformExpr(E->GetTemporaryExpr()); } +static Expr *extractOperand(Expr *E, unsigned Idx) { + assert(Idx < 2); + if (auto *BO = dyn_cast(E)) { + if (Idx == 0) + return BO->getLHS(); + return BO->getRHS(); + } + if (auto *CE = dyn_cast(E)) { + assert(CE->getNumArgs() == 2); + return CE->getArg(Idx); + } + llvm_unreachable("unhandled case"); +} +static std::pair +extractOriginalOperandsFromRewrittenComparison(Expr *E, bool IsThreeWay, + bool IsSynthesized) { + if (IsThreeWay) + return {extractOperand(E, IsSynthesized ? 1 : 0), + extractOperand(E, IsSynthesized ? 0 : 1)}; + return extractOriginalOperandsFromRewrittenComparison( + extractOperand(E, IsSynthesized ? 1 : 0), true, IsSynthesized); +} + +template +ExprResult +TreeTransform::TransformCXXRewrittenExpr(CXXRewrittenExpr *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; + + Expr *Original; + switch (E->getRewrittenKind()) { + case CXXRewrittenExpr::Comparison: { + BinaryOperator *Op = cast(E->getOriginalExpr()); + + // Extract the already transformed operands from the rewritten expression. + std::pair OrigArgs = + extractOriginalOperandsFromRewrittenComparison( + Rewritten, Op->getOpcode() == BO_Cmp, + E->getRewrittenInfo()->CompareBits.IsSynthesized); + + // Build a dummy node representing the expression as written. + Original = new (SemaRef.Context) BinaryOperator( + OpaqueValueExpr::Create(SemaRef.Context, OrigArgs.first), + OpaqueValueExpr::Create(SemaRef.Context, OrigArgs.second), + Op->getOpcode(), Rewritten->getType(), Rewritten->getValueKind(), + Rewritten->getObjectKind(), Op->getOperatorLoc(), Op->getFPFeatures()); + break; + } + } + return getDerived().RebuildCXXRewrittenExpr( + E->getRewrittenKind(), Original, Rewritten, *E->getRewrittenInfo()); +} + template ExprResult TreeTransform::TransformCXXFoldExpr(CXXFoldExpr *E) { Index: lib/Serialization/ASTReaderStmt.cpp =================================================================== --- lib/Serialization/ASTReaderStmt.cpp +++ lib/Serialization/ASTReaderStmt.cpp @@ -1705,6 +1705,21 @@ E->Opcode = (BinaryOperatorKind)Record.readInt(); } +void ASTStmtReader::VisitCXXRewrittenExpr(CXXRewrittenExpr *E) { + VisitExpr(E); + E->setRewrittenKind( + static_cast(Record.readInt())); + E->SubExprs[0] = Record.readSubExpr(); + E->SubExprs[1] = Record.readSubExpr(); + switch (E->getRewrittenKind()) { + case CXXRewrittenExpr::Comparison: { + CXXRewrittenExpr::ComparisonBits &Bits = E->ExtraBits.CompareBits; + Bits.IsSynthesized = Record.readInt(); + break; + } + } +} + void ASTStmtReader::VisitOpaqueValueExpr(OpaqueValueExpr *E) { VisitExpr(E); E->SourceExpr = Record.readSubExpr(); @@ -4074,6 +4089,9 @@ S = new (Context) CXXFoldExpr(Empty); break; + case EXPR_CXX_REWRITTEN_OPERATOR: + S = new (Context) CXXRewrittenExpr(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,21 @@ Code = serialization::EXPR_CXX_FOLD; } +void ASTStmtWriter::VisitCXXRewrittenExpr(CXXRewrittenExpr *E) { + VisitExpr(E); + Record.push_back(E->getRewrittenKind()); + Record.AddStmt(E->SubExprs[0]); + Record.AddStmt(E->SubExprs[1]); + switch (E->getRewrittenKind()) { + case CXXRewrittenExpr::Comparison: { + CXXRewrittenExpr::ComparisonBits Bits = E->ExtraBits.CompareBits; + Record.push_back(Bits.IsSynthesized); + break; + } + } + 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::CXXRewrittenExprClass: 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 {}; +// expected-note@+1 2 {{candidate}} +Tag<0> operator<=>(X, Y) { + return {}; +} +// expected-note@+1 2 {{candidate}} +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; +}; + +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() { + // expected-error@+1 {{use of overloaded operator '<' is ambiguous}} + (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