diff --git a/clang/Driver/RewriteObjC.cpp b/clang/Driver/RewriteObjC.cpp index 9d78c42d0253..65e8841ec793 100644 --- a/clang/Driver/RewriteObjC.cpp +++ b/clang/Driver/RewriteObjC.cpp @@ -1519,7 +1519,8 @@ CallExpr *RewriteObjC::SynthesizeCallToFunctionDecl( // Now, we cast the reference to a pointer to the objc_msgSend type. QualType pToFunc = Context->getPointerType(msgSendType); - ImplicitCastExpr *ICE = new ImplicitCastExpr(pToFunc, DRE); + ImplicitCastExpr *ICE = new ImplicitCastExpr(pToFunc, DRE, + /*isLvalue=*/false); const FunctionType *FT = msgSendType->getAsFunctionType(); diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index efab1a14e140..6fe205cd0848 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -834,14 +834,23 @@ public: /// (*f)(), float->double, short->int, etc. /// class ImplicitCastExpr : public CastExpr { + /// LvalueCast - Whether this cast produces an lvalue. + bool LvalueCast; + public: - ImplicitCastExpr(QualType ty, Expr *op) : - CastExpr(ImplicitCastExprClass, ty, op) {} + ImplicitCastExpr(QualType ty, Expr *op, bool Lvalue) : + CastExpr(ImplicitCastExprClass, ty, op), LvalueCast(Lvalue) {} virtual SourceRange getSourceRange() const { return getSubExpr()->getSourceRange(); } + /// isLvalueCast - Whether this cast produces an lvalue. + bool isLvalueCast() const { return LvalueCast; } + + /// setLvalueCast - Set whether this cast produces an lvalue. + void setLvalueCast(bool Lvalue) { LvalueCast = Lvalue; } + static bool classof(const Stmt *T) { return T->getStmtClass() == ImplicitCastExprClass; } diff --git a/clang/include/clang/AST/TypeOrdering.h b/clang/include/clang/AST/TypeOrdering.h index a21655b58054..a23ca75ff063 100644 --- a/clang/include/clang/AST/TypeOrdering.h +++ b/clang/include/clang/AST/TypeOrdering.h @@ -7,9 +7,9 @@ // //===----------------------------------------------------------------------===// // -// This file provides a function object that gives a total ordering -// on QualType values, so that they can be sorted, used in std::maps -// and std::sets, and so on. +// This file provides a function objects and specializations that +// allow QualType values to be sorted, used in std::maps, std::sets, +// llvm::DenseMaps, and llvm::DenseSets. // //===----------------------------------------------------------------------===// @@ -17,6 +17,7 @@ #define LLVM_CLANG_TYPE_ORDERING_H #include "clang/AST/Type.h" +#include "llvm/ADT/DenseMap.h" #include namespace clang { @@ -31,4 +32,30 @@ struct QualTypeOrdering : std::binary_function { } +namespace llvm { + template<> struct DenseMapInfo { + static inline clang::QualType getEmptyKey() { return clang::QualType(); } + + static inline clang::QualType getTombstoneKey() { + using clang::QualType; + return QualType::getFromOpaquePtr(reinterpret_cast(-1)); + } + + static unsigned getHashValue(clang::QualType Val) { + return (unsigned)Val.getAsOpaquePtr(); + } + + static bool isEqual(clang::QualType LHS, clang::QualType RHS) { + return LHS == RHS; + } + + static bool isPod() { + // QualType isn't *technically* a POD type. However, we can get + // away with calling it a POD type since its copy constructor, + // copy assignment operator, and destructor are all trivial. + return true; + } + }; +} + #endif diff --git a/clang/include/clang/Basic/DiagnosticKinds.def b/clang/include/clang/Basic/DiagnosticKinds.def index 8d821e4362b5..451e8de630b9 100644 --- a/clang/include/clang/Basic/DiagnosticKinds.def +++ b/clang/include/clang/Basic/DiagnosticKinds.def @@ -867,6 +867,8 @@ DIAG(err_ovl_ambiguous_call, ERROR, "call to '%0' is ambiguous; candidates are:") DIAG(err_ovl_candidate, NOTE, "candidate function") +DIAG(err_ovl_builtin_candidate, NOTE, + "built-in candidate function '%0'") DIAG(err_ovl_no_viable_function_in_init, ERROR, "no matching constructor for initialization of '%0'.") DIAG(err_ovl_no_viable_function_in_init_with_cands, ERROR, diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index fbb225613fd2..c54fc40ecb26 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -389,8 +389,28 @@ Expr::isLvalueResult Expr::isLvalue(ASTContext &Ctx) const { cast(this)->getOpcode() == UnaryOperator::Extension) return cast(this)->getSubExpr()->isLvalue(Ctx); // GNU. break; + case ImplicitCastExprClass: + return cast(this)->isLvalueCast()? LV_Valid + : LV_InvalidExpression; case ParenExprClass: // C99 6.5.1p5 return cast(this)->getSubExpr()->isLvalue(Ctx); + case BinaryOperatorClass: + case CompoundAssignOperatorClass: { + const BinaryOperator *BinOp = cast(this); + if (BinOp->isAssignmentOp()) { + if (Ctx.getLangOptions().CPlusPlus) + // C++ [expr.ass]p1: + // The result of an assignment operation [...] is an lvalue. + return LV_Valid; + else + // C99 6.5.16: + // An assignment expression [...] is not an lvalue. + return LV_InvalidExpression; + } else + return LV_InvalidExpression; + + break; + } case CallExprClass: { // C++ [expr.call]p10: // A function call is an lvalue if and only if the result type diff --git a/clang/lib/AST/StmtSerialization.cpp b/clang/lib/AST/StmtSerialization.cpp index f71b88b46f1d..667f597b8ab5 100644 --- a/clang/lib/AST/StmtSerialization.cpp +++ b/clang/lib/AST/StmtSerialization.cpp @@ -662,12 +662,14 @@ ImaginaryLiteral* ImaginaryLiteral::CreateImpl(Deserializer& D, ASTContext& C) { void ImplicitCastExpr::EmitImpl(Serializer& S) const { S.Emit(getType()); S.EmitOwnedPtr(getSubExpr()); + S.Emit(LvalueCast); } ImplicitCastExpr* ImplicitCastExpr::CreateImpl(Deserializer& D, ASTContext& C) { QualType t = QualType::ReadVal(D); Expr* Op = D.ReadOwnedPtr(C); - return new ImplicitCastExpr(t,Op); + bool isLvalue = D.ReadBool(); + return new ImplicitCastExpr(t,Op,isLvalue); } void IndirectGotoStmt::EmitImpl(Serializer& S) const { diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 9c8d0c8bcb41..b78336b6cd2d 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -124,7 +124,8 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer) /// ImpCastExprToType - If Expr is not of type 'Type', insert an implicit cast. /// If there is already an implicit cast, merge into the existing one. -void Sema::ImpCastExprToType(Expr *&Expr, QualType Ty) { + /// If isLvalue, the result of the cast is an lvalue. +void Sema::ImpCastExprToType(Expr *&Expr, QualType Ty, bool isLvalue) { QualType ExprTy = Context.getCanonicalType(Expr->getType()); QualType TypeTy = Context.getCanonicalType(Ty); @@ -143,10 +144,11 @@ void Sema::ImpCastExprToType(Expr *&Expr, QualType Ty) { } } - if (ImplicitCastExpr *ImpCast = dyn_cast(Expr)) + if (ImplicitCastExpr *ImpCast = dyn_cast(Expr)) { ImpCast->setType(Ty); - else - Expr = new ImplicitCastExpr(Ty, Expr); + ImpCast->setLvalueCast(isLvalue); + } else + Expr = new ImplicitCastExpr(Ty, Expr, isLvalue); } void Sema::DeleteExpr(ExprTy *E) { diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index 7cd507a0aa30..5b66694e045d 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -435,6 +435,12 @@ public: void AddConversionCandidate(CXXConversionDecl *Conversion, Expr *From, QualType ToType, OverloadCandidateSet& CandidateSet); + void AddBuiltinCandidate(QualType ResultTy, QualType *ParamTys, + Expr **Args, unsigned NumArgs, + OverloadCandidateSet& CandidateSet); + void AddBuiltinBinaryOperatorCandidates(OverloadedOperatorKind Op, + Expr **Args, + OverloadCandidateSet& CandidateSet); void AddOverloadCandidates(const OverloadedFunctionDecl *Ovl, Expr **Args, unsigned NumArgs, OverloadCandidateSet& CandidateSet, @@ -1052,16 +1058,13 @@ public: /// ImpCastExprToType - If Expr is not of type 'Type', insert an implicit /// cast. If there is already an implicit cast, merge into the existing one. - void ImpCastExprToType(Expr *&Expr, QualType Type); + /// If isLvalue, the result of the cast is an lvalue. + void ImpCastExprToType(Expr *&Expr, QualType Type, bool isLvalue = false); // UsualUnaryConversions - promotes integers (C99 6.3.1.1p2) and converts // functions and arrays to their respective pointers (C99 6.3.2.1). Expr *UsualUnaryConversions(Expr *&expr); - // UsualUnaryConversionType - Same as UsualUnaryConversions, but works - // on types instead of expressions - QualType UsualUnaryConversionType(QualType Ty); - // DefaultFunctionArrayConversion - converts functions and arrays // to their respective pointers (C99 6.3.2.1). void DefaultFunctionArrayConversion(Expr *&expr); @@ -1079,6 +1082,12 @@ public: QualType UsualArithmeticConversions(Expr *&lExpr, Expr *&rExpr, bool isCompAssign = false); + /// UsualArithmeticConversionsType - handles the various conversions + /// that are common to binary operators (C99 6.3.1.8, C++ [expr]p9) + /// and returns the result type of that conversion. + QualType UsualArithmeticConversionsType(QualType lhs, QualType rhs); + + /// AssignConvertType - All of the 'assignment' semantic checks return this /// enum to indicate whether the assignment was allowed. These checks are /// done for simple assignments, as well as initialization, return from diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index fc412a605edf..aaceb4f55911 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -3021,7 +3021,8 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, DeclTy *EnumDeclX, ECD->setInitVal(InitVal); // Adjust the Expr initializer and type. - ECD->setInitExpr(new ImplicitCastExpr(NewTy, ECD->getInitExpr())); + ECD->setInitExpr(new ImplicitCastExpr(NewTy, ECD->getInitExpr(), + /*isLvalue=*/false)); ECD->setType(NewTy); } diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 2f43e5fa9ecc..128df1138589 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -1597,7 +1597,7 @@ Sema::CheckReferenceInit(Expr *&Init, QualType &DeclType, // Perform the conversion. // FIXME: Binding to a subobject of the lvalue is going to require // more AST annotation than this. - ImpCastExprToType(Init, T1); + ImpCastExprToType(Init, T1, /*isLvalue=*/true); } } @@ -1656,7 +1656,7 @@ Sema::CheckReferenceInit(Expr *&Init, QualType &DeclType, // Perform the conversion. // FIXME: Binding to a subobject of the lvalue is going to require // more AST annotation than this. - ImpCastExprToType(Init, T1); + ImpCastExprToType(Init, T1, /*isLvalue=*/true); } break; @@ -1741,7 +1741,7 @@ Sema::CheckReferenceInit(Expr *&Init, QualType &DeclType, } else { // FIXME: Binding to a subobject of the rvalue is going to require // more AST annotation than this. - ImpCastExprToType(Init, T1); + ImpCastExprToType(Init, T1, /*isLvalue=*/true); } return false; } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 92efa68cc434..21bf990fba17 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -99,13 +99,47 @@ QualType Sema::UsualArithmeticConversions(Expr *&lhsExpr, Expr *&rhsExpr, UsualUnaryConversions(lhsExpr); UsualUnaryConversions(rhsExpr); } + // For conversion purposes, we ignore any qualifiers. // For example, "const float" and "float" are equivalent. QualType lhs = Context.getCanonicalType(lhsExpr->getType()).getUnqualifiedType(); QualType rhs = Context.getCanonicalType(rhsExpr->getType()).getUnqualifiedType(); - + + // If both types are identical, no conversion is needed. + if (lhs == rhs) + return lhs; + + // If either side is a non-arithmetic type (e.g. a pointer), we are done. + // The caller can deal with this (e.g. pointer + int). + if (!lhs->isArithmeticType() || !rhs->isArithmeticType()) + return lhs; + + QualType destType = UsualArithmeticConversionsType(lhs, rhs); + if (!isCompAssign) { + ImpCastExprToType(lhsExpr, destType); + ImpCastExprToType(rhsExpr, destType); + } + return destType; +} + +QualType Sema::UsualArithmeticConversionsType(QualType lhs, QualType rhs) { + // Perform the usual unary conversions. We do this early so that + // integral promotions to "int" can allow us to exit early, in the + // lhs == rhs check. Also, for conversion purposes, we ignore any + // qualifiers. For example, "const float" and "float" are + // equivalent. + if (lhs->isPromotableIntegerType()) + lhs = Context.IntTy; + else + lhs = Context.getCanonicalType(lhs).getUnqualifiedType(); + + if (rhs->isPromotableIntegerType()) + rhs = Context.IntTy; + else + rhs = Context.getCanonicalType(rhs).getUnqualifiedType(); + // If both types are identical, no conversion is needed. if (lhs == rhs) return lhs; @@ -122,12 +156,10 @@ QualType Sema::UsualArithmeticConversions(Expr *&lhsExpr, Expr *&rhsExpr, // if we have an integer operand, the result is the complex type. if (rhs->isIntegerType() || rhs->isComplexIntegerType()) { // convert the rhs to the lhs complex type. - if (!isCompAssign) ImpCastExprToType(rhsExpr, lhs); return lhs; } if (lhs->isIntegerType() || lhs->isComplexIntegerType()) { // convert the lhs to the rhs complex type. - if (!isCompAssign) ImpCastExprToType(lhsExpr, rhs); return rhs; } // This handles complex/complex, complex/float, or float/complex. @@ -144,24 +176,16 @@ QualType Sema::UsualArithmeticConversions(Expr *&lhsExpr, Expr *&rhsExpr, if (result > 0) { // The left side is bigger, convert rhs. rhs = Context.getFloatingTypeOfSizeWithinDomain(lhs, rhs); - if (!isCompAssign) - ImpCastExprToType(rhsExpr, rhs); } else if (result < 0) { // The right side is bigger, convert lhs. lhs = Context.getFloatingTypeOfSizeWithinDomain(rhs, lhs); - if (!isCompAssign) - ImpCastExprToType(lhsExpr, lhs); } // At this point, lhs and rhs have the same rank/size. Now, make sure the // domains match. This is a requirement for our implementation, C99 // does not require this promotion. if (lhs != rhs) { // Domains don't match, we have complex/float mix. if (lhs->isRealFloatingType()) { // handle "double, _Complex double". - if (!isCompAssign) - ImpCastExprToType(lhsExpr, rhs); return rhs; } else { // handle "_Complex double, double". - if (!isCompAssign) - ImpCastExprToType(rhsExpr, lhs); return lhs; } } @@ -172,12 +196,10 @@ QualType Sema::UsualArithmeticConversions(Expr *&lhsExpr, Expr *&rhsExpr, // if we have an integer operand, the result is the real floating type. if (rhs->isIntegerType() || rhs->isComplexIntegerType()) { // convert rhs to the lhs floating point type. - if (!isCompAssign) ImpCastExprToType(rhsExpr, lhs); return lhs; } if (lhs->isIntegerType() || lhs->isComplexIntegerType()) { // convert lhs to the rhs floating point type. - if (!isCompAssign) ImpCastExprToType(lhsExpr, rhs); return rhs; } // We have two real floating types, float/complex combos were handled above. @@ -185,14 +207,12 @@ QualType Sema::UsualArithmeticConversions(Expr *&lhsExpr, Expr *&rhsExpr, int result = Context.getFloatingTypeOrder(lhs, rhs); if (result > 0) { // convert the rhs - if (!isCompAssign) ImpCastExprToType(rhsExpr, lhs); return lhs; } if (result < 0) { // convert the lhs - if (!isCompAssign) ImpCastExprToType(lhsExpr, rhs); // convert the lhs return rhs; } - assert(0 && "Sema::UsualArithmeticConversions(): illegal float comparison"); + assert(0 && "Sema::UsualArithmeticConversionsType(): illegal float comparison"); } if (lhs->isComplexIntegerType() || rhs->isComplexIntegerType()) { // Handle GCC complex int extension. @@ -203,19 +223,14 @@ QualType Sema::UsualArithmeticConversions(Expr *&lhsExpr, Expr *&rhsExpr, if (Context.getIntegerTypeOrder(lhsComplexInt->getElementType(), rhsComplexInt->getElementType()) >= 0) { // convert the rhs - if (!isCompAssign) ImpCastExprToType(rhsExpr, lhs); return lhs; } - if (!isCompAssign) - ImpCastExprToType(lhsExpr, rhs); // convert the lhs return rhs; } else if (lhsComplexInt && rhs->isIntegerType()) { // convert the rhs to the lhs complex type. - if (!isCompAssign) ImpCastExprToType(rhsExpr, lhs); return lhs; } else if (rhsComplexInt && lhs->isIntegerType()) { // convert the lhs to the rhs complex type. - if (!isCompAssign) ImpCastExprToType(lhsExpr, rhs); return rhs; } } @@ -244,10 +259,6 @@ QualType Sema::UsualArithmeticConversions(Expr *&lhsExpr, Expr *&rhsExpr, // to the signed type. destType = Context.getCorrespondingUnsignedType(lhsSigned ? lhs : rhs); } - if (!isCompAssign) { - ImpCastExprToType(lhsExpr, destType); - ImpCastExprToType(rhsExpr, destType); - } return destType; } @@ -2765,6 +2776,14 @@ Action::ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc, if (getLangOptions().CPlusPlus && (lhs->getType()->isRecordType() || lhs->getType()->isEnumeralType() || rhs->getType()->isRecordType() || rhs->getType()->isEnumeralType())) { + // If this is one of the assignment operators, we only perform + // overload resolution if the left-hand side is a class or + // enumeration type (C++ [expr.ass]p3). + if (Opc >= BinaryOperator::Assign && Opc <= BinaryOperator::OrAssign && + !(lhs->getType()->isRecordType() || lhs->getType()->isEnumeralType())) { + return CreateBuiltinBinOp(TokLoc, Opc, lhs, rhs); + } + // C++ [over.binary]p1: // A binary operator shall be implemented either by a non-static // member function (9.3) with one parameter or by a non-member @@ -2809,40 +2828,57 @@ Action::ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc, = dyn_cast_or_null(D)) AddOverloadCandidates(Ovl, Args, 2, CandidateSet); - // FIXME: Add builtin overload candidates (C++ [over.built]). + // Add builtin overload candidates (C++ [over.built]). + AddBuiltinBinaryOperatorCandidates(OverOp, Args, CandidateSet); // Perform overload resolution. OverloadCandidateSet::iterator Best; switch (BestViableFunction(CandidateSet, Best)) { case OR_Success: { - // FIXME: We might find a built-in candidate here. + // We found a built-in operator or an overloaded operator. FunctionDecl *FnDecl = Best->Function; - // Convert the arguments. - // FIXME: Conversion will be different for member operators. - if (PerformCopyInitialization(lhs, FnDecl->getParamDecl(0)->getType(), - "passing") || - PerformCopyInitialization(rhs, FnDecl->getParamDecl(1)->getType(), - "passing")) - return true; + if (FnDecl) { + // We matched an overloaded operator. Build a call to that + // operator. - // Determine the result type - QualType ResultTy - = FnDecl->getType()->getAsFunctionType()->getResultType(); - ResultTy = ResultTy.getNonReferenceType(); + // Convert the arguments. + // FIXME: Conversion will be different for member operators. + if (PerformCopyInitialization(lhs, FnDecl->getParamDecl(0)->getType(), + "passing") || + PerformCopyInitialization(rhs, FnDecl->getParamDecl(1)->getType(), + "passing")) + return true; - // Build the actual expression node. - // FIXME: We lose the fact that we have a function here! - if (Opc > BinaryOperator::Assign && Opc <= BinaryOperator::OrAssign) - return new CompoundAssignOperator(lhs, rhs, Opc, ResultTy, ResultTy, - TokLoc); - else - return new BinaryOperator(lhs, rhs, Opc, ResultTy, TokLoc); + // Determine the result type + QualType ResultTy + = FnDecl->getType()->getAsFunctionType()->getResultType(); + ResultTy = ResultTy.getNonReferenceType(); + + // Build the actual expression node. + // FIXME: We lose the fact that we have a function here! + if (Opc > BinaryOperator::Assign && Opc <= BinaryOperator::OrAssign) + return new CompoundAssignOperator(lhs, rhs, Opc, ResultTy, ResultTy, + TokLoc); + else + return new BinaryOperator(lhs, rhs, Opc, ResultTy, TokLoc); + } else { + // We matched a built-in operator. Convert the arguments, then + // break out so that we will build the appropriate built-in + // operator node. + if (PerformCopyInitialization(lhs, Best->BuiltinTypes.ParamTypes[0], + "passing") || + PerformCopyInitialization(rhs, Best->BuiltinTypes.ParamTypes[1], + "passing")) + return true; + + break; + } } case OR_No_Viable_Function: // No viable function; fall through to handling this as a - // built-in operator. + // built-in operator, which will produce an error message for us. break; case OR_Ambiguous: @@ -2854,7 +2890,9 @@ Action::ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc, return true; } - // There was no viable overloaded operator; fall through. + // Either we found no viable overloaded operator or we matched a + // built-in operator. In either case, fall through to trying to + // build a built-in operation. } diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index b60e55492ba9..9229fa35d295 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -14,8 +14,11 @@ #include "Sema.h" #include "SemaInherit.h" #include "clang/Basic/Diagnostic.h" +#include "clang/Lex/Preprocessor.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Expr.h" +#include "clang/AST/TypeOrdering.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/Support/Compiler.h" #include @@ -413,6 +416,7 @@ Sema::IsStandardConversion(Expr* From, QualType ToType, return false; // Standard conversions (C++ [conv]) + SCS.setAsIdentityConversion(); SCS.Deprecated = false; SCS.FromTypePtr = FromType.getAsOpaquePtr(); SCS.CopyConstructor = 0; @@ -1530,7 +1534,7 @@ Sema::AddConversionCandidate(CXXConversionDecl *Conversion, DeclRefExpr ConversionRef(Conversion, Conversion->getType(), SourceLocation()); ImplicitCastExpr ConversionFn(Context.getPointerType(Conversion->getType()), - &ConversionRef); + &ConversionRef, false); CallExpr Call(&ConversionFn, 0, 0, Conversion->getConversionType().getNonReferenceType(), SourceLocation()); @@ -1550,6 +1554,562 @@ Sema::AddConversionCandidate(CXXConversionDecl *Conversion, } } +/// AddBuiltinCandidate - Add a candidate for a built-in +/// operator. ResultTy and ParamTys are the result and parameter types +/// of the built-in candidate, respectively. Args and NumArgs are the +/// arguments being passed to the candidate. +void Sema::AddBuiltinCandidate(QualType ResultTy, QualType *ParamTys, + Expr **Args, unsigned NumArgs, + OverloadCandidateSet& CandidateSet) { + // Add this candidate + CandidateSet.push_back(OverloadCandidate()); + OverloadCandidate& Candidate = CandidateSet.back(); + Candidate.Function = 0; + Candidate.BuiltinTypes.ResultTy = ResultTy; + for (unsigned ArgIdx = 0; ArgIdx < NumArgs; ++ArgIdx) + Candidate.BuiltinTypes.ParamTypes[ArgIdx] = ParamTys[ArgIdx]; + + // Determine the implicit conversion sequences for each of the + // arguments. + Candidate.Viable = true; + Candidate.Conversions.resize(NumArgs); + for (unsigned ArgIdx = 0; ArgIdx < NumArgs; ++ArgIdx) { + Candidate.Conversions[ArgIdx] + = TryCopyInitialization(Args[ArgIdx], ParamTys[ArgIdx], false); + if (Candidate.Conversions[ArgIdx].ConversionKind + == ImplicitConversionSequence::BadConversion) + Candidate.Viable = false; + } +} + +/// BuiltinCandidateTypeSet - A set of types that will be used for the +/// candidate operator functions for built-in operators (C++ +/// [over.built]). The types are separated into pointer types and +/// enumeration types. +class BuiltinCandidateTypeSet { + /// TypeSet - A set of types. + typedef llvm::DenseSet TypeSet; + + /// PointerTypes - The set of pointer types that will be used in the + /// built-in candidates. + TypeSet PointerTypes; + + /// EnumerationTypes - The set of enumeration types that will be + /// used in the built-in candidates. + TypeSet EnumerationTypes; + + /// Context - The AST context in which we will build the type sets. + ASTContext &Context; + + bool AddWithMoreQualifiedTypeVariants(QualType Ty); + +public: + /// iterator - Iterates through the types that are part of the set. + typedef TypeSet::iterator iterator; + + BuiltinCandidateTypeSet(ASTContext &Context) : Context(Context) { } + + void AddTypesConvertedFrom(QualType Ty, bool AllowUserConversions = true); + + /// pointer_begin - First pointer type found; + iterator pointer_begin() { return PointerTypes.begin(); } + + /// pointer_end - Last pointer type found; + iterator pointer_end() { return PointerTypes.end(); } + + /// enumeration_begin - First enumeration type found; + iterator enumeration_begin() { return EnumerationTypes.begin(); } + + /// enumeration_end - Last enumeration type found; + iterator enumeration_end() { return EnumerationTypes.end(); } +}; + +/// AddWithMoreQualifiedTypeVariants - Add the pointer type @p Ty to +/// the set of pointer types along with any more-qualified variants of +/// that type. For example, if @p Ty is "int const *", this routine +/// will add "int const *", "int const volatile *", "int const +/// restrict *", and "int const volatile restrict *" to the set of +/// pointer types. Returns true if the add of @p Ty itself succeeded, +/// false otherwise. +bool BuiltinCandidateTypeSet::AddWithMoreQualifiedTypeVariants(QualType Ty) { + // Insert this type. + if (!PointerTypes.insert(Ty).second) + return false; + + if (const PointerType *PointerTy = Ty->getAsPointerType()) { + QualType PointeeTy = PointerTy->getPointeeType(); + // FIXME: Optimize this so that we don't keep trying to add the same types. + + // FIXME: Do we have to add CVR qualifiers at *all* levels to deal + // with all pointer conversions that don't cast away constness? + if (!PointeeTy.isConstQualified()) + AddWithMoreQualifiedTypeVariants + (Context.getPointerType(PointeeTy.withConst())); + if (!PointeeTy.isVolatileQualified()) + AddWithMoreQualifiedTypeVariants + (Context.getPointerType(PointeeTy.withVolatile())); + if (!PointeeTy.isRestrictQualified()) + AddWithMoreQualifiedTypeVariants + (Context.getPointerType(PointeeTy.withRestrict())); + } + + return true; +} + +/// AddTypesConvertedFrom - Add each of the types to which the type @p +/// Ty can be implicit converted to the given set of @p Types. We're +/// primarily interested in pointer types, enumeration types, +void BuiltinCandidateTypeSet::AddTypesConvertedFrom(QualType Ty, + bool AllowUserConversions) { + // Only deal with canonical types. + Ty = Context.getCanonicalType(Ty); + + // Look through reference types; they aren't part of the type of an + // expression for the purposes of conversions. + if (const ReferenceType *RefTy = Ty->getAsReferenceType()) + Ty = RefTy->getPointeeType(); + + // We don't care about qualifiers on the type. + Ty = Ty.getUnqualifiedType(); + + if (const PointerType *PointerTy = Ty->getAsPointerType()) { + QualType PointeeTy = PointerTy->getPointeeType(); + + // Insert our type, and its more-qualified variants, into the set + // of types. + if (!AddWithMoreQualifiedTypeVariants(Ty)) + return; + + // Add 'cv void*' to our set of types. + if (!Ty->isVoidType()) { + QualType QualVoid + = Context.VoidTy.getQualifiedType(PointeeTy.getCVRQualifiers()); + AddWithMoreQualifiedTypeVariants(Context.getPointerType(QualVoid)); + } + + // If this is a pointer to a class type, add pointers to its bases + // (with the same level of cv-qualification as the original + // derived class, of course). + if (const RecordType *PointeeRec = PointeeTy->getAsRecordType()) { + CXXRecordDecl *ClassDecl = cast(PointeeRec->getDecl()); + for (CXXRecordDecl::base_class_iterator Base = ClassDecl->bases_begin(); + Base != ClassDecl->bases_end(); ++Base) { + QualType BaseTy = Context.getCanonicalType(Base->getType()); + BaseTy = BaseTy.getQualifiedType(PointeeTy.getCVRQualifiers()); + + // Add the pointer type, recursively, so that we get all of + // the indirect base classes, too. + AddTypesConvertedFrom(Context.getPointerType(BaseTy), false); + } + } + } else if (Ty->isEnumeralType()) { + EnumerationTypes.insert(Ty); + } else if (AllowUserConversions) { + if (const RecordType *TyRec = Ty->getAsRecordType()) { + CXXRecordDecl *ClassDecl = cast(TyRec->getDecl()); + // FIXME: Visit conversion functions in the base classes, too. + OverloadedFunctionDecl *Conversions + = ClassDecl->getConversionFunctions(); + for (OverloadedFunctionDecl::function_iterator Func + = Conversions->function_begin(); + Func != Conversions->function_end(); ++Func) { + CXXConversionDecl *Conv = cast(*Func); + AddTypesConvertedFrom(Conv->getConversionType(), false); + } + } + } +} + +/// AddBuiltinCandidates - Add the appropriate built-in operator +/// overloads to the candidate set (C++ [over.built]), based on the +/// operator @p Op and the arguments given. For example, if the +/// operator is a binary '+', this routine might add +/// "int operator+(int, int)" +/// to cover integer addition. +void +Sema::AddBuiltinBinaryOperatorCandidates(OverloadedOperatorKind Op, + Expr **Args, + OverloadCandidateSet& CandidateSet) { + // The set of "promoted arithmetic types", which are the arithmetic + // types are that preserved by promotion (C++ [over.built]p2). Note + // that the first few of these types are the promoted integral + // types; these types need to be first. + // FIXME: What about complex? + const unsigned FirstIntegralType = 0; + const unsigned LastIntegralType = 13; + const unsigned FirstPromotedIntegralType = 7, + LastPromotedIntegralType = 13; + const unsigned FirstPromotedArithmeticType = 7, + LastPromotedArithmeticType = 16; + const unsigned NumArithmeticTypes = 16; + QualType ArithmeticTypes[NumArithmeticTypes] = { + Context.BoolTy, Context.CharTy, Context.WCharTy, + Context.SignedCharTy, Context.ShortTy, + Context.UnsignedCharTy, Context.UnsignedShortTy, + Context.IntTy, Context.LongTy, Context.LongLongTy, + Context.UnsignedIntTy, Context.UnsignedLongTy, Context.UnsignedLongLongTy, + Context.FloatTy, Context.DoubleTy, Context.LongDoubleTy + }; + + // Find all of the types that the arguments can convert to, but only + // if the operator we're looking at has built-in operator candidates + // that make use of these types. + BuiltinCandidateTypeSet CandidateTypes(Context); + if (Op == OO_Less || Op == OO_Greater || Op == OO_LessEqual || + Op == OO_GreaterEqual || Op == OO_EqualEqual || Op == OO_ExclaimEqual || + Op == OO_Plus || Op == OO_Minus || Op == OO_Equal || + Op == OO_PlusEqual || Op == OO_MinusEqual || Op == OO_Subscript || + Op == OO_ArrowStar) { + for (unsigned ArgIdx = 0; ArgIdx < 2; ++ArgIdx) + CandidateTypes.AddTypesConvertedFrom(Args[ArgIdx]->getType()); + } + + bool isComparison = false; + switch (Op) { + case OO_None: + case NUM_OVERLOADED_OPERATORS: + assert(false && "Expected an overloaded operator"); + break; + + case OO_New: + case OO_Delete: + case OO_Array_New: + case OO_Array_Delete: + case OO_Tilde: + case OO_Exclaim: + case OO_PlusPlus: + case OO_MinusMinus: + case OO_Arrow: + case OO_Call: + assert(false && "Expected a binary operator"); + break; + + case OO_Comma: + // C++ [over.match.oper]p3: + // -- For the operator ',', the unary operator '&', or the + // operator '->', the built-in candidates set is empty. + // We don't check '&' or '->' here, since they are unary operators. + break; + + case OO_Less: + case OO_Greater: + case OO_LessEqual: + case OO_GreaterEqual: + case OO_EqualEqual: + case OO_ExclaimEqual: + // C++ [over.built]p15: + // + // For every pointer or enumeration type T, there exist + // candidate operator functions of the form + // + // bool operator<(T, T); + // bool operator>(T, T); + // bool operator<=(T, T); + // bool operator>=(T, T); + // bool operator==(T, T); + // bool operator!=(T, T); + for (BuiltinCandidateTypeSet::iterator Ptr = CandidateTypes.pointer_begin(); + Ptr != CandidateTypes.pointer_end(); ++Ptr) { + QualType ParamTypes[2] = { *Ptr, *Ptr }; + AddBuiltinCandidate(Context.BoolTy, ParamTypes, Args, 2, CandidateSet); + } + for (BuiltinCandidateTypeSet::iterator Enum + = CandidateTypes.enumeration_begin(); + Enum != CandidateTypes.enumeration_end(); ++Enum) { + QualType ParamTypes[2] = { *Enum, *Enum }; + AddBuiltinCandidate(Context.BoolTy, ParamTypes, Args, 2, CandidateSet); + } + + // Fall through. + isComparison = true; + + case OO_Plus: + case OO_Minus: + if (!isComparison) { + // We didn't fall through, so we must have OO_Plus or OO_Minus. + + // C++ [over.built]p13: + // + // For every cv-qualified or cv-unqualified object type T + // there exist candidate operator functions of the form + // + // T* operator+(T*, ptrdiff_t); + // T& operator[](T*, ptrdiff_t); [BELOW] + // T* operator-(T*, ptrdiff_t); + // T* operator+(ptrdiff_t, T*); + // T& operator[](ptrdiff_t, T*); [BELOW] + // + // C++ [over.built]p14: + // + // For every T, where T is a pointer to object type, there + // exist candidate operator functions of the form + // + // ptrdiff_t operator-(T, T); + for (BuiltinCandidateTypeSet::iterator Ptr + = CandidateTypes.pointer_begin(); + Ptr != CandidateTypes.pointer_end(); ++Ptr) { + QualType ParamTypes[2] = { *Ptr, Context.getPointerDiffType() }; + + // operator+(T*, ptrdiff_t) or operator-(T*, ptrdiff_t) + AddBuiltinCandidate(*Ptr, ParamTypes, Args, 2, CandidateSet); + + if (Op == OO_Plus) { + // T* operator+(ptrdiff_t, T*); + ParamTypes[0] = ParamTypes[1]; + ParamTypes[1] = *Ptr; + AddBuiltinCandidate(*Ptr, ParamTypes, Args, 2, CandidateSet); + } else { + // ptrdiff_t operator-(T, T); + ParamTypes[1] = *Ptr; + AddBuiltinCandidate(Context.getPointerDiffType(), ParamTypes, + Args, 2, CandidateSet); + } + } + } + // Fall through + + case OO_Star: + case OO_Slash: + // C++ [over.built]p12: + // + // For every pair of promoted arithmetic types L and R, there + // exist candidate operator functions of the form + // + // LR operator*(L, R); + // LR operator/(L, R); + // LR operator+(L, R); + // LR operator-(L, R); + // bool operator<(L, R); + // bool operator>(L, R); + // bool operator<=(L, R); + // bool operator>=(L, R); + // bool operator==(L, R); + // bool operator!=(L, R); + // + // where LR is the result of the usual arithmetic conversions + // between types L and R. + for (unsigned Left = FirstPromotedArithmeticType; + Left < LastPromotedArithmeticType; ++Left) { + for (unsigned Right = FirstPromotedArithmeticType; + Right < LastPromotedArithmeticType; ++Right) { + QualType LandR[2] = { ArithmeticTypes[Left], ArithmeticTypes[Right] }; + QualType Result + = isComparison? Context.BoolTy + : UsualArithmeticConversionsType(LandR[0], LandR[1]); + AddBuiltinCandidate(Result, LandR, Args, 2, CandidateSet); + } + } + break; + + case OO_Percent: + case OO_Amp: + case OO_Caret: + case OO_Pipe: + case OO_LessLess: + case OO_GreaterGreater: + // C++ [over.built]p17: + // + // For every pair of promoted integral types L and R, there + // exist candidate operator functions of the form + // + // LR operator%(L, R); + // LR operator&(L, R); + // LR operator^(L, R); + // LR operator|(L, R); + // L operator<<(L, R); + // L operator>>(L, R); + // + // where LR is the result of the usual arithmetic conversions + // between types L and R. + for (unsigned Left = FirstPromotedIntegralType; + Left < LastPromotedIntegralType; ++Left) { + for (unsigned Right = FirstPromotedIntegralType; + Right < LastPromotedIntegralType; ++Right) { + QualType LandR[2] = { ArithmeticTypes[Left], ArithmeticTypes[Right] }; + QualType Result = (Op == OO_LessLess || Op == OO_GreaterGreater) + ? LandR[0] + : UsualArithmeticConversionsType(LandR[0], LandR[1]); + AddBuiltinCandidate(Result, LandR, Args, 2, CandidateSet); + } + } + break; + + case OO_Equal: + // C++ [over.built]p20: + // + // For every pair (T, VQ), where T is an enumeration or + // (FIXME:) pointer to member type and VQ is either volatile or + // empty, there exist candidate operator functions of the form + // + // VQ T& operator=(VQ T&, T); + for (BuiltinCandidateTypeSet::iterator Enum + = CandidateTypes.enumeration_begin(); + Enum != CandidateTypes.enumeration_end(); ++Enum) { + QualType ParamTypes[2]; + + // T& operator=(T&, T) + ParamTypes[0] = Context.getReferenceType(*Enum); + ParamTypes[1] = *Enum; + AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet); + + // volatile T& operator=(volatile T&, T) + ParamTypes[0] = Context.getReferenceType(Enum->withVolatile()); + ParamTypes[1] = *Enum; + AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet); + } + // Fall through. + + case OO_PlusEqual: + case OO_MinusEqual: + // C++ [over.built]p19: + // + // For every pair (T, VQ), where T is any type and VQ is either + // volatile or empty, there exist candidate operator functions + // of the form + // + // T*VQ& operator=(T*VQ&, T*); + // + // C++ [over.built]p21: + // + // For every pair (T, VQ), where T is a cv-qualified or + // cv-unqualified object type and VQ is either volatile or + // empty, there exist candidate operator functions of the form + // + // T*VQ& operator+=(T*VQ&, ptrdiff_t); + // T*VQ& operator-=(T*VQ&, ptrdiff_t); + for (BuiltinCandidateTypeSet::iterator Ptr = CandidateTypes.pointer_begin(); + Ptr != CandidateTypes.pointer_end(); ++Ptr) { + QualType ParamTypes[2]; + ParamTypes[1] = (Op == OO_Equal)? *Ptr : Context.getPointerDiffType(); + + // non-volatile version + ParamTypes[0] = Context.getReferenceType(*Ptr); + AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet); + + // volatile version + ParamTypes[0] = Context.getReferenceType(Ptr->withVolatile()); + AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet); + } + // Fall through. + + case OO_StarEqual: + case OO_SlashEqual: + // C++ [over.built]p18: + // + // For every triple (L, VQ, R), where L is an arithmetic type, + // VQ is either volatile or empty, and R is a promoted + // arithmetic type, there exist candidate operator functions of + // the form + // + // VQ L& operator=(VQ L&, R); + // VQ L& operator*=(VQ L&, R); + // VQ L& operator/=(VQ L&, R); + // VQ L& operator+=(VQ L&, R); + // VQ L& operator-=(VQ L&, R); + for (unsigned Left = 0; Left < NumArithmeticTypes; ++Left) { + for (unsigned Right = FirstPromotedArithmeticType; + Right < LastPromotedArithmeticType; ++Right) { + QualType ParamTypes[2]; + ParamTypes[1] = ArithmeticTypes[Right]; + + // Add this built-in operator as a candidate (VQ is empty). + ParamTypes[0] = Context.getReferenceType(ArithmeticTypes[Left]); + AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet); + + // Add this built-in operator as a candidate (VQ is 'volatile'). + ParamTypes[0] = ArithmeticTypes[Left].withVolatile(); + ParamTypes[0] = Context.getReferenceType(ParamTypes[0]); + AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet); + } + } + break; + + case OO_PercentEqual: + case OO_LessLessEqual: + case OO_GreaterGreaterEqual: + case OO_AmpEqual: + case OO_CaretEqual: + case OO_PipeEqual: + // C++ [over.built]p22: + // + // For every triple (L, VQ, R), where L is an integral type, VQ + // is either volatile or empty, and R is a promoted integral + // type, there exist candidate operator functions of the form + // + // VQ L& operator%=(VQ L&, R); + // VQ L& operator<<=(VQ L&, R); + // VQ L& operator>>=(VQ L&, R); + // VQ L& operator&=(VQ L&, R); + // VQ L& operator^=(VQ L&, R); + // VQ L& operator|=(VQ L&, R); + for (unsigned Left = FirstIntegralType; Left < LastIntegralType; ++Left) { + for (unsigned Right = FirstPromotedIntegralType; + Right < LastPromotedIntegralType; ++Right) { + QualType ParamTypes[2]; + ParamTypes[1] = ArithmeticTypes[Right]; + + // Add this built-in operator as a candidate (VQ is empty). + // FIXME: We should be caching these declarations somewhere, + // rather than re-building them every time. + ParamTypes[0] = Context.getReferenceType(ArithmeticTypes[Left]); + AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet); + + // Add this built-in operator as a candidate (VQ is 'volatile'). + ParamTypes[0] = ArithmeticTypes[Left]; + ParamTypes[0].addVolatile(); + ParamTypes[0] = Context.getReferenceType(ParamTypes[0]); + AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet); + } + } + break; + + case OO_AmpAmp: + case OO_PipePipe: { + // C++ [over.operator]p23: + // + // There also exist candidate operator functions of the form + // + // bool operator!(bool); [In Unary version] + // bool operator&&(bool, bool); + // bool operator||(bool, bool); + QualType ParamTypes[2] = { Context.BoolTy, Context.BoolTy }; + AddBuiltinCandidate(Context.BoolTy, ParamTypes, Args, 2, CandidateSet); + break; + } + + case OO_Subscript: + // C++ [over.built]p13: + // + // For every cv-qualified or cv-unqualified object type T there + // exist candidate operator functions of the form + // + // T* operator+(T*, ptrdiff_t); [ABOVE] + // T& operator[](T*, ptrdiff_t); + // T* operator-(T*, ptrdiff_t); [ABOVE] + // T* operator+(ptrdiff_t, T*); [ABOVE] + // T& operator[](ptrdiff_t, T*); + for (BuiltinCandidateTypeSet::iterator Ptr = CandidateTypes.pointer_begin(); + Ptr != CandidateTypes.pointer_end(); ++Ptr) { + QualType ParamTypes[2] = { *Ptr, Context.getPointerDiffType() }; + QualType PointeeType = (*Ptr)->getAsPointerType()->getPointeeType(); + QualType ResultTy = Context.getReferenceType(PointeeType); + + // T& operator[](T*, ptrdiff_t) + AddBuiltinCandidate(ResultTy, ParamTypes, Args, 2, CandidateSet); + + // T& operator[](ptrdiff_t, T*); + ParamTypes[0] = ParamTypes[1]; + ParamTypes[1] = *Ptr; + AddBuiltinCandidate(ResultTy, ParamTypes, Args, 2, CandidateSet); + } + break; + + case OO_ArrowStar: + // FIXME: No support for pointer-to-members yet. + break; + } +} + /// AddOverloadCandidates - Add all of the function overloads in Ovl /// to the candidate set. void @@ -1609,7 +2169,8 @@ Sema::isBetterOverloadCandidate(const OverloadCandidate& Cand1, if (HasBetterConversion) return true; - // FIXME: Several other bullets in (C++ 13.3.3p1) need to be implemented. + // FIXME: Several other bullets in (C++ 13.3.3p1) need to be + // implemented, but they require template support. // C++ [over.match.best]p1b4: // @@ -1688,8 +2249,24 @@ Sema::PrintOverloadCandidates(OverloadCandidateSet& CandidateSet, OverloadCandidateSet::iterator Cand = CandidateSet.begin(), LastCand = CandidateSet.end(); for (; Cand != LastCand; ++Cand) { - if (Cand->Viable ||!OnlyViable) - Diag(Cand->Function->getLocation(), diag::err_ovl_candidate); + if (Cand->Viable || !OnlyViable) { + if (Cand->Function) { + // Normal function + Diag(Cand->Function->getLocation(), diag::err_ovl_candidate); + } else { + // FIXME: We need to get the identifier in here + // FIXME: Do we want the error message to point at the + // operator? (built-ins won't have a location) + QualType FnType + = Context.getFunctionType(Cand->BuiltinTypes.ResultTy, + Cand->BuiltinTypes.ParamTypes, + Cand->Conversions.size(), + false, 0); + + Diag(SourceLocation(), diag::err_ovl_builtin_candidate, + FnType.getAsString()); + } + } } } diff --git a/clang/lib/Sema/SemaOverload.h b/clang/lib/Sema/SemaOverload.h index 00a81859c8c3..24b796b21143 100644 --- a/clang/lib/Sema/SemaOverload.h +++ b/clang/lib/Sema/SemaOverload.h @@ -198,8 +198,16 @@ namespace clang { /// OverloadCandidate - A single candidate in an overload set (C++ 13.3). struct OverloadCandidate { - /// Function - The actual function that this candidate represents. + /// Function - The actual function that this candidate + /// represents. When NULL, this is a built-in candidate. FunctionDecl *Function; + + // BuiltinTypes - Provides the return and parameter types of a + // built-in overload candidate. Only valid when Function is NULL. + struct { + QualType ResultTy; + QualType ParamTypes[3]; + } BuiltinTypes; /// Conversions - The conversion sequences used to convert the /// function arguments to the function parameters. @@ -217,7 +225,7 @@ namespace clang { /// OverloadCandidateSet - A set of overload candidates, used in C++ /// overload resolution (C++ 13.3). - typedef llvm::SmallVector OverloadCandidateSet; + typedef llvm::SmallVector OverloadCandidateSet; } // end namespace clang #endif // LLVM_CLANG_SEMA_OVERLOAD_H diff --git a/clang/test/SemaCXX/overloaded-builtin-operators.cpp b/clang/test/SemaCXX/overloaded-builtin-operators.cpp new file mode 100644 index 000000000000..aabe8dde1b1e --- /dev/null +++ b/clang/test/SemaCXX/overloaded-builtin-operators.cpp @@ -0,0 +1,66 @@ +// RUN: clang -fsyntax-only -verify %s +struct yes; +struct no; + +struct Short { + operator short(); +}; + +struct Long { + operator long(); +}; + +yes& islong(long); +no& islong(int); + +void f(Short s, Long l) { + // C++ [over.built]p12 + (void)static_cast(islong(s + l)); + (void)static_cast(islong(s + s)); + + // C++ [over.built]p17 + (void)static_cast(islong(s % l)); + (void)static_cast(islong(l << s)); + (void)static_cast(islong(s << l)); +} + +struct ShortRef { + operator short&(); +}; + +struct LongRef { + operator volatile long&(); +}; + +void g(ShortRef sr, LongRef lr) { + // C++ [over.built]p18 + short& sr1 = (sr *= lr); + volatile long& lr1 = (lr *= sr); + + // C++ [over.built]p22 + short& sr2 = (sr %= lr); + volatile long& lr2 = (lr <<= sr); + + bool b1 = (sr && lr) || (sr || lr); +} + +struct VolatileIntPtr { + operator int volatile *(); +}; + +struct ConstIntPtr { + operator int const *(); +}; + +void test_with_ptrs(VolatileIntPtr vip, ConstIntPtr cip, ShortRef sr) { +#if 0 + // FIXME: Enable these tests once we have operator overloading for + // operator[]. + const int& cir1 = cip[sr]; + const int& cir2 = sr[cip]; + volatile int& vir1 = vip[sr]; + volatile int& vir2 = sr[vip]; +#endif + bool b1 = (vip == cip); + long p1 = vip - cip; +}