Index: include/clang/AST/ComparisonCategories.h =================================================================== --- include/clang/AST/ComparisonCategories.h +++ include/clang/AST/ComparisonCategories.h @@ -160,6 +160,13 @@ static StringRef getCategoryString(ComparisonCategoryType Kind); static StringRef getResultString(ComparisonCategoryResult Kind); + /// \brief 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; + /// \brief Return the list of results which are valid for the specified /// comparison category type. static std::vector Index: include/clang/AST/Decl.h =================================================================== --- include/clang/AST/Decl.h +++ include/clang/AST/Decl.h @@ -2162,6 +2162,9 @@ /// true through IsAligned. bool isReplaceableGlobalAllocationFunction(bool *IsAligned = nullptr) const; + /// \brief Determine whether this is a C++2a default comparison operator. + bool isDefaultComparisonOperator(bool IsExplicitlyDefault = false) const; + /// \brief Determine whether this is a destroying operator delete. bool isDestroyingOperatorDelete() const; 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,138 @@ child_range children() { return child_range(SubExprs, SubExprs + 2); } }; +class CXXRewrittenOperator : public Expr { + friend class ASTReader; + friend class ASTWriter; + friend class ASTStmtReader; + friend class ASTStmtWriter; + + Stmt *SubExprs[2]; + + unsigned Kind : 2; + + CXXRewrittenOperator(EmptyShell Empty) + : Expr(CXXRewrittenOperatorClass, Empty) {} + +public: + typedef BinaryOperatorKind Opcode; + enum RewrittenOperatorKind { ROC_None, ROC_Rewritten, ROC_Synthesized }; + +public: + // FIXME(EricWF): Figure out if this will even be built for dependent + // expressions. + CXXRewrittenOperator(RewrittenOperatorKind Kind, Expr *Underlying, + Expr *Rewritten) + : Expr(CXXRewrittenOperatorClass, Rewritten->getType(), + Rewritten->getValueKind(), Rewritten->getObjectKind(), + /*Dependent*/ false, false, false, false), + Kind(Kind) { + SubExprs[0] = Underlying; + SubExprs[1] = Rewritten; + } + + Expr *getUnderlyingExpr() const { + return static_cast(SubExprs[0]); + } + Expr *getRewrittenExpr() const { + return static_cast(SubExprs[1]); + } + + void setKind(RewrittenOperatorKind xKind) { Kind = xKind; } + RewrittenOperatorKind getKind() const { + return static_cast(Kind); + } + bool isReverseOrder() const LLVM_READONLY { + return getKind() == ROC_Synthesized; + } + + static Expr *getLHSExpr(Expr *E) { + if (auto *UE = dyn_cast(E)) + return UE->getLHS(); + else + return cast(E)->getArg(0); + } + static Expr *getRHSExpr(Expr *E) { + if (auto *UE = dyn_cast(E)) + return UE->getRHS(); + else + return cast(E)->getArg(1); + } + + Expr *getOrigLHS() const { + if (!isReverseOrder()) + return getLHSExpr(getLHSExpr(getRewrittenExpr())); + else + return getRHSExpr(getRHSExpr(getRewrittenExpr())); + } + + Expr *getOrigRHS() const { + if (!isReverseOrder()) + return getRHSExpr(getLHSExpr(getRewrittenExpr())); + else + return getLHSExpr(getRHSExpr(getRewrittenExpr())); + } + + static Opcode getOpcodeFromExpr(Expr *E) { + if (auto *UE = dyn_cast(E)) + return UE->getOpcode(); + else + return BinaryOperator::getOverloadedOpcode( + cast(E)->getOperator()); + } + + Opcode getOrigOpcode() const; + + Expr *getLHS() const { return getLHSExpr(getRewrittenExpr()); } + Expr *getRHS() const { return getRHSExpr(getRewrittenExpr()); } + Opcode getOpcode() const { return getOpcodeFromExpr(getRewrittenExpr()); } + + // Forwarders to the getUnderlyingExpr() expression + + SourceLocation getLocStart() const { + Expr *Underlying = getUnderlyingExpr(); + if (auto *UE = dyn_cast(Underlying)) { + return UE->getLocStart(); + } else { + assert(isa(Underlying)); + return cast(Underlying)->getLocStart(); + } + } + SourceLocation getLocEnd() const { + Expr *Underlying = getUnderlyingExpr(); + if (auto *UE = dyn_cast(Underlying)) { + return UE->getLocEnd(); + } else { + assert(isa(Underlying)); + return cast(Underlying)->getLocEnd(); + } + } + SourceLocation getExprLoc() const { + Expr *Underlying = getUnderlyingExpr(); + if (auto *UE = dyn_cast(Underlying)) { + return UE->getExprLoc(); + } else { + assert(isa(Underlying)); + return cast(Underlying)->getExprLoc(); + } + } + SourceLocation getOperatorLoc() const { + Expr *Underlying = getUnderlyingExpr(); + if (auto *UE = dyn_cast(Underlying)) { + return UE->getOperatorLoc(); + } else { + assert(isa(Underlying)); + return cast(Underlying)->getOperatorLoc(); + } + } + + child_range children() { return child_range(SubExprs, SubExprs + 2); } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CXXRewrittenOperatorClass; + } +}; + /// \brief 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(CXXRewrittenOperator, { + TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getUnderlyingExpr()); + 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/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -1377,6 +1377,9 @@ def note_pure_qualified_call_kext : Note< "qualified call to %0::%1 is treated as a virtual call to %1 due to -fapple-kext">; +def err_defaulted_decl_not_first : Error< + "defaulted definition must be first declaration">; + def err_deleted_decl_not_first : Error< "deleted definition must be first declaration">; @@ -1660,8 +1663,8 @@ "implicit default constructor suppressed by user-declared constructor">; def note_nontrivial_no_copy : Note< "because no %select{<>|constructor|constructor|assignment operator|" - "assignment operator|<>}2 can be used to " - "%select{<>|copy|move|copy|move|<>}2 " + "assignment operator|<>|<>}2 can be used to " + "%select{<>|copy|move|copy|move|<>|<>}2 " "%select{base class|field|an object}0 of type %3">; def note_nontrivial_user_provided : Note< "because %select{base class of |field of |}0type %1 has a user-provided " @@ -3872,9 +3875,9 @@ "overload resolution selected %select{unavailable|deleted}0 operator '%1'%2">; def err_ovl_deleted_special_oper : Error< "object of type %0 cannot be %select{constructed|copied|moved|assigned|" - "assigned|destroyed}1 because its %select{default constructor|" + "assigned|destroyed|compared}1 because its %select{default constructor|" "copy constructor|move constructor|copy assignment operator|" - "move assignment operator|destructor}1 is implicitly deleted">; + "move assignment operator|destructor|comparison operator}1 is implicitly deleted">; def err_ovl_no_viable_subscript : Error<"no viable overloaded operator[] for type %0">; def err_ovl_no_oper : @@ -4658,11 +4661,11 @@ def err_definition_of_implicitly_declared_member : Error< "definition of implicitly declared %select{default constructor|copy " "constructor|move constructor|copy assignment operator|move assignment " - "operator|destructor|function}1">; + "operator|destructor|comparison operator|function}1">; def err_definition_of_explicitly_defaulted_member : Error< "definition of explicitly defaulted %select{default constructor|copy " "constructor|move constructor|copy assignment operator|move assignment " - "operator|destructor|function}0">; + "operator|destructor|comparison operator|function}0">; def err_redefinition_extern_inline : Error< "redefinition of a 'extern inline' function %0 is not supported in " "%select{C99 mode|C++}1">; @@ -6439,6 +6442,8 @@ "member %0 declared here">; def note_member_first_declared_here : Note< "member %0 first declared here">; +def note_first_declared_here : Note< + "%0 first declared here">; def err_decrement_bool : Error<"cannot decrement expression of type bool">; def warn_increment_bool : Warning< "incrementing expression of type bool is deprecated and " @@ -7726,13 +7731,16 @@ // C++11 defaulted functions def err_defaulted_special_member_params : Error< - "an explicitly-defaulted %select{|copy |move }0constructor cannot " + "an explicitly-defaulted %select{default constructor|copy constructor|" + "move constructor|<>|<>|<>|comparison operator}0 cannot " "have default arguments">; def err_defaulted_special_member_variadic : Error< - "an explicitly-defaulted %select{|copy |move }0constructor cannot " + "an explicitly-defaulted %select{default constructor|copy constructor|" + "move constructor|<>|<>|<>|comparison operator}0 cannot " "be variadic">; def err_defaulted_special_member_return_type : Error< - "explicitly-defaulted %select{copy|move}0 assignment operator must " + "explicitly-defaulted %select{<>|<>|<>|copy assignment|" + "move assignment|<>|comparison}0 operator must " "return %1">; def err_defaulted_special_member_quals : Error< "an explicitly-defaulted %select{copy|move}0 assignment operator may not " @@ -7740,7 +7748,7 @@ def err_defaulted_special_member_volatile_param : Error< "the parameter for an explicitly-defaulted %select{<>|" "copy constructor|move constructor|copy assignment operator|" - "move assignment operator|<>}0 may not be volatile">; + "move assignment operator|<>|comparison operator}0 may not be volatile">; def err_defaulted_special_member_move_const_param : Error< "the parameter for an explicitly-defaulted move " "%select{constructor|assignment operator}0 may not be const">; @@ -7754,11 +7762,12 @@ def err_incorrect_defaulted_exception_spec : Error< "exception specification of explicitly defaulted %select{default constructor|" "copy constructor|move constructor|copy assignment operator|move assignment " - "operator|destructor}0 does not match the " + "operator|destructor|comparison operator}0 does not match the " "calculated one">; def err_incorrect_defaulted_constexpr : Error< "defaulted definition of %select{default constructor|copy constructor|" - "move constructor|copy assignment operator|move assignment operator}0 " + "move constructor|copy assignment operator|move assignment operator|" + "destructor|comparison operator}0 " "is not constexpr">; def err_out_of_line_default_deletes : Error< "defaulting this %select{default constructor|copy constructor|move " @@ -7776,6 +7785,17 @@ "specification redeclared with an %select{implicit|explicit}0 exception " "specification">, InGroup>; +// C++2a defaulted comparison operators +def err_defaulted_comparison_operator_template : Error< + "an explicitly-defaulted comparison operator cannot be a template">; +def err_defaulted_comparison_operator_decl_scope : Error< + "an explicitly-defaulted comparison operator not declared at class scope">; +def err_defaulted_comparison_operator_param_type : Error< + "the parameter for this explicitly-defaulted comparison operator must have " + "type %0, but parameter has type %1">; +def err_defaulted_comparison_operator_member_non_const : Error< + "explicitly-defaulted comparison operator is not const qualified">; + def warn_ptr_arith_precedes_bounds : Warning< "the pointer decremented by %0 refers before the beginning of the array">, InGroup, DefaultIgnore; @@ -9409,4 +9429,8 @@ def err_std_compare_type_not_supported : Error< "standard library implementation of %0 is not supported; " "member '%1' does not have expected form">; +def ext_default_comparison_op : ExtWarn< + "defaulted comparison operator functions are a C++2a extension">, + InGroup; + } // end of sema component. 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 CXXRewrittenOperator : DStmt; // C++ Coroutines TS expressions def CoroutineSuspendExpr : DStmt; Index: include/clang/Sema/Overload.h =================================================================== --- include/clang/Sema/Overload.h +++ include/clang/Sema/Overload.h @@ -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 @@ -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. @@ -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; @@ -952,8 +1016,9 @@ } /// Find the best viable function on this overload set, if it exists. - OverloadingResult BestViableFunction(Sema &S, SourceLocation Loc, - OverloadCandidateSet::iterator& Best); + OverloadingResult BestViableFunction( + Sema &S, SourceLocation Loc, OverloadCandidateSet::iterator &Best, + SmallVectorImpl *EquivalentCands = nullptr); void NoteCandidates(Sema &S, OverloadCandidateDisplayKind OCD, Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -608,13 +608,14 @@ SmallVector, 2> DelayedExceptionSpecChecks; - /// \brief All the members seen during a class definition which were both - /// explicitly defaulted and had explicitly-specified exception - /// specifications, along with the function type containing their - /// user-specified exception specification. Those exception specifications - /// were overridden with the default specifications, but we still need to - /// check whether they are compatible with the default specification, and - /// we can't do that until the nesting set of class definitions is complete. + /// \brief All the members or comparison friend functions seen during a + /// class definition which were both explicitly defaulted and had + /// explicitly-specified exception specifications, along with the function + /// type containing their user-specified exception specification. Those + /// exception specifications were overridden with the default specifications, + /// but we still need to check whether they are compatible with the default + /// specification, and we can't do that until the nesting set of class + /// definitions is complete. SmallVector, 2> DelayedDefaultedMemberExceptionSpecs; @@ -1030,18 +1031,47 @@ }; private: - llvm::PointerIntPair Pair; + union { + FunctionDecl *Function; + void *BuiltinArgType; + }; + unsigned char LookupKind : 2; + unsigned char IsBuiltin : 1; public: - SpecialMemberOverloadResult() : Pair() {} - SpecialMemberOverloadResult(CXXMethodDecl *MD) - : Pair(MD, MD->isDeleted() ? NoMemberOrDeleted : Success) {} - - CXXMethodDecl *getMethod() const { return Pair.getPointer(); } - void setMethod(CXXMethodDecl *MD) { Pair.setPointer(MD); } - - Kind getKind() const { return static_cast(Pair.getInt()); } - void setKind(Kind K) { Pair.setInt(K); } + SpecialMemberOverloadResult() + : Function(nullptr), LookupKind(0), IsBuiltin(false) {} + SpecialMemberOverloadResult(FunctionDecl *FD) + : Function(FD), + LookupKind(FD->isDeleted() ? NoMemberOrDeleted : Success), + IsBuiltin(false) {} + SpecialMemberOverloadResult(QualType Ty) + : BuiltinArgType(Ty.getAsOpaquePtr()), LookupKind(Success), + IsBuiltin(true) {} + + FunctionDecl *getFunction() const { + if (IsBuiltin) + return nullptr; + return Function; + } + void setFunction(FunctionDecl *FD) { + Function = FD; + IsBuiltin = false; + } + QualType getBuiltinArgType() const { + if (!IsBuiltin) + return QualType(); + return QualType::getFromOpaquePtr(BuiltinArgType); + } + void setBuiltinArgType(QualType Ty) { + BuiltinArgType = Ty.getAsOpaquePtr(); + IsBuiltin = true; + } + bool hasBuiltin() const { return IsBuiltin; } + bool hasFunction() const { return !IsBuiltin && Function; } + + Kind getKind() const { return static_cast(LookupKind); } + void setKind(Kind K) { LookupKind = K; } }; class SpecialMemberOverloadResultEntry @@ -1124,7 +1154,8 @@ /// of -Wselector. llvm::MapVector ReferencedSelectors; - /// Kinds of C++ special members. + /// Kind of C++ members which can be defaulted. Including special members and + /// explicitly defaulted comparison operators. enum CXXSpecialMember { CXXDefaultConstructor, CXXCopyConstructor, @@ -1132,6 +1163,7 @@ CXXCopyAssignment, CXXMoveAssignment, CXXDestructor, + CXXComparisonOperator, CXXInvalid }; @@ -2232,10 +2264,11 @@ TAH_ConsiderTrivialABI }; - bool SpecialMemberIsTrivial(CXXMethodDecl *MD, CXXSpecialMember CSM, + bool SpecialMemberIsTrivial(FunctionDecl *FD, CXXSpecialMember CSM, TrivialABIHandling TAH = TAH_IgnoreTrivialABI, bool Diagnose = false); - CXXSpecialMember getSpecialMember(const CXXMethodDecl *MD); + CXXSpecialMember getSpecialMember(const FunctionDecl *FD, + bool IsExplicitlyDefault = false); void ActOnLastBitfield(SourceLocation DeclStart, SmallVectorImpl &AllIvarDecls); Decl *ActOnIvar(Scope *S, SourceLocation DeclStart, @@ -2777,6 +2810,12 @@ void AddBuiltinOperatorCandidates(OverloadedOperatorKind Op, SourceLocation OpLoc, ArrayRef Args, OverloadCandidateSet& CandidateSet); + void AddRewrittenOperatorCandidates(OverloadedOperatorKind Op, + SourceLocation OpLoc, + ArrayRef Args, + const UnresolvedSetImpl &Fns, + OverloadCandidateSet &CandidateSet, + bool PerformADL); void AddArgumentDependentLookupCandidates(DeclarationName Name, SourceLocation Loc, ArrayRef Args, @@ -2919,11 +2958,10 @@ 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, @@ -3182,6 +3220,13 @@ bool RValueThis, unsigned ThisQuals); CXXDestructorDecl *LookupDestructor(CXXRecordDecl *Class); + /// \brief Lookup the three-way comparison operator for the specified ArgTy. + /// The comparison is homogenious as required for explicitly-defaulted + /// comparison operators. ArgTy is transformed into a const lvalue before + /// lookup is performed. + SpecialMemberOverloadResult LookupThreeWayComparison(QualType ArgTy, + SourceLocation Loc); + bool checkLiteralOperatorId(const CXXScopeSpec &SS, const UnqualifiedId &Id); LiteralOperatorLookupResult LookupLiteralOperator(Scope *S, LookupResult &R, ArrayRef ArgTys, @@ -3189,6 +3234,7 @@ bool AllowTemplate, bool AllowStringTemplate, bool DiagnoseMissing); + bool isKnownName(StringRef name); void ArgumentDependentLookup(DeclarationName Name, SourceLocation Loc, @@ -4746,7 +4792,7 @@ const QualType *data() const { return Exceptions.data(); } /// \brief Integrate another called method into the collected data. - void CalledDecl(SourceLocation CallLoc, const CXXMethodDecl *Method); + void CalledDecl(SourceLocation CallLoc, const CXXMethodDecl *MD); /// \brief Integrate an invoked expression into the collected data. void CalledExpr(Expr *E); @@ -4839,8 +4885,8 @@ class InheritedConstructorInfo; - /// \brief Determine if a special member function should have a deleted - /// definition when it is defaulted. + /// \brief Determine if a special member function, or defaulted comparison + /// operator, should have a deleted definition when it is defaulted. bool ShouldDeleteSpecialMember(CXXMethodDecl *MD, CXXSpecialMember CSM, InheritedConstructorInfo *ICI = nullptr, bool Diagnose = false); @@ -4967,6 +5013,17 @@ /// it simply returns the passed in expression. ExprResult MaybeBindToTemporary(Expr *E); + /// \brief Check whether the specified explicitly-defaulted comparison + /// declaration is ollowed. + /// + /// \return true if an error occurred + bool checkDefaultedComparisonOperatorDecl(SourceLocation DefaultLoc, + Decl *Dcl); + + /// \brief Defines a explicitly-defaulted comparison operator. + void DefineDefaultedComparisonOperator(SourceLocation CurrentLocation, + FunctionDecl *FD); + bool CompleteConstructorCall(CXXConstructorDecl *Constructor, MultiExprArg ArgsPtr, SourceLocation Loc, @@ -5896,7 +5953,7 @@ StorageClass &SC); void CheckDeductionGuideTemplate(FunctionTemplateDecl *TD); - void CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD); + void CheckExplicitlyDefaultedMember(FunctionDecl *FD); void CheckExplicitlyDefaultedMemberExceptionSpec(CXXMethodDecl *MD, const FunctionProtoType *T); void CheckDelayedMemberExceptionSpecs(); @@ -10355,11 +10412,13 @@ const FunctionProtoType *Proto, SourceLocation Loc); +public: void checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto, const Expr *ThisArg, ArrayRef Args, bool IsMemberFunction, SourceLocation Loc, SourceRange Range, VariadicCallType CallType); +private: bool CheckObjCString(Expr *Arg); ExprResult CheckOSLogFormatStringArg(Expr *Arg); Index: include/clang/Serialization/ASTBitCodes.h =================================================================== --- include/clang/Serialization/ASTBitCodes.h +++ include/clang/Serialization/ASTBitCodes.h @@ -1808,6 +1808,7 @@ EXPR_FUNCTION_PARM_PACK, // FunctionParmPackExpr EXPR_MATERIALIZE_TEMPORARY, // MaterializeTemporaryExpr EXPR_CXX_FOLD, // CXXFoldExpr + EXPR_CXX_REWRITTEN_OPERATOR, // CXXRewrittenOperator // CUDA EXPR_CUDA_KERNEL_CALL, // CUDAKernelCallExpr Index: lib/AST/ComparisonCategories.cpp =================================================================== --- lib/AST/ComparisonCategories.cpp +++ lib/AST/ComparisonCategories.cpp @@ -187,3 +187,49 @@ Values.push_back(CCR::Unordered); return Values; } + +/// C++2a [class.spaceship]p4 - compute the common category type. +const ComparisonCategoryInfo *ComparisonCategories::computeCommonComparisonType( + ArrayRef Types) const { + 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 Ty : Types) { + const ComparisonCategoryInfo *Info = lookupInfoForType(Ty); + // --- If any T is not a comparison category type, U is void. + if (!Info) + return nullptr; + Seen[static_cast(Info->Kind)]++; + } + // --- 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 lookupInfo(CCT::WeakEquality); + + // --- Otherwise, if at least one Ti is std::strong_equality, U is + // std::strong_equality + if (Count(CCT::StrongEquality)) + return lookupInfo(CCT::StrongEquality); + + // --- Otherwise, if at least one Ti is std::partial_ordering, U is + // std::partial_ordering. + if (Count(CCT::PartialOrdering)) + return lookupInfo(CCT::PartialOrdering); + + // --- Otherwise, if at least one Ti is std::weak_ordering, U is + // std::weak_ordering. + if (Count(CCT::WeakOrdering)) + return lookupInfo(CCT::WeakOrdering); + + // FIXME(EricWF): What if we don't find std::strong_ordering + // --- Otherwise, U is std::strong_ordering. + return lookupInfo(CCT::StrongOrdering); +} Index: lib/AST/Decl.cpp =================================================================== --- lib/AST/Decl.cpp +++ lib/AST/Decl.cpp @@ -2809,6 +2809,21 @@ RD->getIdentifier()->isStr("destroying_delete_t"); } +bool FunctionDecl::isDefaultComparisonOperator(bool IsExplicitlyDefault) const { + // C++2a [class.campare.default] A defaulted comparison operator function + // ([expr.spaceship], [expr.rel], [expr.eq]) for some class C shall be a + // non-template function declared in the member-specification of C that is + // (1.1) a non-static member of C having one parameter of type const C&, or + if ((!isDefaulted() && !IsExplicitlyDefault) || !isOverloadedOperator()) + return false; + if (!isa(this) && !isa(getLexicalParent())) + return false; + auto Opc = BinaryOperator::getOverloadedOpcode(getOverloadedOperator()); + if (BinaryOperator::isComparisonOp(Opc)) + return true; + return false; +} + LanguageLinkage FunctionDecl::getLanguageLinkage() const { return getDeclLanguageLinkage(*this); } Index: lib/AST/Expr.cpp =================================================================== --- lib/AST/Expr.cpp +++ lib/AST/Expr.cpp @@ -3187,6 +3187,11 @@ return false; } + case CXXRewrittenOperatorClass: { + 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. 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::CXXRewrittenOperatorClass: + 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 VisitCXXRewrittenOperator(const CXXRewrittenOperator *E) { + return StmtVisitorTy::Visit(E->getRewrittenExpr()); + } }; } // namespace @@ -10847,6 +10851,8 @@ case Expr::ChooseExprClass: { return CheckICE(cast(E)->getChosenSubExpr(), Ctx); } + case Expr::CXXRewrittenOperatorClass: + return CheckICE(cast(E)->getRewrittenExpr(), Ctx); } llvm_unreachable("Invalid StmtClass!"); Index: lib/AST/ItaniumMangle.cpp =================================================================== --- lib/AST/ItaniumMangle.cpp +++ lib/AST/ItaniumMangle.cpp @@ -3551,6 +3551,11 @@ mangleExpression(cast(E)->getSubExpr(), Arity); break; + case Expr::CXXRewrittenOperatorClass: + // FIXME(EricWF): Is this correct? + mangleExpression(cast(E)->getRewrittenExpr(), 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,15 @@ OS << ")"; } +void StmtPrinter::VisitCXXRewrittenOperator(CXXRewrittenOperator *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->getOrigLHS()); + OS << " " << BinaryOperator::getOpcodeStr(E->getOpcode()) << " "; + Visit(E->getOrigRHS()); +} + // 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::VisitCXXRewrittenOperator(const CXXRewrittenOperator *S) { + VisitExpr(S); + ID.AddInteger(S->getKind()); +} + 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 *VisitCXXRewrittenOperator(const CXXRewrittenOperator *E) { + return Visit(E->getRewrittenExpr()); + } + llvm::Value *EmitIncDecConsiderOverflowBehavior(const UnaryOperator *E, llvm::Value *InVal, bool IsInc); Index: lib/Sema/SemaCUDA.cpp =================================================================== --- lib/Sema/SemaCUDA.cpp +++ lib/Sema/SemaCUDA.cpp @@ -303,10 +303,11 @@ /* ConstThis */ false, /* VolatileThis */ false); - if (!SMOR.getMethod()) + if (!SMOR.getFunction()) continue; - CUDAFunctionTarget BaseMethodTarget = IdentifyCUDATarget(SMOR.getMethod()); + CUDAFunctionTarget BaseMethodTarget = + IdentifyCUDATarget(SMOR.getFunction()); if (!InferredTarget.hasValue()) { InferredTarget = BaseMethodTarget; } else { @@ -346,11 +347,11 @@ /* ConstThis */ false, /* VolatileThis */ false); - if (!SMOR.getMethod()) + if (!SMOR.getFunction()) continue; CUDAFunctionTarget FieldMethodTarget = - IdentifyCUDATarget(SMOR.getMethod()); + IdentifyCUDATarget(SMOR.getFunction()); if (!InferredTarget.hasValue()) { InferredTarget = FieldMethodTarget; } else { Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -2799,24 +2799,25 @@ } // end anonymous namespace /// getSpecialMember - get the special member enum for a method. -Sema::CXXSpecialMember Sema::getSpecialMember(const CXXMethodDecl *MD) { +Sema::CXXSpecialMember Sema::getSpecialMember(const FunctionDecl *FD, + bool IsExplicitlyDefault) { + if (FD->isDefaultComparisonOperator(IsExplicitlyDefault)) + return Sema::CXXComparisonOperator; + if (const auto *MD = dyn_cast(FD)) { if (const CXXConstructorDecl *Ctor = dyn_cast(MD)) { if (Ctor->isDefaultConstructor()) return Sema::CXXDefaultConstructor; - if (Ctor->isCopyConstructor()) return Sema::CXXCopyConstructor; - if (Ctor->isMoveConstructor()) return Sema::CXXMoveConstructor; - } else if (isa(MD)) { + } else if (isa(MD)) return Sema::CXXDestructor; - } else if (MD->isCopyAssignmentOperator()) { + else if (MD->isCopyAssignmentOperator()) return Sema::CXXCopyAssignment; - } else if (MD->isMoveAssignmentOperator()) { + else if (MD->isMoveAssignmentOperator()) return Sema::CXXMoveAssignment; } - return Sema::CXXInvalid; } Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -149,15 +149,14 @@ } } -void -Sema::ImplicitExceptionSpecification::CalledDecl(SourceLocation CallLoc, - const CXXMethodDecl *Method) { +void Sema::ImplicitExceptionSpecification::CalledDecl( + SourceLocation CallLoc, const CXXMethodDecl *Method) { // If we have an MSAny spec already, don't bother. if (!Method || ComputedEST == EST_MSAny) return; - const FunctionProtoType *Proto - = Method->getType()->getAs(); + const FunctionProtoType *Proto = + Method->getType()->getAs(); Proto = Self->ResolveExceptionSpec(CallLoc, Proto); if (!Proto) return; @@ -5765,27 +5764,30 @@ } } -static void DefineImplicitSpecialMember(Sema &S, CXXMethodDecl *MD, +static void DefineImplicitSpecialMember(Sema &S, FunctionDecl *FD, SourceLocation DefaultLoc) { - switch (S.getSpecialMember(MD)) { + switch (S.getSpecialMember(FD)) { case Sema::CXXDefaultConstructor: S.DefineImplicitDefaultConstructor(DefaultLoc, - cast(MD)); + cast(FD)); break; case Sema::CXXCopyConstructor: - S.DefineImplicitCopyConstructor(DefaultLoc, cast(MD)); + S.DefineImplicitCopyConstructor(DefaultLoc, cast(FD)); break; case Sema::CXXCopyAssignment: - S.DefineImplicitCopyAssignment(DefaultLoc, MD); + S.DefineImplicitCopyAssignment(DefaultLoc, cast(FD)); break; case Sema::CXXDestructor: - S.DefineImplicitDestructor(DefaultLoc, cast(MD)); + S.DefineImplicitDestructor(DefaultLoc, cast(FD)); break; case Sema::CXXMoveConstructor: - S.DefineImplicitMoveConstructor(DefaultLoc, cast(MD)); + S.DefineImplicitMoveConstructor(DefaultLoc, cast(FD)); break; case Sema::CXXMoveAssignment: - S.DefineImplicitMoveAssignment(DefaultLoc, MD); + S.DefineImplicitMoveAssignment(DefaultLoc, cast(FD)); + break; + case Sema::CXXComparisonOperator: + S.DefineDefaultedComparisonOperator(DefaultLoc, FD); break; case Sema::CXXInvalid: llvm_unreachable("Invalid special member."); @@ -6006,7 +6008,7 @@ bool HasMethodWithOverrideControl = false, HasOverridingMethodWithoutOverrideControl = false; if (!Record->isDependentType()) { - for (auto *M : Record->methods()) { + for (CXXMethodDecl *M : Record->methods()) { // See if a method overloads virtual methods in a base // class without overriding any. if (!M->isStatic()) @@ -6017,7 +6019,7 @@ HasOverridingMethodWithoutOverrideControl = true; // Check whether the explicitly-defaulted special members are valid. if (!M->isInvalidDecl() && M->isExplicitlyDefaulted()) - CheckExplicitlyDefaultedSpecialMember(M); + CheckExplicitlyDefaultedMember(M); // For an explicitly defaulted or deleted special member, we defer // determining triviality until the class is complete. That time is now! @@ -6037,7 +6039,8 @@ // Set triviality for the purpose of calls if this is a user-provided // copy/move constructor or destructor. if ((CSM == CXXCopyConstructor || CSM == CXXMoveConstructor || - CSM == CXXDestructor) && M->isUserProvided()) { + CSM == CXXDestructor) && + M->isUserProvided()) { M->setTrivialForCall(HasTrivialABI); Record->setTrivialForCallFlags(M); } @@ -6229,11 +6232,108 @@ Sema::SpecialMemberOverloadResult SMOR = lookupCallFromSpecialMember(S, ClassDecl, CSM, Quals, ConstRHS); - if (!SMOR.getMethod()) + if (!SMOR.getFunction()) // A constructor we wouldn't select can't be "involved in initializing" // anything. return true; - return SMOR.getMethod()->isConstexpr(); + return SMOR.getFunction()->isConstexpr(); +} + +struct DefaultedComparisonInfo { + llvm::SmallVector ReturnTypes; + QualType CommonCategoryType; + bool ShouldDelete = false; + bool IsConstexpr = true; +}; + +static bool deduceBuiltinComparisonReturnType(Sema &S, QualType ArgType, + DefaultedComparisonInfo &Info) { + using CCT = ComparisonCategoryType; + using CCInfo = ComparisonCategoryInfo; + + ComparisonCategoryType ReturnKind; + if (ArgType->hasFloatingRepresentation()) { + ReturnKind = CCT::PartialOrdering; + } else if (ArgType->isIntegralOrEnumerationType()) { + ReturnKind = CCT::StrongOrdering; + } else if (ArgType->isMemberPointerType()) { + ReturnKind = CCT::StrongEquality; + } else if (ArgType->isPointerType()) { + ReturnKind = CCT::StrongOrdering; + } else { + // Not a type supported for builtin comparisons. (likely an array type) + Info.IsConstexpr = false; + Info.ShouldDelete = true; + return true; + } + CCInfo *CompInfo = S.Context.CompCategories.lookupInfo(ReturnKind); + if (!CompInfo) { + Info.IsConstexpr = false; + Info.ShouldDelete = true; + return true; + } + Info.ReturnTypes.push_back(QualType(CompInfo->CCDecl->getTypeForDecl(), 0)); + return false; +} + +static DefaultedComparisonInfo +getDefaultedComparisonInfo(Sema &S, CXXRecordDecl *ClassDecl) { + using OvlResult = Sema::SpecialMemberOverloadResult; + using ResultKind = OvlResult::Kind; + DefaultedComparisonInfo Info; + auto CheckOverload = [&](QualType ArgType) { + // FIXME(EricWF): Get and pass a better source location here. + OvlResult Res = + S.LookupThreeWayComparison(ArgType, ClassDecl->getLocStart()); + switch (Res.getKind()) { + case ResultKind::Success: { + if (Res.hasBuiltin()) { + if (deduceBuiltinComparisonReturnType(S, Res.getBuiltinArgType(), Info)) + return true; + } else { + Info.IsConstexpr = Info.IsConstexpr && Res.getFunction()->isConstexpr(); + Info.ReturnTypes.push_back(Res.getFunction()->getReturnType()); + } + return false; + } + case ResultKind::Ambiguous: + case ResultKind::NoMemberOrDeleted: { + Info.IsConstexpr = false; + Info.ShouldDelete = true; + return true; + } + } + }; + for (const auto &B : ClassDecl->bases()) { + const RecordType *BaseType = B.getType()->getAs(); + if (!BaseType) + continue; + + CXXRecordDecl *BaseClassDecl = cast(BaseType->getDecl()); + QualType ArgTy(BaseClassDecl->getTypeForDecl(), 0); + if (CheckOverload(ArgTy)) + return Info; + } + + for (const auto *F : ClassDecl->fields()) { + if (F->isInvalidDecl()) + continue; + QualType BaseType = S.Context.getBaseElementType(F->getType()); + if (CheckOverload(BaseType)) + return Info; + } + + // OK, we have a potentially OK comparison operator. Deduce it's common + // category type. + const ComparisonCategoryInfo *CCInfo = + S.Context.CompCategories.computeCommonComparisonType(Info.ReturnTypes); + if (!CCInfo) { + Info.ShouldDelete = true; + Info.CommonCategoryType = S.Context.VoidTy; + } else { + Info.CommonCategoryType = QualType(CCInfo->CCDecl->getTypeForDecl(), 0); + } + return Info; } /// Determine whether the specified special member function would be constexpr @@ -6272,7 +6372,8 @@ // In C++1y, we need to perform overload resolution. Ctor = false; break; - + case Sema::CXXComparisonOperator: + llvm_unreachable("handled elsewhere"); case Sema::CXXDestructor: case Sema::CXXInvalid: return false; @@ -6343,8 +6444,9 @@ } static Sema::ImplicitExceptionSpecification -ComputeDefaultedSpecialMemberExceptionSpec( - Sema &S, SourceLocation Loc, CXXMethodDecl *MD, Sema::CXXSpecialMember CSM, +ComputeDefaultedSpecialMemberExceptionSpec(Sema &S, SourceLocation Loc, + CXXMethodDecl *MD, + Sema::CXXSpecialMember CSM, Sema::InheritedConstructorInfo *ICI); static Sema::ImplicitExceptionSpecification @@ -6377,7 +6479,8 @@ return EPI; } -void Sema::EvaluateImplicitExceptionSpec(SourceLocation Loc, CXXMethodDecl *MD) { +void Sema::EvaluateImplicitExceptionSpec(SourceLocation Loc, + CXXMethodDecl *MD) { const FunctionProtoType *FPT = MD->getType()->castAs(); if (FPT->getExceptionSpecType() != EST_Unevaluated) return; @@ -6398,19 +6501,33 @@ UpdateExceptionSpec(MD->getCanonicalDecl(), ESI); } -void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) { - CXXRecordDecl *RD = MD->getParent(); - CXXSpecialMember CSM = getSpecialMember(MD); +void Sema::CheckExplicitlyDefaultedMember(FunctionDecl *FD) { + CXXSpecialMember CSM = getSpecialMember(FD); - assert(MD->isExplicitlyDefaulted() && CSM != CXXInvalid && + assert(FD->isExplicitlyDefaulted() && CSM != CXXInvalid && "not an explicitly-defaulted special member"); // Whether this was the first-declared instance of the constructor. // This affects whether we implicitly add an exception spec and constexpr. - bool First = MD == MD->getCanonicalDecl(); + FunctionDecl *CanonFD = FD->getCanonicalDecl(); + bool First = FD == CanonFD; + + bool IsMethod = isa(FD); + + // Dig up the record decl in which this function was declared. If it's a + // defaulted non-method comparison operator, the canonical decls lexical + // context will provide this. + + CXXRecordDecl *RD; + if (IsMethod) + RD = cast(FD)->getParent(); + else + RD = dyn_cast(FD->getLexicalParent()); + assert(RD); bool HadError = false; + // C++11 [dcl.fct.def.default]p1: // A function that is explicitly defaulted shall // -- be a special member function (checked elsewhere), @@ -6421,20 +6538,25 @@ unsigned ExpectedParams = 1; if (CSM == CXXDefaultConstructor || CSM == CXXDestructor) ExpectedParams = 0; - if (MD->getNumParams() != ExpectedParams) { - // This also checks for default arguments: a copy or move constructor with a - // default argument is classified as a default constructor, and assignment - // operations and destructors can't have default arguments. - Diag(MD->getLocation(), diag::err_defaulted_special_member_params) - << CSM << MD->getSourceRange(); + else if (!IsMethod) { + assert(CSM == CXXComparisonOperator); + ExpectedParams = 2; + } + if (FD->getNumParams() != ExpectedParams) { + // This also checks for default arguments: a copy or move constructor with + // a default argument is classified as a default constructor, and + // assignment operations and destructors can't have default arguments. + Diag(FD->getLocation(), diag::err_defaulted_special_member_params) + << CSM << FD->getSourceRange(); + HadError = true; - } else if (MD->isVariadic()) { - Diag(MD->getLocation(), diag::err_defaulted_special_member_variadic) - << CSM << MD->getSourceRange(); + } else if (FD->isVariadic()) { + Diag(FD->getLocation(), diag::err_defaulted_special_member_variadic) + << CSM << FD->getSourceRange(); HadError = true; } - const FunctionProtoType *Type = MD->getType()->getAs(); + const FunctionProtoType *Type = FD->getType()->getAs(); bool CanHaveConstParam = false; if (CSM == CXXCopyConstructor) @@ -6449,14 +6571,14 @@ QualType ExpectedReturnType = Context.getLValueReferenceType(Context.getTypeDeclType(RD)); if (!Context.hasSameType(ReturnType, ExpectedReturnType)) { - Diag(MD->getLocation(), diag::err_defaulted_special_member_return_type) - << (CSM == CXXMoveAssignment) << ExpectedReturnType; + Diag(FD->getLocation(), diag::err_defaulted_special_member_return_type) + << CSM << ExpectedReturnType; HadError = true; } // A defaulted special member cannot have cv-qualifiers. if (Type->getTypeQuals()) { - Diag(MD->getLocation(), diag::err_defaulted_special_member_quals) + Diag(FD->getLocation(), diag::err_defaulted_special_member_quals) << (CSM == CXXMoveAssignment) << getLangOpts().CPlusPlus14; HadError = true; } @@ -6465,25 +6587,78 @@ // Check for parameter type matching. QualType ArgType = ExpectedParams ? Type->getParamType(0) : QualType(); bool HasConstParam = false; - if (ExpectedParams && ArgType->isReferenceType()) { + // FIXME(EricWF): Integrate this better + DefaultedComparisonInfo CompareInfo; + if (CSM == CXXComparisonOperator) { + CompareInfo = getDefaultedComparisonInfo(*this, RD); + + auto Opc = BinaryOperator::getOverloadedOpcode(FD->getOverloadedOperator()); + bool IsEqualityOrRelational = BinaryOperator::isEqualityOp(Opc) || + BinaryOperator::isRelationalOp(Opc); + ReturnType = FD->getReturnType(); + if (IsEqualityOrRelational) { + QualType ExpectTy = Context.BoolTy; + if (!Context.hasSameType(ReturnType, ExpectTy)) { + Diag(FD->getLocation(), diag::err_defaulted_special_member_return_type) + << CSM << ExpectTy; + HadError = true; + } + } else { + if (auto *AutoTy = FD->getReturnType()->getAs()) { + if (!CompareInfo.CommonCategoryType.isNull()) + ReturnType = CompareInfo.CommonCategoryType; + else + ReturnType = Context.VoidTy; + } + if (!FD->isInvalidDecl() && + Context.hasSameType(ReturnType, Context.VoidTy)) + CompareInfo.ShouldDelete = true; + } + + // FIXME(EricWF): This isn't currently required by the standard, however, + // not requiring it leads to issues. + if (IsMethod && !Type->isConst()) { + Diag(FD->getLocEnd(), + diag::err_defaulted_comparison_operator_member_non_const) + << FD->getSourceRange(); + HadError = true; + } + + // Check the types of the parameters. They must be 'T const&' where T is + // the record type it is being declared within. + if (FD->getNumParams() == ExpectedParams) { + QualType ExpectedParamType = Context.getLValueReferenceType( + Context.getTypeDeclType(RD).withConst()); + for (unsigned I = 0, End = FD->getNumParams(); I < End; ++I) { + QualType ParamTy = FD->getParamDecl(I)->getType(); + if (!ParamTy->isDependentType() && + !Context.hasSameType(ExpectedParamType, ParamTy)) { + Diag(FD->getLocation(), + diag::err_defaulted_comparison_operator_param_type) + << ExpectedParamType << ParamTy + << FD->getParamDecl(I)->getSourceRange(); + HadError = true; + } + } + } + } else if (ExpectedParams && ArgType->isReferenceType()) { // Argument must be reference to possibly-const T. QualType ReferentType = ArgType->getPointeeType(); HasConstParam = ReferentType.isConstQualified(); if (ReferentType.isVolatileQualified()) { - Diag(MD->getLocation(), - diag::err_defaulted_special_member_volatile_param) << CSM; + Diag(FD->getLocation(), diag::err_defaulted_special_member_volatile_param) + << CSM; HadError = true; } - if (HasConstParam && !CanHaveConstParam) { if (CSM == CXXCopyConstructor || CSM == CXXCopyAssignment) { - Diag(MD->getLocation(), + Diag(FD->getLocation(), diag::err_defaulted_special_member_copy_const_param) << (CSM == CXXCopyAssignment); // FIXME: Explain why this special member can't be const. } else { - Diag(MD->getLocation(), + Diag(FD->getLocation(), diag::err_defaulted_special_member_move_const_param) << (CSM == CXXMoveAssignment); } @@ -6493,7 +6668,7 @@ // A copy assignment operator can take its argument by value, but a // defaulted one cannot. assert(CSM == CXXCopyAssignment && "unexpected non-ref argument"); - Diag(MD->getLocation(), diag::err_defaulted_copy_assign_not_ref); + Diag(FD->getLocation(), diag::err_defaulted_copy_assign_not_ref); HadError = true; } @@ -6504,27 +6679,32 @@ // makes such functions always instantiate to constexpr functions. For // functions which cannot be constexpr (for non-constructors in C++11 and for // destructors in C++1y), this is checked elsewhere. - bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, RD, CSM, - HasConstParam); - if ((getLangOpts().CPlusPlus14 ? !isa(MD) - : isa(MD)) && - MD->isConstexpr() && !Constexpr && - MD->getTemplatedKind() == FunctionDecl::TK_NonTemplate) { - Diag(MD->getLocStart(), diag::err_incorrect_defaulted_constexpr) << CSM; + bool Constexpr = + CSM == CXXComparisonOperator + ? CompareInfo.IsConstexpr + : defaultedSpecialMemberIsConstexpr(*this, RD, CSM, HasConstParam); + if ((getLangOpts().CPlusPlus14 ? !isa(FD) + : isa(FD)) && + FD->isConstexpr() && !Constexpr && + FD->getTemplatedKind() == FunctionDecl::TK_NonTemplate) { + Diag(FD->getLocStart(), diag::err_incorrect_defaulted_constexpr) << CSM; // FIXME: Explain why the special member can't be constexpr. HadError = true; } // and may have an explicit exception-specification only if it is compatible // with the exception-specification on the implicit declaration. - if (Type->hasExceptionSpec()) { + // FIXME(EricWF): Check exception specs? + if (Type->hasExceptionSpec() && CSM != CXXComparisonOperator) { + assert(isa(FD)); + CXXMethodDecl *MD = cast(FD); // Delay the check if this is the first declaration of the special member, // since we may not have parsed some necessary in-class initializers yet. if (First) { // If the exception specification needs to be instantiated, do so now, // before we clobber it with an EST_Unevaluated specification below. if (Type->getExceptionSpecType() == EST_Uninstantiated) { - InstantiateExceptionSpec(MD->getLocStart(), MD); + InstantiateExceptionSpec(FD->getLocStart(), MD); Type = MD->getType()->getAs(); } DelayedDefaultedMemberExceptionSpecs.push_back(std::make_pair(MD, Type)); @@ -6536,34 +6716,41 @@ if (First) { // -- it is implicitly considered to be constexpr if the implicit // definition would be, - MD->setConstexpr(Constexpr); - + FD->setConstexpr(Constexpr); + llvm::SmallVector Args; + Args.append(ExpectedParams, ArgType); // -- it is implicitly considered to have the same exception-specification // as if it had been implicitly declared, FunctionProtoType::ExtProtoInfo EPI = Type->getExtProtoInfo(); - EPI.ExceptionSpec.Type = EST_Unevaluated; - EPI.ExceptionSpec.SourceDecl = MD; - MD->setType(Context.getFunctionType(ReturnType, - llvm::makeArrayRef(&ArgType, - ExpectedParams), - EPI)); - } - - if (ShouldDeleteSpecialMember(MD, CSM)) { + // FIXME(EricWF) + EPI.ExceptionSpec.Type = + CSM == CXXComparisonOperator ? EST_None : EST_Unevaluated; + EPI.ExceptionSpec.SourceDecl = FD; + FD->setType(Context.getFunctionType(ReturnType, Args, EPI)); + } + + bool ShouldDelete = + CSM == CXXComparisonOperator + ? CompareInfo.ShouldDelete + : ShouldDeleteSpecialMember(cast(FD), CSM); + if (ShouldDelete) { if (First) { - SetDeclDeleted(MD, MD->getLocation()); + SetDeclDeleted(FD, FD->getLocation()); } else { + assert(CSM != CXXComparisonOperator); // FIXME(EricWF) + assert(isa(FD)); + CXXMethodDecl *MD = cast(FD); // C++11 [dcl.fct.def.default]p4: // [For a] user-provided explicitly-defaulted function [...] if such a // function is implicitly defined as deleted, the program is ill-formed. - Diag(MD->getLocation(), diag::err_out_of_line_default_deletes) << CSM; + Diag(FD->getLocation(), diag::err_out_of_line_default_deletes) << CSM; ShouldDeleteSpecialMember(MD, CSM, nullptr, /*Diagnose*/ true); HadError = true; } } if (HadError) - MD->setInvalidDecl(); + FD->setInvalidDecl(); } /// Check whether the exception specification provided for an @@ -6590,9 +6777,9 @@ // Ensure that it matches. CheckEquivalentExceptionSpec( PDiag(diag::err_incorrect_defaulted_exception_spec) - << getSpecialMember(MD), PDiag(), - ImplicitType, SourceLocation(), - SpecifiedType, MD->getLocation()); + << getSpecialMember(MD), + PDiag(), ImplicitType, SourceLocation(), SpecifiedType, + MD->getLocation()); } void Sema::CheckDelayedMemberExceptionSpecs() { @@ -6616,8 +6803,7 @@ namespace { /// CRTP base class for visiting operations performed by a special member /// function (or inherited constructor). -template -struct SpecialMemberVisitor { +template struct SpecialMemberVisitor { Sema &S; CXXMethodDecl *MD; Sema::CXXSpecialMember CSM; @@ -6641,6 +6827,8 @@ break; case Sema::CXXDestructor: break; + case Sema::CXXComparisonOperator: + llvm_unreachable("handled elsewhere"); case Sema::CXXInvalid: llvm_unreachable("invalid special member kind"); } @@ -6672,8 +6860,9 @@ if (!ICI) return {}; assert(CSM == Sema::CXXDefaultConstructor); - auto *BaseCtor = - cast(MD)->getInheritedConstructor().getConstructor(); + auto *BaseCtor = cast(MD) + ->getInheritedConstructor() + .getConstructor(); if (auto *MD = ICI->findConstructorForBase(Class, BaseCtor).first) return MD; return {}; @@ -6794,7 +6983,7 @@ bool SpecialMemberDeletionInfo::shouldDeleteForSubobjectCall( Subobject Subobj, Sema::SpecialMemberOverloadResult SMOR, bool IsDtorCallInCtor) { - CXXMethodDecl *Decl = SMOR.getMethod(); + CXXMethodDecl *Decl = cast_or_null(SMOR.getFunction()); FieldDecl *Field = Subobj.dyn_cast(); int DiagKind = -1; @@ -6822,8 +7011,8 @@ if (Field) { S.Diag(Field->getLocation(), diag::note_deleted_special_member_class_subobject) - << getEffectiveCSM() << MD->getParent() << /*IsField*/true - << Field << DiagKind << IsDtorCallInCtor; + << getEffectiveCSM() << MD->getParent() << /*IsField*/ true << Field + << DiagKind << IsDtorCallInCtor; } else { CXXBaseSpecifier *Base = Subobj.get(); S.Diag(Base->getLocStart(), @@ -6861,8 +7050,8 @@ // C++11 [class.dtor]p5: // -- any direct or virtual base class [...] has a type with a destructor // that is deleted or inaccessible - if (!(CSM == Sema::CXXDefaultConstructor && - Field && Field->hasInClassInitializer()) && + if (!(CSM == Sema::CXXDefaultConstructor && Field && + Field->hasInClassInitializer()) && shouldDeleteForSubobjectCall(Subobj, lookupIn(Class, Quals, IsMutable), false)) return true; @@ -6871,9 +7060,8 @@ // -- any direct or virtual base class or non-static data member has a // type with a destructor that is deleted or inaccessible if (IsConstructor) { - Sema::SpecialMemberOverloadResult SMOR = - S.LookupSpecialMember(Class, Sema::CXXDestructor, - false, false, false, false, false); + Sema::SpecialMemberOverloadResult SMOR = S.LookupSpecialMember( + Class, Sema::CXXDestructor, false, false, false, false, false); if (shouldDeleteForSubobjectCall(Subobj, SMOR, true)) return true; } @@ -6892,7 +7080,7 @@ // If we have an inheriting constructor, check whether we're calling an // inherited constructor instead of a default constructor. Sema::SpecialMemberOverloadResult SMOR = lookupInheritedCtor(BaseClass); - if (auto *BaseCtor = SMOR.getMethod()) { + if (auto *BaseCtor = SMOR.getFunction()) { // Note that we do not check access along this path; other than that, // this is the same as shouldDeleteForSubobjectCall(Base, BaseCtor, false); // FIXME: Check that the base has a usable destructor! Sink this into @@ -6953,7 +7141,8 @@ if (FieldType->isReferenceType()) { if (Diagnose) S.Diag(FD->getLocation(), diag::note_deleted_assign_field) - << isMove() << MD->getParent() << FD << FieldType << /*Reference*/0; + << isMove() << MD->getParent() << FD << FieldType + << /*Reference*/ 0; return true; } if (!FieldRecord && FieldType.isConstQualified()) { @@ -6961,7 +7150,8 @@ // -- a non-static data member of const non-class type (or array thereof) if (Diagnose) S.Diag(FD->getLocation(), diag::note_deleted_assign_field) - << isMove() << MD->getParent() << FD << FD->getType() << /*Const*/1; + << isMove() << MD->getParent() << FD << FD->getType() + << /*Const*/ 1; return true; } } @@ -7177,7 +7367,8 @@ switch (CSM) { case Sema::CXXInvalid: llvm_unreachable("not a special member"); - + case Sema::CXXComparisonOperator: + llvm_unreachable("handled elsewhere"); case Sema::CXXDefaultConstructor: // C++11 [class.ctor]p5: // A default constructor is trivial if: @@ -7273,7 +7464,7 @@ if (SMOR.getKind() == Sema::SpecialMemberOverloadResult::Ambiguous) return true; - if (!SMOR.getMethod()) { + if (!SMOR.getFunction()) { assert(SMOR.getKind() == Sema::SpecialMemberOverloadResult::NoMemberOrDeleted); return false; @@ -7281,13 +7472,14 @@ // We deliberately don't check if we found a deleted special member. We're // not supposed to! + CXXMethodDecl *MD = cast(SMOR.getFunction()); if (Selected) - *Selected = SMOR.getMethod(); + *Selected = MD; if (TAH == Sema::TAH_ConsiderTrivialABI && (CSM == Sema::CXXCopyConstructor || CSM == Sema::CXXMoveConstructor)) - return SMOR.getMethod()->isTrivialForCall(); - return SMOR.getMethod()->isTrivial(); + return MD->isTrivialForCall(); + return MD->isTrivial(); } llvm_unreachable("unknown special method kind"); @@ -7435,9 +7627,15 @@ /// Determine whether a defaulted or deleted special member function is trivial, /// as specified in C++11 [class.ctor]p5, C++11 [class.copy]p12, /// C++11 [class.copy]p25, and C++11 [class.dtor]p5. -bool Sema::SpecialMemberIsTrivial(CXXMethodDecl *MD, CXXSpecialMember CSM, +bool Sema::SpecialMemberIsTrivial(FunctionDecl *FD, CXXSpecialMember CSM, TrivialABIHandling TAH, bool Diagnose) { - assert(!MD->isUserProvided() && CSM != CXXInvalid && "not special enough"); + assert(CSM != CXXInvalid && "not special enough"); + + if (CSM == CXXComparisonOperator) + return false; + + auto *MD = cast(FD); + assert(MD && !MD->isUserProvided() && "not special enough"); CXXRecordDecl *RD = MD->getParent(); @@ -7484,7 +7682,8 @@ } break; } - + case Sema::CXXComparisonOperator: + llvm_unreachable("handled elsewhere"); case CXXInvalid: llvm_unreachable("not a special member"); } @@ -10543,7 +10742,7 @@ auto *BaseClass = cast(RT->getDecl()); Sema::SpecialMemberOverloadResult SMOR = lookupInheritedCtor(BaseClass); - if (auto *BaseCtor = SMOR.getMethod()) { + if (auto *BaseCtor = cast_or_null(SMOR.getFunction())) { visitSubobjectCall(Base, BaseCtor); return false; } @@ -10584,7 +10783,7 @@ Subobject Subobj, Sema::SpecialMemberOverloadResult SMOR) { // Note, if lookup fails, it doesn't matter what exception specification we // choose because the special member will be deleted. - if (CXXMethodDecl *MD = SMOR.getMethod()) + if (CXXMethodDecl *MD = cast_or_null(SMOR.getFunction())) ExceptSpec.CalledDecl(getSubobjectLoc(Subobj), MD); } @@ -11168,6 +11367,29 @@ } }; +class RefOrThisBuilder : public ExprBuilder { + VarDecl *Var = nullptr; + QualType VarType; + +public: + Expr *build(Sema &S, SourceLocation Loc) const override { + if (Var) + return assertNotNull( + S.BuildDeclRefExpr(Var, VarType, VK_LValue, Loc).get()); + else { + Expr *E = assertNotNull(S.ActOnCXXThis(Loc).getAs()); + return assertNotNull(S.CreateBuiltinUnaryOp(Loc, UO_Deref, E).get()); + } + } + + void makeRefBuilder(VarDecl *xVar, QualType VarTy) { + Var = xVar; + VarType = VarTy; + } + + bool isThisBuilder() const { return Var == nullptr; } +}; + class CastBuilder: public ExprBuilder { const ExprBuilder &Builder; QualType Type; @@ -11993,8 +12215,8 @@ /*ConstArg*/false, /*VolatileArg*/false, /*RValueThis*/true, /*ConstThis*/false, /*VolatileThis*/false); - if (!SMOR.getMethod() || SMOR.getMethod()->isTrivial() || - !SMOR.getMethod()->isMoveAssignmentOperator()) + CXXMethodDecl *MD = cast_or_null(SMOR.getFunction()); + if (!MD || MD->isTrivial() || !MD->isMoveAssignmentOperator()) continue; if (BaseSpec->isVirtual()) { @@ -12025,7 +12247,7 @@ // Only walk over bases that have defaulted move assignment operators. // We assume that any user-provided move assignment operator handles // the multiple-moves-of-vbase case itself somehow. - if (!SMOR.getMethod()->isDefaulted()) + if (!SMOR.getFunction()->isDefaulted()) continue; // We're going to move the base classes of Base. Add them to the list. @@ -12622,6 +12844,303 @@ } } +namespace { +class ComparisonBuilder { + Sema &S; + FunctionDecl *CompareOperator; + SmallVector Statements; + BinaryOperatorKind Opc; + + Expr *LastCmpResult = nullptr; + +public: + SourceLocation Loc; + + ComparisonBuilder(Sema &S, FunctionDecl *CompareOperator) + : S(S), CompareOperator(CompareOperator) { + // Our location for everything implicitly-generated. + Loc = CompareOperator->getLocEnd().isValid() + ? CompareOperator->getLocEnd() + : CompareOperator->getLocation(); + Opc = BinaryOperator::getOverloadedOpcode( + CompareOperator->getOverloadedOperator()); + } + + bool BuildCompare(const ExprBuilder &LHSB, const ExprBuilder &RHSB) { + Expr *LHS = LHSB.build(S, Loc); + Expr *RHS = RHSB.build(S, Loc); + ExprResult Cmp = S.BuildBinOp(nullptr, Loc, BO_Cmp, LHS, RHS); + if (Cmp.isInvalid()) + return Error(); + + Expr *LastCmp = Cmp.get(); + std::swap(LastCmp, LastCmpResult); + + if (LastCmp && BuildDelayedComparison(LastCmp)) + return Error(); + + return false; + } + + bool Finalize() { + Expr *Cmp = LastCmpResult; + if (!Cmp) { + ExprResult EqualExpr = BuildEqualResult(); + if (EqualExpr.isInvalid()) + return Error(); + Cmp = EqualExpr.get(); + } + + StmtResult Return = S.BuildReturnStmt(Loc, Cmp); + if (Return.isInvalid()) + return Error(); + + Statements.push_back(Return.getAs()); + + StmtResult Body; + { + Sema::CompoundScopeRAII CompoundScope(S); + Body = S.ActOnCompoundStmt(Loc, Loc, Statements, + /*isStmtExpr=*/false); + assert(!Body.isInvalid() && "Compound statement creation cannot fail"); + } + CompareOperator->setBody(Body.getAs()); + CompareOperator->markUsed(S.Context); + return false; + } + +private: + bool Error() { + CompareOperator->setInvalidDecl(); + return true; + } + + bool BuildDelayedComparison(Expr *Cmp, bool IsFinalCmp = false) { + StmtResult BranchOnCmp = BuildCompareEqualOrReturn(cast(Cmp)); + if (BranchOnCmp.isInvalid()) + return true; + + // Success! + Stmt *Result = BranchOnCmp.getAs(); + Statements.push_back(Result); + return false; + } + + StmtResult BuildCompareEqualOrReturn(Expr *LastResult) { + assert(LastResult && "should not be null"); + + VarDecl *VD = BuildInventedVarDecl(LastResult); + if (!VD) + return StmtError(); + Expr *DRE = BuildDeclRef(VD); + + // Now attempt to build the full expression '(LHS <=> RHS) @ 0' using the + // evaluated operand and the literal 0. + ExprResult CmpRes = + S.BuildBinOp(nullptr, Loc, BO_NE, DRE, BuildZeroLiteral()); + if (CmpRes.isInvalid()) + return StmtError(); + + StmtResult VDDeclStmt = + S.ActOnDeclStmt(S.ConvertDeclToDeclGroup(VD), Loc, Loc); + if (VDDeclStmt.isInvalid()) + return false; + + // If the last comparison returned not equal, we'll return it's value. + StmtResult Return = S.BuildReturnStmt(Loc, DRE); + if (Return.isInvalid()) + return StmtError(); + + if (cast(Return.get())->getNRVOCandidate() == VD) + VD->setNRVOVariable(true); + + return S.ActOnIfStmt(Loc, false, VDDeclStmt.getAs(), + S.ActOnCondition(nullptr, Loc, CmpRes.get(), + Sema::ConditionKind::Boolean), + Return.get(), SourceLocation(), nullptr); + } + + ExprResult BuildEqualResult() { + if (Opc != BO_Cmp) { + bool Val = Opc != BO_NE && Opc != BO_LT && Opc != BO_GT; + return new (S.Context) CXXBoolLiteralExpr(Val, S.Context.BoolTy, Loc); + } + + // FIXME: we need to emit diagnostics here + QualType RetTy = S.CheckComparisonCategoryType( + ComparisonCategoryType::StrongOrdering, Loc); + if (RetTy.isNull()) + return ExprError(); + + const VarDecl *VDC = + S.Context.CompCategories.getInfoForType(RetTy).lookupResultDecl( + ComparisonCategoryResult::Equal); + VarDecl *VD = const_cast(VDC); + return S.BuildDeclRefExpr(VD, RetTy, VK_LValue, Loc); + } + + VarDecl *BuildInventedVarDecl(Expr *Init) { + // Build a dummy variable to hold the result of our last comparison. + auto *VD = VarDecl::Create( + S.Context, CompareOperator, Loc, Loc, + &S.PP.getIdentifierTable().get("__cmp_res"), Init->getType(), + S.Context.getTrivialTypeSourceInfo(Init->getType(), Loc), SC_Auto); + S.CheckVariableDeclarationType(VD); + if (VD->isInvalidDecl()) + return nullptr; + + // Initialize the dummy variable using the last comparison expression as + // the initializer. + InitializedEntity Entity = InitializedEntity::InitializeVariable(VD); + InitializationKind Kind = InitializationKind::CreateForInit( + VD->getLocation(), /*DirectInit=*/true, Init); + InitializationSequence InitSeq(S, Entity, Kind, Init, + /*TopLevelOfInitList=*/false, + /*TreatUnavailableAsInvalid=*/false); + if (!InitSeq) + return nullptr; + ExprResult InitResult = InitSeq.Perform(S, Entity, Kind, Init); + if (InitResult.isInvalid()) + return nullptr; + InitResult = S.ActOnFinishFullExpr(InitResult.get()); + if (InitResult.isInvalid()) + return nullptr; + + S.AddInitializerToDecl(VD, InitResult.get(), /*DirectInit=*/false); + + S.CheckCompleteVariableDeclaration(VD); + return VD; + } + + Expr *BuildDeclRef(VarDecl *VD) { + ExprResult DRE = + S.BuildDeclRefExpr(VD, VD->getType(), ExprValueKind::VK_LValue, Loc); + assert(!DRE.isInvalid()); + return DRE.get(); + } + + Expr *BuildZeroLiteral() { + llvm::APInt I = + llvm::APInt::getNullValue(S.Context.getIntWidth(S.Context.IntTy)); + return IntegerLiteral::Create(S.Context, I, S.Context.IntTy, + SourceLocation()); + } +}; +} + +void Sema::DefineDefaultedComparisonOperator(SourceLocation CurrentLocation, + FunctionDecl *CompareOperator) { + assert(CompareOperator->isDefaulted() && + !CompareOperator->doesThisDeclarationHaveABody()); + assert(!CompareOperator->isDeleted()); + assert(CompareOperator->isDefaultComparisonOperator()); + + if (CompareOperator->isInvalidDecl()) + return; + + CompareOperator->setImplicitlyInline(); + + const bool IsMethod = isa(CompareOperator); + + CXXRecordDecl *ClassDecl = + cast(CompareOperator->getLexicalParent()); + SynthesizedFunctionScope Scope(*this, CompareOperator); + // FIXME(EricWF): What does the spec say about implicit exception specs for + // defaulted comparison operators? + + // Add a context note for diagnostics produced after this point. + Scope.addContextNote(CurrentLocation); + + auto CheckParam = [&](ParmVarDecl *PD) { + QualType RefTy = + PD->getType()->getAs()->getPointeeType(); + auto Quals = RefTy.getQualifiers(); + Quals.removeConst(); + assert(!Quals && "Bad argument type for defaulted comparison"); + return RefTy; + }; + + RefOrThisBuilder LHSRef; + if (!IsMethod) { + ParmVarDecl *VD = CompareOperator->getParamDecl(0); + LHSRef.makeRefBuilder(VD, CheckParam(VD)); + } + + // The parameter for the "other" object. + ParmVarDecl *RHSPD = CompareOperator->getParamDecl(IsMethod ? 0 : 1); + QualType RHSRefType = CheckParam(RHSPD); + // Builds a reference to the "other" object. + RefBuilder RHSRef(RHSPD, RHSRefType); + + auto CallQuals = + CompareOperator->getType()->getAs()->getTypeQuals(); + + ComparisonBuilder Builder(*this, CompareOperator); + + // compare base classes + bool Invalid = false; + for (auto &Base : ClassDecl->bases()) { + // C++2a [class.spaceship]p1: + // It is unspecified whether virtual base class subobjects are compared + // more than once. + + QualType BaseType = Base.getType(); + if (!BaseType->isRecordType()) { + Invalid = true; + continue; + } + + CXXCastPath BasePath; + BasePath.push_back(&Base); + + // Implicitly cast "this" to the appropriately-qualified base type. + CastBuilder LHS(LHSRef, Context.getCVRQualifiedType(BaseType, CallQuals), + VK_LValue, BasePath); + + // Construct the "from" expression, which is an implicit cast to the + // appropriately-qualified base type. + CastBuilder RHS(RHSRef, BaseType, VK_LValue, BasePath); + + if (Builder.BuildCompare(LHS, RHS)) + return; + } + + // Assign non-static members. + for (auto *Field : ClassDecl->fields()) { + if (Field->isInvalidDecl()) { + Invalid = true; + continue; + } + + // Suppress assigning zero-width bitfields. + if (Field->isZeroLengthBitField(Context)) + continue; + + // Build references to the field in the object we're copying from and to. + LookupResult MemberLookup(*this, Field->getDeclName(), Builder.Loc, + LookupMemberName); + MemberLookup.addDecl(Field); + MemberLookup.resolveKind(); + MemberBuilder RHS(RHSRef, RHSRefType, /*IsArrow=*/false, MemberLookup); + MemberBuilder LHS(LHSRef, RHSRefType, /*IsArrow=*/false, MemberLookup); + + if (Builder.BuildCompare(LHS, RHS)) + return; + } + + if (Invalid) { + CompareOperator->setInvalidDecl(); + return; + } + + if (Builder.Finalize()) + return; + + if (ASTMutationListener *L = getASTMutationListener()) { + L->CompletedImplicitDefinition(CompareOperator); + } +} + /// \brief Determine whether the given list arguments contains exactly one /// "real" (non-default) argument. static bool hasOneRealArgument(MultiExprArg Args) { @@ -14270,7 +14789,7 @@ FD = FTD->getTemplatedDecl(); else FD = cast(ND); - + assert(!FD->isExplicitlyDefaulted()); // C++11 [dcl.fct.default]p4: If a friend declaration specifies a // default argument expression, that declaration shall be a definition // and shall be the only declaration of the function or function @@ -14367,17 +14886,77 @@ Fn->setDeletedAsWritten(); } +bool Sema::checkDefaultedComparisonOperatorDecl(SourceLocation DefaultLoc, + Decl *Dcl) { + if (!Dcl) + return false; + + // If we don't have a defaulted comparison decl, return. + auto *FD = Dcl->getAsFunction(); + if (!FD || !FD->isDefaultComparisonOperator(true)) + return false; + + // Require defaulted comparison decls to be the first declaration for a + // given function. + Decl *CanonDcl = Dcl->getCanonicalDecl(); + if (CanonDcl != Dcl) { + // FIXME(EricWF): IDK if this is strictly required by the standard, + // but I don't want to handle it now. + // TODO: Handle this better? + Diag(FD->getLocation(), diag::err_defaulted_decl_not_first); + Diag(CanonDcl->getLocation(), diag::note_previous_declaration) + << CanonDcl->getSourceRange(); + Dcl->setInvalidDecl(); + return true; + } + + // Dig up the record decl in which this function was declared. If it's a + // defaulted non-method comparison operator, the canonical decls lexical + // context will provide this. + CXXRecordDecl *RD; + if (auto *MD = dyn_cast(FD)) + RD = MD->getParent(); + else + RD = dyn_cast(FD->getLexicalParent()); + if (!RD) { + Diag(FD->getLocation(), diag::err_defaulted_comparison_operator_decl_scope); + Dcl->setInvalidDecl(); + return true; + } + + // defaulted comparison operators are not allowed to be template functions. + if (isa(Dcl)) { + Diag(FD->getLocStart(), diag::err_defaulted_comparison_operator_template); + Dcl->setInvalidDecl(); + return true; + } + + // non-valid binary operators get diagnosed elsewhere. For now just ensure + // we skip further checking. + unsigned ExpectedParams = isa(FD) ? 1 : 2; + if (ExpectedParams != FD->getNumParams()) + return true; + + FD->setImplicitlyInline(); + return false; +} + void Sema::SetDeclDefaulted(Decl *Dcl, SourceLocation DefaultLoc) { - CXXMethodDecl *MD = dyn_cast_or_null(Dcl); + // If this is the first time we've seen a friend decl for a defaulted + // comparison operator, we need to do some initial checking on the + // declaration. + if (checkDefaultedComparisonOperatorDecl(DefaultLoc, Dcl)) + return; - if (MD) { + if (auto *MD = dyn_cast_or_null(Dcl)) { if (MD->getParent()->isDependentType()) { MD->setDefaulted(); MD->setExplicitlyDefaulted(); return; } - CXXSpecialMember Member = getSpecialMember(MD); + CXXSpecialMember Member = + getSpecialMember(MD, /*IsExplicitlyDefaulted*/ true); if (Member == CXXInvalid) { if (!MD->isInvalidDecl()) Diag(DefaultLoc, diag::err_default_special_members); @@ -14390,7 +14969,7 @@ // Unset that we will have a body for this function. We might not, // if it turns out to be trivial, and we don't need this marking now // that we've marked it as defaulted. - MD->setWillHaveBody(false); + MD->setWillHaveBody(Member == CXXComparisonOperator); // If this definition appears within the record, do the checking when // the record is complete. @@ -14406,10 +14985,31 @@ if (Primary->getCanonicalDecl()->isDefaulted()) return; - CheckExplicitlyDefaultedSpecialMember(MD); + CheckExplicitlyDefaultedMember(MD); if (!MD->isInvalidDecl()) DefineImplicitSpecialMember(*this, MD, DefaultLoc); + } else if (auto *FD = dyn_cast_or_null(Dcl)) { + if (FD->isDefaultComparisonOperator(true)) { + CXXRecordDecl *Parent = dyn_cast(FD->getLexicalParent()); + assert(!isa(Dcl)); + assert(Parent); + if (Parent->isDependentType()) { + FD->setDefaulted(); + FD->setExplicitlyDefaulted(); + return; + } + FD->setDefaulted(); + FD->setExplicitlyDefaulted(); + FD->setWillHaveBody(true); + + CheckExplicitlyDefaultedMember(FD); + + if (!FD->isInvalidDecl()) + DefineImplicitSpecialMember(*this, FD, DefaultLoc); + } else { + Diag(DefaultLoc, diag::err_default_special_members); + } } else { Diag(DefaultLoc, diag::err_default_special_members); } Index: lib/Sema/SemaExceptionSpec.cpp =================================================================== --- lib/Sema/SemaExceptionSpec.cpp +++ lib/Sema/SemaExceptionSpec.cpp @@ -1210,6 +1210,8 @@ case Expr::VAArgExprClass: return canSubExprsThrow(*this, E); + case Expr::CXXRewrittenOperatorClass: + 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 @@ -103,7 +103,8 @@ // Try to diagnose why this special member function was implicitly // deleted. This might fail, if that reason no longer applies. CXXSpecialMember CSM = getSpecialMember(Method); - if (CSM != CXXInvalid) + // FIXME(EricWF): Handle CXXComparisonOperator + if (CSM != CXXInvalid && CSM != CXXComparisonOperator) ShouldDeleteSpecialMember(Method, CSM, nullptr, /*Diagnose=*/true); return; @@ -12329,7 +12330,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); @@ -14149,7 +14155,8 @@ static bool isImplicitlyDefinableConstexprFunction(FunctionDecl *Func) { CXXMethodDecl *MD = dyn_cast(Func); return Func->isConstexpr() && - (Func->isImplicitlyInstantiable() || (MD && !MD->isUserProvided())); + (Func->isImplicitlyInstantiable() || (MD && !MD->isUserProvided()) || + Func->isDefaultComparisonOperator()); } /// \brief Mark a function referenced, and check whether it is odr-used @@ -14239,13 +14246,16 @@ MarkVTableUsed(Loc, Destructor->getParent()); } else if (CXXMethodDecl *MethodDecl = dyn_cast(Func)) { if (MethodDecl->isOverloadedOperator() && - MethodDecl->getOverloadedOperator() == OO_Equal) { + (MethodDecl->getOverloadedOperator() == OO_Equal || + MethodDecl->isDefaultComparisonOperator())) { MethodDecl = cast(MethodDecl->getFirstDecl()); if (MethodDecl->isDefaulted() && !MethodDecl->isDeleted()) { if (MethodDecl->isCopyAssignmentOperator()) DefineImplicitCopyAssignment(Loc, MethodDecl); else if (MethodDecl->isMoveAssignmentOperator()) DefineImplicitMoveAssignment(Loc, MethodDecl); + else if (MethodDecl->isDefaultComparisonOperator()) + DefineDefaultedComparisonOperator(Loc, MethodDecl); } } else if (isa(MethodDecl) && MethodDecl->getParent()->isLambda()) { @@ -14257,6 +14267,9 @@ DefineImplicitLambdaToFunctionPointerConversion(Loc, Conversion); } else if (MethodDecl->isVirtual() && getLangOpts().AppleKext) MarkVTableUsed(Loc, MethodDecl->getParent()); + } else if (Func->isDefaultComparisonOperator()) { + if (Func->isDefaulted() && !Func->isDeleted()) + DefineDefaultedComparisonOperator(Loc, Func); } // Recursive functions should be marked when used from another function. Index: lib/Sema/SemaLookup.cpp =================================================================== --- lib/Sema/SemaLookup.cpp +++ lib/Sema/SemaLookup.cpp @@ -2857,6 +2857,8 @@ bool RValueThis, bool ConstThis, bool VolatileThis) { + assert(SM != CXXComparisonOperator && + "lookups for comparisons should be handled elsewhere"); assert(CanDeclareSpecialMemberFunction(RD) && "doing special member lookup into record that isn't fully complete"); RD = RD->getDefinition(); @@ -2896,7 +2898,7 @@ DeclareImplicitDestructor(RD); CXXDestructorDecl *DD = RD->getDestructor(); assert(DD && "record without a destructor"); - Result->setMethod(DD); + Result->setFunction(DD); Result->setKind(DD->isDeleted() ? SpecialMemberOverloadResult::NoMemberOrDeleted : SpecialMemberOverloadResult::Success); @@ -2981,7 +2983,7 @@ // destructor. assert(SM == CXXDefaultConstructor && "lookup for a constructor or assignment operator was empty"); - Result->setMethod(nullptr); + Result->setFunction(nullptr); Result->setKind(SpecialMemberOverloadResult::NoMemberOrDeleted); return *Result; } @@ -3028,22 +3030,22 @@ OverloadCandidateSet::iterator Best; switch (OCS.BestViableFunction(*this, LookupLoc, Best)) { case OR_Success: - Result->setMethod(cast(Best->Function)); + Result->setFunction(Best->Function); Result->setKind(SpecialMemberOverloadResult::Success); break; case OR_Deleted: - Result->setMethod(cast(Best->Function)); + Result->setFunction(Best->Function); Result->setKind(SpecialMemberOverloadResult::NoMemberOrDeleted); break; case OR_Ambiguous: - Result->setMethod(nullptr); + Result->setFunction(nullptr); Result->setKind(SpecialMemberOverloadResult::Ambiguous); break; case OR_No_Viable_Function: - Result->setMethod(nullptr); + Result->setFunction(nullptr); Result->setKind(SpecialMemberOverloadResult::NoMemberOrDeleted); break; } @@ -3057,7 +3059,7 @@ LookupSpecialMember(Class, CXXDefaultConstructor, false, false, false, false, false); - return cast_or_null(Result.getMethod()); + return cast_or_null(Result.getFunction()); } /// \brief Look up the copying constructor for the given class. @@ -3069,7 +3071,7 @@ LookupSpecialMember(Class, CXXCopyConstructor, Quals & Qualifiers::Const, Quals & Qualifiers::Volatile, false, false, false); - return cast_or_null(Result.getMethod()); + return cast_or_null(Result.getFunction()); } /// \brief Look up the moving constructor for the given class. @@ -3079,7 +3081,7 @@ LookupSpecialMember(Class, CXXMoveConstructor, Quals & Qualifiers::Const, Quals & Qualifiers::Volatile, false, false, false); - return cast_or_null(Result.getMethod()); + return cast_or_null(Result.getFunction()); } /// \brief Look up the constructors for the given class. @@ -3113,7 +3115,7 @@ ThisQuals & Qualifiers::Const, ThisQuals & Qualifiers::Volatile); - return Result.getMethod(); + return cast_or_null(Result.getFunction()); } /// \brief Look up the moving assignment operator for the given class. @@ -3129,7 +3131,7 @@ ThisQuals & Qualifiers::Const, ThisQuals & Qualifiers::Volatile); - return Result.getMethod(); + return cast_or_null(Result.getFunction()); } /// \brief Look for the destructor of the given class. @@ -3140,8 +3142,88 @@ /// \returns The destructor for this class. CXXDestructorDecl *Sema::LookupDestructor(CXXRecordDecl *Class) { return cast(LookupSpecialMember(Class, CXXDestructor, - false, false, false, - false, false).getMethod()); + false, false, false, false, + false) + .getFunction()); +} + +Sema::SpecialMemberOverloadResult +Sema::LookupThreeWayComparison(QualType ArgType, SourceLocation Loc) { + using ResultKind = SpecialMemberOverloadResultEntry::Kind; + llvm::FoldingSetNodeID ID; + // FIXME(EricWF): Should we remove the quals before canonicalizing the type? + ID.AddPointer(ArgType.getAsOpaquePtr()); + ID.AddInteger(CXXComparisonOperator); + ID.AddInteger(/*ConstArg*/ true); + ID.AddInteger(/*VolatileArg*/ false); + ID.AddInteger(/*RValueThis*/ false); + ID.AddInteger(/*ConstThis*/ true); + ID.AddInteger(/*VolatileThis*/ false); + + void *InsertPoint; + SpecialMemberOverloadResultEntry *Result = + SpecialMemberCache.FindNodeOrInsertPos(ID, InsertPoint); + + // This was already cached + if (Result) + return *Result; + + Result = BumpAlloc.Allocate(); + Result = new (Result) SpecialMemberOverloadResultEntry(ID); + SpecialMemberCache.InsertNode(Result, InsertPoint); + + DeclarationName Name = + Context.DeclarationNames.getCXXOperatorName(OO_Spaceship); + + ArgType.addConst(); + OpaqueValueExpr FakeArg(Loc, ArgType, VK_LValue); + Expr *Args[2] = {&FakeArg, &FakeArg}; + + // FIXME(EricWF): Do this earlier, and pass in the result. + UnresolvedSet<16> Functions; + LookupOverloadedOperatorName(OO_Spaceship, getCurScope(), ArgType, ArgType, + Functions); + + OverloadCandidateSet CandidateSet(Loc, OverloadCandidateSet::CSK_Operator); + // Add the candidates from the given function set. + AddFunctionCandidates(Functions, Args, CandidateSet); + + // Add operator candidates that are member functions. + AddMemberOperatorCandidates(OO_Spaceship, Loc, Args, CandidateSet); + + // FIXME(EricWF): Should we be performing ADL here? Seems likely. + AddArgumentDependentLookupCandidates(Name, Loc, Args, + /*ExplicitTemplateArgs*/ nullptr, + CandidateSet); + // Add builtin operator candidates. + AddBuiltinOperatorCandidates(OO_Spaceship, Loc, Args, CandidateSet); + + // FIXME(EricWF): Ignore rewritten candidates for now. + + OverloadCandidateSet::iterator Best; + switch (CandidateSet.BestViableFunction(*this, Loc, Best)) { + case OR_Success: { + OverloadCandidate &Ovl = *Best; + Result->setKind(ResultKind::Success); + if (Ovl.Function) + Result->setFunction(Ovl.Function); + else { + assert(Ovl.getNumParams() == 2 && + Context.hasSameType(Ovl.BuiltinParamTypes[0], + Ovl.BuiltinParamTypes[1])); + Result->setBuiltinArgType(Ovl.BuiltinParamTypes[0]); + } + break; + } + case OR_Ambiguous: + Result->setKind(ResultKind::Ambiguous); + break; + case OR_Deleted: + case OR_No_Viable_Function: + Result->setKind(ResultKind::NoMemberOrDeleted); + break; + } + return *Result; } /// LookupLiteralOperator - Determine which literal operator should be used for 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(); @@ -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); @@ -8860,6 +8882,67 @@ } } +/// 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 IsRelationalOrEquality = + BinaryOperator::isRelationalOp(Opc) || BinaryOperator::isEqualityOp(Opc); + 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 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); +} + /// \brief Add function candidates found via argument-dependent lookup /// to the set of overloading candidates. /// @@ -8867,13 +8950,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 @@ -9006,8 +9086,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; @@ -9023,9 +9103,8 @@ // viable function F2 if for all arguments i, ICSi(F1) is not a worse // conversion sequence than ICSi(F2), and then... for (unsigned ArgIdx = StartArg; ArgIdx < NumArgs; ++ArgIdx) { - switch (CompareImplicitConversionSequences(S, Loc, - Cand1.Conversions[ArgIdx], - Cand2.Conversions[ArgIdx])) { + switch (CompareImplicitConversionSequences( + S, Loc, Cand1.getConversion(ArgIdx), Cand2.getConversion(ArgIdx))) { case ImplicitConversionSequence::Better: // Cand1 has a better conversion sequence. HasBetterConversion = true; @@ -9131,6 +9210,32 @@ // 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; + for (unsigned I = 0; I < Ovl.getNumParams(); ++I) + Types.push_back(Ovl.getParamType(I).getCanonicalType()); + if (Ovl.getRewrittenKind() == ROC_Synthesized) + llvm::reverse(Types); + 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); @@ -9243,9 +9348,9 @@ /// function, \p Best points to the candidate function found. /// /// \returns The result of overload resolution. -OverloadingResult -OverloadCandidateSet::BestViableFunction(Sema &S, SourceLocation Loc, - iterator &Best) { +OverloadingResult OverloadCandidateSet::BestViableFunction( + Sema &S, SourceLocation Loc, iterator &Best, + SmallVectorImpl *EquivalentCands) { llvm::SmallVector Candidates; std::transform(begin(), end(), std::back_inserter(Candidates), [](OverloadCandidate &Cand) { return &Cand; }); @@ -9287,23 +9392,32 @@ if (Best == end()) return OR_No_Viable_Function; - llvm::SmallVector EquivalentCands; - + llvm::SmallVector EquivalentFunctionCands; + if (EquivalentCands) + EquivalentCands->push_back(&(*Best)); // Make sure that this function is better than every other viable // function. If not, we have an ambiguity. for (auto *Cand : Candidates) { if (Cand->Viable && Cand != Best && !isBetterOverloadCandidate(S, *Best, *Cand, Loc, Kind)) { + if (EquivalentCands) + EquivalentCands->push_back(Cand); if (S.isEquivalentInternalLinkageDeclaration(Best->Function, Cand->Function)) { - EquivalentCands.push_back(Cand->Function); + EquivalentFunctionCands.push_back(Cand->Function); continue; } + if (EquivalentCands) + continue; Best = end(); return OR_Ambiguous; } } + if (EquivalentCands && EquivalentCands->size() > 1) { + Best = end(); + return OR_Ambiguous; + } // Best is the best viable function. if (Best->Function && @@ -9311,9 +9425,9 @@ S.isFunctionConsideredUnavailable(Best->Function))) return OR_Deleted; - if (!EquivalentCands.empty()) + if (!EquivalentFunctionCands.empty()) S.diagnoseEquivalentInternalLinkageDeclarations(Loc, Best->Function, - EquivalentCands); + EquivalentFunctionCands); return OR_Success; } @@ -12226,6 +12340,313 @@ return CreateBuiltinUnaryOp(OpLoc, Opc, Input); } +static ExprResult buildBinaryOperatorCandidate(Sema &S, 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. + S.CheckMemberOperatorAccess(OpLoc, Args[0], Args[1], Ovl.FoundDecl); + + ExprResult Arg1 = + S.PerformCopyInitialization(InitializedEntity::InitializeParameter( + S.Context, FnDecl->getParamDecl(0)), + SourceLocation(), Args[1]); + if (Arg1.isInvalid()) + return ExprError(); + + ExprResult Arg0 = S.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 = + S.PerformCopyInitialization(InitializedEntity::InitializeParameter( + S.Context, FnDecl->getParamDecl(0)), + SourceLocation(), Args[0]); + if (Arg0.isInvalid()) + return ExprError(); + + ExprResult Arg1 = + S.PerformCopyInitialization(InitializedEntity::InitializeParameter( + S.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(S, 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(S.Context); + + CXXOperatorCallExpr *TheCall = new (S.Context) CXXOperatorCallExpr( + S.Context, Op, FnExpr.get(), Args, ResultTy, VK, OpLoc, S.FPFeatures); + + if (S.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) + S.DiagnoseSelfMove(Args[0], Args[1], OpLoc); + + S.checkCall(FnDecl, nullptr, ImplicitThis, ArgsArray, + isa(FnDecl), OpLoc, TheCall->getSourceRange(), + Sema::VariadicDoesNotApply); + + return S.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 = + S.PerformImplicitConversion(Args[0], Ovl.BuiltinParamTypes[0], + Ovl.Conversions[0], Sema::AA_Passing); + if (ArgsRes0.isInvalid()) + return ExprError(); + Args[0] = ArgsRes0.get(); + + ExprResult ArgsRes1 = + S.PerformImplicitConversion(Args[1], Ovl.BuiltinParamTypes[1], + Ovl.Conversions[1], Sema::AA_Passing); + if (ArgsRes1.isInvalid()) + return ExprError(); + Args[1] = ArgsRes1.get(); + } + // We matched a built-in operator; build it. + return S.CreateBuiltinBinOp(OpLoc, Opc, Args[0], Args[1]); +} + +namespace { + +/// \brief RewrittenOverloadResolver - This class handles initial +/// overload resolution for candidate sets which include rewritten candidates. +/// +/// Rewritten candidates haven't been fully checked for validity. They may still +/// be invalid if: +/// (A) The rewritten candidate is a builtin, but semantic checking of the +/// builtin would fail. +/// (B) The result of the "partially rewritten expression" +/// (ie the (LHS <=> RHS) part) is ill-formed when used as an operand to +/// ( @ 0) or (0 @ ). +/// +/// TODO: Separate out the bits of semantic checking for builtin spaceship +/// operators which determine validity and the return type, and use that instead +/// of building the full expression to check validity. +class RewrittenOverloadResolver { + enum CheckOverloadResult { Done, Continue }; + +public: + RewrittenOverloadResolver(Sema &S, SourceLocation OpLoc, + BinaryOperatorKind Opc, ArrayRef Args, + const UnresolvedSetImpl &Fns, bool PerformADL, + OverloadCandidateSet &CS) + : S(S), OpLoc(OpLoc), Opc(Opc), Args(Args), Fns(Fns), + PerformADL(PerformADL), CandidateSet(CS) {} + + ExprResult ResolveRewrittenCandidates() { + ExprResult FinalResult = ExprError(); + OverloadCandidateSet::iterator Best; + OverloadingResult OvlRes; + llvm::SmallVector EquivCands; + do { + EquivCands.clear(); + OvlRes = CandidateSet.BestViableFunction(S, OpLoc, Best, &EquivCands); + } while (Continue == RemoveNonViableRewrittenCandidates( + OvlRes, Best, EquivCands, FinalResult)); + return FinalResult; + } +private: + CheckOverloadResult RemoveNonViableRewrittenCandidates( + OverloadingResult OvlRes, OverloadCandidateSet::iterator Best, + ArrayRef EquivCands, ExprResult &FinalResult); + ExprResult BuildRewrittenCandidate(const OverloadCandidate &Ovl); + + RewrittenOverloadResolver(RewrittenOverloadResolver const &) = delete; + RewrittenOverloadResolver & + operator=(RewrittenOverloadResolver const &) = delete; + +private: + Sema &S; + SourceLocation OpLoc; + BinaryOperatorKind Opc; + ArrayRef Args; + const UnresolvedSetImpl &Fns; + bool PerformADL; + OverloadCandidateSet &CandidateSet; +}; +} // end namespace + +ExprResult RewrittenOverloadResolver::BuildRewrittenCandidate( + const OverloadCandidate &Ovl) { + Expr *RewrittenArgs[2] = {Args[0], Args[1]}; + assert(Ovl.getRewrittenKind()); + bool IsSynthesized = Ovl.getRewrittenKind() == ROC_Synthesized; + if (IsSynthesized) + std::swap(RewrittenArgs[0], RewrittenArgs[1]); + + ExprResult RewrittenRes = ExprError(); + + // 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. + RewrittenRes = buildBinaryOperatorCandidate( + S, OpLoc, BO_Cmp, Ovl, RewrittenArgs[0], RewrittenArgs[1], + /*HadMultipleCandidates*/ false); + if (RewrittenRes.isInvalid()) + return ExprError(); + + // 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); + + ExprResult FinalRes = + S.CreateOverloadedBinOp(OpLoc, Opc, Fns, NewLHS, NewRHS, PerformADL, + /*AllowRewrittenCandidates*/ false); + if (FinalRes.isInvalid()) + return ExprError(); + return new (S.Context) CXXRewrittenOperator( + (CXXRewrittenOperator::RewrittenOperatorKind)Ovl.getRewrittenKind(), + RewrittenRes.get(), FinalRes.get()); +} + +/// Rewritten candidates have been added but not checked for validity. They +/// could still be non-viable if: +/// (A) The rewritten call (x <=> y) is a builtin, but it will be ill-formed +/// when built (for example it has narrowing conversions). +/// (B) The expression (x <=> y) @ 0 is ill-formed for the result of (x <=> y). +/// +/// If either is the case, this function should be considered non-viable and +/// another best viable function needs to be computed. +/// +/// Therefore, we do the following: +/// (1) If we have no viable candidate, or a deleted candidate, stop. +/// Otherwise, if we have a best viable candidate or a set of ambiguous +/// candidates and none of them are rewritten, stop. +/// +/// (2) If the best viable candidate is a rewritten candidate, build and +/// check the full expression for that candidate. If it succeeds return +/// the result. Otherwise, mark the candidate as non-viable, re-compute +/// the best viable function, and continue. +/// +/// (3) If we have ambiguity attempt to resolve it by evaluating each rewritten +/// candidate causing ambiguity: +/// +/// (3.1) build the full expression for the specified candidate. +/// (3.2) If the result is invalid, mark the candidate as non-viable. +/// +/// If only one viable candidate remains, stop. If the viable candidate is +/// rewritten, return the previously computed full expression. Otherwise, +/// if we have more than one viable candidate, stop. If no viable candidates +/// remain from the initial set of equally ranked candidates, recompute the +/// new best viable overload and continue. +RewrittenOverloadResolver::CheckOverloadResult +RewrittenOverloadResolver::RemoveNonViableRewrittenCandidates( + OverloadingResult OvlRes, OverloadCandidateSet::iterator Best, + ArrayRef EquivCands, ExprResult &FinalResult) { + auto Success = [&](ExprResult Res) { + FinalResult = Res; + return Done; + }; + switch (OvlRes) { + case OR_Deleted: + // FIXME(EricWF): If we've found a deleted rewritten operator, it's + // possible we should have never considered it a viable candidate. + case OR_No_Viable_Function: + return Done; + + case OR_Success: { + OverloadCandidate &Ovl = *Best; + if (!Ovl.getRewrittenKind()) + return Done; + // Build the full expression for the rewritten candidate, and return it if + // it's valid. Otherwise mark this candidate as non-viable and continue. + ExprResult Res = BuildRewrittenCandidate(Ovl); + if (Res.isInvalid()) { + Ovl.Viable = false; + return Continue; + } + return Success(Res); + } + case OR_Ambiguous: { + assert(EquivCands.size() > 1); + // If none of the viable candidates are rewritten, stop. + if (llvm::none_of(EquivCands, [](const OverloadCandidate *Cand) { + return (bool)Cand->getRewrittenKind(); + })) + return Done; + int NumViableCandidates = 0; + ExprResult ViableRewritten = ExprError(); + for (auto *Cand : EquivCands) { + OverloadCandidate &Ovl = *Cand; + if (Ovl.getRewrittenKind()) { + ExprResult Res = BuildRewrittenCandidate(Ovl); + if (Res.isInvalid()) { + Ovl.Viable = false; + continue; + } + ViableRewritten = Res; + } + ++NumViableCandidates; + } + // If only one of the candidates turns out to be viable, and it's a + // rewritten candidate, return that candidate as the result. + if (NumViableCandidates == 1 && !ViableRewritten.isInvalid()) + return Success(ViableRewritten); + // If we still have ambiguity, return and let the caller diagnose it. + if (NumViableCandidates > 1) + return Done; + // All of the equally ranked candidates are invalid. Loop and try overload + // resolution again. + return Continue; + } + } + llvm_unreachable("unhandled case"); +} + /// \brief Create a binary operation that may resolve to an overloaded /// operator. /// @@ -12242,11 +12663,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 @@ -12308,11 +12729,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); @@ -12328,119 +12765,43 @@ // 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 (HasRewrittenCandidates) { + RewrittenOverloadResolver RewrittenOvlResolver(*this, OpLoc, Opc, Args, Fns, + PerformADL, CandidateSet); - if (CheckCallReturnType(FnDecl->getReturnType(), OpLoc, TheCall, - FnDecl)) - return ExprError(); + // Perform initial overload resolution that includes partially checked + // rewritten candidates, removing rewritten candidates which turn out to be + // invalid as needed. + ExprResult RewrittenResult = + RewrittenOvlResolver.ResolveRewrittenCandidates(); - 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; - } + // If overload resolution was successful and the result was a re-written + // overload candidate, then that candidate was evaluated and we can return + // the result directly. + if (!RewrittenResult.isInvalid()) + return RewrittenResult; } + // Perform final overload resolution. + bool HadMultipleCandidates = (CandidateSet.size() > 1); + OverloadCandidateSet::iterator Best; + switch (CandidateSet.BestViableFunction(*this, OpLoc, Best)) { + case OR_Success: + assert( + !Best->getRewrittenKind() && + "rewritten candidates should have already been resolved and evaluated"); + return buildBinaryOperatorCandidate(*this, 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 @@ -12453,15 +12814,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 @@ -12482,12 +12843,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(); @@ -12505,8 +12865,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/SemaTemplateInstantiateDecl.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiateDecl.cpp +++ lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1853,7 +1853,11 @@ PrincipalDecl->isInIdentifierNamespace(Decl::IDNS_Ordinary)) PrincipalDecl->setNonMemberOperator(); - assert(!D->isDefaulted() && "only methods should be defaulted"); + if (D->isExplicitlyDefaulted()) { + assert(D->isDefaultComparisonOperator() && + "Only default comparison operators may be defaulted"); + SemaRef.SetDeclDefaulted(Function, Function->getLocation()); + } return Function; } Index: lib/Sema/TreeTransform.h =================================================================== --- lib/Sema/TreeTransform.h +++ lib/Sema/TreeTransform.h @@ -3217,6 +3217,13 @@ return getSema().BuildEmptyCXXFoldExpr(EllipsisLoc, Operator); } + ExprResult + RebuildCXXRewrittenOperator(CXXRewrittenOperator::RewrittenOperatorKind Kind, + Expr *Underlying, Expr *Rewritten) { + return new (SemaRef.Context) + CXXRewrittenOperator(Kind, Underlying, Rewritten); + } + /// \brief Build a new atomic operation expression. /// /// By default, performs semantic analysis to build the new expression. @@ -11524,6 +11531,28 @@ template ExprResult +TreeTransform::TransformCXXRewrittenOperator(CXXRewrittenOperator *E) { + ExprResult Orig = getDerived().TransformExpr(E->getUnderlyingExpr()); + if (Orig.isInvalid()) + return ExprError(); + + // 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 Rewritten = getDerived().TransformExpr(E->getRewrittenExpr()); + if (Rewritten.isInvalid()) + return ExprError(); + + if (getDerived().AlwaysRebuild() || Orig.get() != E->getUnderlyingExpr() || + Rewritten.get() != E->getRewrittenExpr()) { + return getDerived().RebuildCXXRewrittenOperator(E->getKind(), Orig.get(), + Rewritten.get()); + } + return E; +} + +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::VisitCXXRewrittenOperator(CXXRewrittenOperator *E) { + VisitExpr(E); + E->Kind = (CXXRewrittenOperator::RewrittenOperatorKind)Record.readInt(); + E->SubExprs[0] = Record.readSubExpr(); + E->SubExprs[1] = 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) CXXRewrittenOperator(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,14 @@ Code = serialization::EXPR_CXX_FOLD; } +void ASTStmtWriter::VisitCXXRewrittenOperator(CXXRewrittenOperator *E) { + VisitExpr(E); + Record.push_back(E->Kind); + Record.AddStmt(E->SubExprs[0]); + Record.AddStmt(E->SubExprs[1]); + 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 @@ -1285,6 +1285,7 @@ case Stmt::PackExpansionExprClass: case Stmt::SubstNonTypeTemplateParmPackExprClass: case Stmt::FunctionParmPackExprClass: + case Stmt::CXXRewrittenOperatorClass: 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 @@ -146,3 +146,47 @@ // CHECK: icmp ult i64 {{.*}} %[[UL]] (void)(B <=> ul); } + +namespace RewrittenTest { +struct U { + int x; + std::strong_ordering operator<=>(U const &) const; +}; +struct T : public U { + int x, y; + auto operator<=>(T const &) const = default; +}; + +// FIXME(EricWF): Write this test +auto test(T t1, T t2) { + return (t1 < t2); +} + +} // namespace RewrittenTest + +namespace DefaultTest { +struct T { + int x, y, z; + std::strong_ordering operator<=>(T const &) const; +}; +struct U { + float y; + friend std::partial_ordering operator<=>(U const &, U const &); +}; +struct TU : public T, public U { + friend auto operator<=>(TU const &, TU const &) = default; +}; + +// CHECK: FIXME(EricWF) Write this test +void test1(TU x, TU y) { + (void)(x <=> y); +} + +struct Empty { + auto operator<=>(Empty const &) const = default; +}; + +auto test_empty(Empty t1, Empty t2) { + return t1 <=> t2; +} +} // namespace DefaultTest Index: test/SemaCXX/compare-cxx2a.cpp =================================================================== --- test/SemaCXX/compare-cxx2a.cpp +++ test/SemaCXX/compare-cxx2a.cpp @@ -293,20 +293,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() { @@ -375,3 +376,267 @@ ASSERT_EXPR_TYPE(r4, std::partial_ordering); } + +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; +// expected-note@+2 {{candidate}} +struct T { + std::strong_ordering operator<=>(U const &RHS) const; +}; +// expected-note@+2 {{candidate}} +struct U { + std::strong_ordering operator<=>(T const &RHS) const; +}; + +void test() { + // expected-error@+1 {{use of overloaded operator '<' is ambiguous}} + (void)(T{} < U{}); +} + +} // namespace TestOvlMatchingIgnoresImplicitObject + +namespace IllFormedSpecialMemberTest { + +struct T1 { + // expected-error@+1 {{the parameter for this explicitly-defaulted comparison operator must have type 'const IllFormedSpecialMemberTest::T1 &', but parameter has type 'IllFormedSpecialMemberTest::T1'}} + void operator<=>(T1) const = default; + // expected-error@+1 {{explicitly-defaulted comparison operator must have type}} + void operator<=>(T1 &) const = default; + // expected-error@+1 {{explicitly-defaulted comparison operator must have type}} + void operator<=>(int) const = default; + // expected-error@+1 {{overloaded 'operator<=>' must be a binary operator (has 1 parameter)}} + void operator<=>() const = default; + // expected-error@+1 {{overloaded 'operator<=>' must be a binary operator (has 3 parameters)}} + void operator<=>(T1 const &, T1 const &) = default; + + // expected-error@+2 {{an explicitly-defaulted comparison operator cannot be a template}} + template + void operator<=>(U const &) = default; + + // FIXME(EricWF): This should delete itself. + void operator<=>(T1 const &) const = default; // OK +}; +// expected-error@+1 {{definition of explicitly defaulted comparison operator}} +void T1::operator<=>(T1 const &) const = default; + +struct T2 { + // expected-error@+1 {{overloaded 'operator<=>' must be a binary operator (has 1 parameter)}} + friend void operator<=>(T2 const &) = default; + // expected-error@+1 {{overloaded 'operator<=>' must be a binary operator (has 3 parameters)}} + friend void operator<=>(T2 const &, T2 const &, T2 const &) = default; + + // FIXME(EricWF): Improve this diagnostic to point to the parameter. + // expected-error@+2 {{explicitly-defaulted comparison operator must have type}} + // expected-error@+1 {{explicitly-defaulted comparison operator must have type}} + friend void operator<=>(T2, T2) = default; + // expected-error@+1 {{explicitly-defaulted comparison operator must have type}} + friend void operator<=>(T2 &, T2 const &) = default; + // expected-error@+1 {{explicitly-defaulted comparison operator must have type}} + friend void operator<=>(T2 const &, T2 const volatile &) = default; + + friend auto operator<=>(T2 const &, T2 const &) = default; //OK + + // expected-error@+2 {{an explicitly-defaulted comparison operator cannot be a template}} + template + friend void operator<=>(Tp const &, Tp const &) = default; +}; + +struct T3; +// expected-note@+1 {{previous declaration is here}} +auto operator<=>(T3 const &, T3 const &); +struct T3 { + // expected-error@+1 {{defaulted definition must be first declaration}} + friend auto operator<=>(T3 const &, T3 const &) = default; +}; + +struct T4 { + bool operator<(T4 const &) const = default; // OK + bool operator>(T4 const &) const = default; + bool operator==(T4 const &) const = default; + bool operator!=(T4 const &) const = default; + + friend bool operator<(T4 const &, T4 const &) = default; + friend bool operator!=(T4 const &, T4 const &) = default; + + // expected-error@+1 {{explicitly-defaulted comparison operator must return 'bool'}} + int operator<=(T4 const &) const = default; + // expected-error@+1 {{explicitly-defaulted comparison operator must return 'bool'}} + bool &operator>=(T4 const &) const = default; + // expected-error@+1 {{explicitly-defaulted comparison operator must return 'bool'}} + friend void operator<=(T4 const &, T4 const &) = default; +}; + +struct T5 { + bool operator<(T5 const &) const = default; + friend auto operator<=>(T5 const &, T5 const &) = default; +}; + +// expected-error@+1 {{definition of explicitly defaulted comparison operator}} +bool T5::operator<(T5 const &) const { + return false; +} + +} // namespace IllFormedSpecialMemberTest + +namespace SpecialMemberTest { + +struct T { + int x; + auto operator<=>(T const &) const = default; +}; + +struct U { + int x; + friend auto operator<=>(U const &, U const &) = default; +}; + +} // namespace SpecialMemberTest + +namespace ConstexprDefaultTest { +struct T { + int x, y, z; + constexpr std::strong_ordering operator<=>(T const &other) const = default; +}; + +constexpr T t1{0, 0, 0}; +constexpr T t2{1, 1, 1}; +static_assert(t1 < t2); +static_assert(t1 != t2); + +// expected-error@+1 {{static_assert failed due to requirement 't1 != (const ConstexprDefaultTest::T &)t1'}} +static_assert(t1 != (T const &)t1); + +} // namespace ConstexprDefaultTest + +namespace TestCommonCategoryType { +using SO = std::strong_ordering; +using WO = std::weak_ordering; +using PO = std::partial_ordering; +using SE = std::strong_equality; +using WE = std::weak_equality; + +template +struct Tag { using type = T; }; +template +struct CmpResult { + friend Tp operator<=>(CmpResult const &, CmpResult const &); +}; +template +struct CmpResult> { + friend T operator<=>(CmpResult const &, CmpResult const &); +}; +template <> +struct CmpResult> { + friend auto operator<=>(CmpResult const &, CmpResult const &) = delete; +}; + +template +struct Cmp : CmpResult... { + friend auto operator<=>(Cmp const &, Cmp const &) = default; +}; + +template +struct Cmp2 : CmpResult... { + auto operator<=>(Cmp2 const &) const = default; +}; + +void test() { + { + using C = Cmp<>; + C c1, c2; + ASSERT_EXPR_TYPE(c1 <=> c2, SO); + } + { + using C = Cmp; + C c1, c2; + (void)(c1 <=> c2); + ASSERT_EXPR_TYPE(c1 <=> c2, SO); + } + { + using C = Cmp2; + C c1, c2; + (void)(c1 <=> c2); + ASSERT_EXPR_TYPE(c1 <=> c2, SO); + } +} + +} // namespace TestCommonCategoryType + +namespace DefaultTest { +struct T { + int x; + std::strong_ordering operator<=>(T const &) const { return std::strong_ordering::equal; } +}; +struct U { + float y; + friend std::partial_ordering operator<=>(U const &, U const &) = default; +}; +struct TU : public T, public U { + friend auto operator<=>(TU const &, TU const &) = default; +}; + +void test1(T x, T y) { + (void)(x <=> y); +} +} // namespace DefaultTest