From d08452f60a2731c279f3b845d84a4a8940844d99 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Wed, 19 Nov 2008 15:42:04 +0000 Subject: [PATCH] Added operator overloading for unary operators, post-increment, and post-decrement, including support for generating all of the built-in operator candidates for these operators. C++ and C have different rules for the arguments to the builtin unary '+' and '-'. Implemented both variants in Sema::ActOnUnaryOp. In C++, pre-increment and pre-decrement return lvalues. Update Expr::isLvalue accordingly. llvm-svn: 59638 --- clang/Driver/PrintParserCallbacks.cpp | 6 +- clang/include/clang/Parse/Action.h | 6 +- clang/lib/AST/Expr.cpp | 5 + clang/lib/Parse/ParseExpr.cpp | 10 +- clang/lib/Parse/ParseStmt.cpp | 3 +- clang/lib/Sema/Sema.h | 12 +- clang/lib/Sema/SemaExpr.cpp | 219 ++++++++++++++-- clang/lib/Sema/SemaOverload.cpp | 242 +++++++++++++++--- .../SemaCXX/overloaded-builtin-operators.cpp | 40 ++- clang/test/SemaCXX/overloaded-operator.cpp | 28 ++ clang/www/cxx_status.html | 38 +-- 11 files changed, 522 insertions(+), 87 deletions(-) diff --git a/clang/Driver/PrintParserCallbacks.cpp b/clang/Driver/PrintParserCallbacks.cpp index 0048a6d459b1..2a85f64646fa 100644 --- a/clang/Driver/PrintParserCallbacks.cpp +++ b/clang/Driver/PrintParserCallbacks.cpp @@ -436,7 +436,7 @@ namespace { } // Postfix Expressions. - virtual ExprResult ActOnPostfixUnaryOp(SourceLocation OpLoc, + virtual ExprResult ActOnPostfixUnaryOp(Scope *S, SourceLocation OpLoc, tok::TokenKind Kind, ExprTy *Input) { llvm::cout << __FUNCTION__ << "\n"; return 0; @@ -467,8 +467,8 @@ namespace { } // Unary Operators. 'Tok' is the token for the operator. - virtual ExprResult ActOnUnaryOp(SourceLocation OpLoc, tok::TokenKind Op, - ExprTy *Input) { + virtual ExprResult ActOnUnaryOp(Scope *S, SourceLocation OpLoc, + tok::TokenKind Op, ExprTy *Input) { llvm::cout << __FUNCTION__ << "\n"; return 0; } diff --git a/clang/include/clang/Parse/Action.h b/clang/include/clang/Parse/Action.h index 8e1a23f6e743..e9e59257dfd3 100644 --- a/clang/include/clang/Parse/Action.h +++ b/clang/include/clang/Parse/Action.h @@ -509,7 +509,7 @@ public: } // Postfix Expressions. - virtual ExprResult ActOnPostfixUnaryOp(SourceLocation OpLoc, + virtual ExprResult ActOnPostfixUnaryOp(Scope *S, SourceLocation OpLoc, tok::TokenKind Kind, ExprTy *Input) { return 0; } @@ -536,8 +536,8 @@ public: } // Unary Operators. 'Tok' is the token for the operator. - virtual ExprResult ActOnUnaryOp(SourceLocation OpLoc, tok::TokenKind Op, - ExprTy *Input) { + virtual ExprResult ActOnUnaryOp(Scope *S, SourceLocation OpLoc, + tok::TokenKind Op, ExprTy *Input) { return 0; } virtual ExprResult diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 49a4d7778380..a179af8b1450 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -398,6 +398,11 @@ Expr::isLvalueResult Expr::isLvalue(ASTContext &Ctx) const { cast(this)->getOpcode() == UnaryOperator::Imag || cast(this)->getOpcode() == UnaryOperator::Extension) return cast(this)->getSubExpr()->isLvalue(Ctx); // GNU. + + if (Ctx.getLangOptions().CPlusPlus && // C++ [expr.pre.incr]p1 + (cast(this)->getOpcode() == UnaryOperator::PreInc || + cast(this)->getOpcode() == UnaryOperator::PreDec)) + return LV_Valid; break; case ImplicitCastExprClass: return cast(this)->isLvalueCast()? LV_Valid diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 2b803d9ad0c7..440393dac6bf 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -515,7 +515,7 @@ Parser::ExprResult Parser::ParseCastExpression(bool isUnaryExpression) { SourceLocation SavedLoc = ConsumeToken(); Res = ParseCastExpression(true); if (!Res.isInvalid) - Res = Actions.ActOnUnaryOp(SavedLoc, SavedKind, Res.Val); + Res = Actions.ActOnUnaryOp(CurScope, SavedLoc, SavedKind, Res.Val); return Res; } case tok::amp: // unary-expression: '&' cast-expression @@ -529,7 +529,7 @@ Parser::ExprResult Parser::ParseCastExpression(bool isUnaryExpression) { SourceLocation SavedLoc = ConsumeToken(); Res = ParseCastExpression(false); if (!Res.isInvalid) - Res = Actions.ActOnUnaryOp(SavedLoc, SavedKind, Res.Val); + Res = Actions.ActOnUnaryOp(CurScope, SavedLoc, SavedKind, Res.Val); return Res; } @@ -539,7 +539,7 @@ Parser::ExprResult Parser::ParseCastExpression(bool isUnaryExpression) { SourceLocation SavedLoc = ConsumeToken(); Res = ParseCastExpression(false); if (!Res.isInvalid) - Res = Actions.ActOnUnaryOp(SavedLoc, SavedKind, Res.Val); + Res = Actions.ActOnUnaryOp(CurScope, SavedLoc, SavedKind, Res.Val); return Res; } case tok::kw_sizeof: // unary-expression: 'sizeof' unary-expression @@ -724,8 +724,8 @@ Parser::ExprResult Parser::ParsePostfixExpressionSuffix(ExprResult LHS) { case tok::plusplus: // postfix-expression: postfix-expression '++' case tok::minusminus: // postfix-expression: postfix-expression '--' if (!LHS.isInvalid) - LHS = Actions.ActOnPostfixUnaryOp(Tok.getLocation(), Tok.getKind(), - LHS.Val); + LHS = Actions.ActOnPostfixUnaryOp(CurScope, Tok.getLocation(), + Tok.getKind(), LHS.Val); ConsumeToken(); break; } diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp index 49b818090486..221ad90273dd 100644 --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -386,7 +386,8 @@ Parser::StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) { } // Add the __extension__ node to the AST. - Res = Actions.ActOnUnaryOp(ExtLoc, tok::kw___extension__, Res.Val); + Res = Actions.ActOnUnaryOp(CurScope, ExtLoc, tok::kw___extension__, + Res.Val); if (Res.isInvalid) continue; diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index 29994d41c30b..c60fd352f9db 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -435,9 +435,9 @@ public: void AddBuiltinCandidate(QualType ResultTy, QualType *ParamTys, Expr **Args, unsigned NumArgs, OverloadCandidateSet& CandidateSet); - void AddBuiltinBinaryOperatorCandidates(OverloadedOperatorKind Op, - Expr **Args, - OverloadCandidateSet& CandidateSet); + void AddBuiltinOperatorCandidates(OverloadedOperatorKind Op, + Expr **Args, unsigned NumArgs, + OverloadCandidateSet& CandidateSet); void AddOverloadCandidates(const OverloadedFunctionDecl *Ovl, Expr **Args, unsigned NumArgs, OverloadCandidateSet& CandidateSet, @@ -640,8 +640,8 @@ public: virtual ExprResult ActOnStringLiteral(const Token *Toks, unsigned NumToks); // Binary/Unary Operators. 'Tok' is the token for the operator. - virtual ExprResult ActOnUnaryOp(SourceLocation OpLoc, tok::TokenKind Op, - ExprTy *Input); + virtual ExprResult ActOnUnaryOp(Scope *S, SourceLocation OpLoc, + tok::TokenKind Op, ExprTy *Input); virtual ExprResult ActOnSizeOfAlignOfExpr(SourceLocation OpLoc, bool isSizeof, bool isType, void *TyOrEx, const SourceRange &ArgRange); @@ -649,7 +649,7 @@ public: bool CheckSizeOfAlignOfOperand(QualType type, SourceLocation OpLoc, const SourceRange &R, bool isSizeof); - virtual ExprResult ActOnPostfixUnaryOp(SourceLocation OpLoc, + virtual ExprResult ActOnPostfixUnaryOp(Scope *S, SourceLocation OpLoc, tok::TokenKind Kind, ExprTy *Input); virtual ExprResult ActOnArraySubscriptExpr(ExprTy *Base, SourceLocation LLoc, diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 9a77e4f8e1d5..c7803722fe9f 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -752,19 +752,112 @@ QualType Sema::CheckRealImagOperand(Expr *&V, SourceLocation Loc) { -Action::ExprResult Sema::ActOnPostfixUnaryOp(SourceLocation OpLoc, +Action::ExprResult Sema::ActOnPostfixUnaryOp(Scope *S, SourceLocation OpLoc, tok::TokenKind Kind, ExprTy *Input) { + Expr *Arg = (Expr *)Input; + UnaryOperator::Opcode Opc; switch (Kind) { default: assert(0 && "Unknown unary op!"); case tok::plusplus: Opc = UnaryOperator::PostInc; break; case tok::minusminus: Opc = UnaryOperator::PostDec; break; } - QualType result = CheckIncrementDecrementOperand((Expr *)Input, OpLoc); + + if (getLangOptions().CPlusPlus && + (Arg->getType()->isRecordType() || Arg->getType()->isEnumeralType())) { + // Which overloaded operator? + OverloadedOperatorKind OverOp = + (Opc == UnaryOperator::PostInc)? OO_PlusPlus : OO_MinusMinus; + + // C++ [over.inc]p1: + // + // [...] If the function is a member function with one + // parameter (which shall be of type int) or a non-member + // function with two parameters (the second of which shall be + // of type int), it defines the postfix increment operator ++ + // for objects of that type. When the postfix increment is + // called as a result of using the ++ operator, the int + // argument will have value zero. + Expr *Args[2] = { + Arg, + new IntegerLiteral(llvm::APInt(Context.Target.getIntWidth(), 0, + /*isSigned=*/true), + Context.IntTy, SourceLocation()) + }; + + // Build the candidate set for overloading + OverloadCandidateSet CandidateSet; + AddOperatorCandidates(OverOp, S, Args, 2, CandidateSet); + + // Perform overload resolution. + OverloadCandidateSet::iterator Best; + switch (BestViableFunction(CandidateSet, Best)) { + case OR_Success: { + // We found a built-in operator or an overloaded operator. + FunctionDecl *FnDecl = Best->Function; + + if (FnDecl) { + // We matched an overloaded operator. Build a call to that + // operator. + + // Convert the arguments. + if (CXXMethodDecl *Method = dyn_cast(FnDecl)) { + if (PerformObjectArgumentInitialization(Arg, Method)) + return true; + } else { + // Convert the arguments. + if (PerformCopyInitialization(Arg, + FnDecl->getParamDecl(0)->getType(), + "passing")) + return true; + } + + // Determine the result type + QualType ResultTy + = FnDecl->getType()->getAsFunctionType()->getResultType(); + ResultTy = ResultTy.getNonReferenceType(); + + // Build the actual expression node. + Expr *FnExpr = new DeclRefExpr(FnDecl, FnDecl->getType(), + SourceLocation()); + UsualUnaryConversions(FnExpr); + + return new CXXOperatorCallExpr(FnExpr, Args, 2, ResultTy, OpLoc); + } 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(Arg, Best->BuiltinTypes.ParamTypes[0], + "passing")) + return true; + + break; + } + } + + case OR_No_Viable_Function: + // No viable function; fall through to handling this as a + // built-in operator, which will produce an error message for us. + break; + + case OR_Ambiguous: + Diag(OpLoc, diag::err_ovl_ambiguous_oper) + << UnaryOperator::getOpcodeStr(Opc) + << Arg->getSourceRange(); + PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/true); + return true; + } + + // 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. + } + + QualType result = CheckIncrementDecrementOperand(Arg, OpLoc); if (result.isNull()) return true; - return new UnaryOperator((Expr *)Input, Opc, result, OpLoc); + return new UnaryOperator(Arg, Opc, result, OpLoc); } Action::ExprResult Sema:: @@ -2819,16 +2912,6 @@ Action::ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc, !(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 - // function with two parameters. Thus, for any binary operator - // @, x@y can be interpreted as either x.operator@(y) or - // operator@(x,y). If both forms of the operator function have - // been declared, the rules in 13.3.1.2 determines which, if - // any, interpretation is used. - OverloadCandidateSet CandidateSet; // Determine which overloaded operator we're dealing with. static const OverloadedOperatorKind OverOps[] = { @@ -2854,6 +2937,7 @@ Action::ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc, // Add the appropriate overloaded operators (C++ [over.match.oper]) // to the candidate set. + OverloadCandidateSet CandidateSet; Expr *Args[2] = { lhs, rhs }; AddOperatorCandidates(OverOp, S, Args, 2, CandidateSet); @@ -2893,7 +2977,6 @@ Action::ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc, SourceLocation()); UsualUnaryConversions(FnExpr); - Expr *Args[2] = { lhs, rhs }; return new CXXOperatorCallExpr(FnExpr, Args, 2, ResultTy, TokLoc); } else { // We matched a built-in operator. Convert the arguments, then @@ -2933,10 +3016,98 @@ Action::ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc, } // Unary Operators. 'Tok' is the token for the operator. -Action::ExprResult Sema::ActOnUnaryOp(SourceLocation OpLoc, tok::TokenKind Op, - ExprTy *input) { +Action::ExprResult Sema::ActOnUnaryOp(Scope *S, SourceLocation OpLoc, + tok::TokenKind Op, ExprTy *input) { Expr *Input = (Expr*)input; UnaryOperator::Opcode Opc = ConvertTokenKindToUnaryOpcode(Op); + + if (getLangOptions().CPlusPlus && + (Input->getType()->isRecordType() + || Input->getType()->isEnumeralType())) { + // Determine which overloaded operator we're dealing with. + static const OverloadedOperatorKind OverOps[] = { + OO_None, OO_None, + OO_PlusPlus, OO_MinusMinus, + OO_Amp, OO_Star, + OO_Plus, OO_Minus, + OO_Tilde, OO_Exclaim, + OO_None, OO_None, + OO_None, + OO_None + }; + OverloadedOperatorKind OverOp = OverOps[Opc]; + + // Add the appropriate overloaded operators (C++ [over.match.oper]) + // to the candidate set. + OverloadCandidateSet CandidateSet; + if (OverOp != OO_None) + AddOperatorCandidates(OverOp, S, &Input, 1, CandidateSet); + + // Perform overload resolution. + OverloadCandidateSet::iterator Best; + switch (BestViableFunction(CandidateSet, Best)) { + case OR_Success: { + // We found a built-in operator or an overloaded operator. + FunctionDecl *FnDecl = Best->Function; + + if (FnDecl) { + // We matched an overloaded operator. Build a call to that + // operator. + + // Convert the arguments. + if (CXXMethodDecl *Method = dyn_cast(FnDecl)) { + if (PerformObjectArgumentInitialization(Input, Method)) + return true; + } else { + // Convert the arguments. + if (PerformCopyInitialization(Input, + FnDecl->getParamDecl(0)->getType(), + "passing")) + return true; + } + + // Determine the result type + QualType ResultTy + = FnDecl->getType()->getAsFunctionType()->getResultType(); + ResultTy = ResultTy.getNonReferenceType(); + + // Build the actual expression node. + Expr *FnExpr = new DeclRefExpr(FnDecl, FnDecl->getType(), + SourceLocation()); + UsualUnaryConversions(FnExpr); + + return new CXXOperatorCallExpr(FnExpr, &Input, 1, ResultTy, OpLoc); + } 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(Input, Best->BuiltinTypes.ParamTypes[0], + "passing")) + return true; + + break; + } + } + + case OR_No_Viable_Function: + // No viable function; fall through to handling this as a + // built-in operator, which will produce an error message for us. + break; + + case OR_Ambiguous: + Diag(OpLoc, diag::err_ovl_ambiguous_oper) + << UnaryOperator::getOpcodeStr(Opc) + << Input->getSourceRange(); + PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/true); + return true; + } + + // 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. + } + + QualType resultType; switch (Opc) { default: @@ -2956,10 +3127,18 @@ Action::ExprResult Sema::ActOnUnaryOp(SourceLocation OpLoc, tok::TokenKind Op, case UnaryOperator::Minus: UsualUnaryConversions(Input); resultType = Input->getType(); - if (!resultType->isArithmeticType()) // C99 6.5.3.3p1 - return Diag(OpLoc, diag::err_typecheck_unary_expr, - resultType.getAsString()); - break; + if (resultType->isArithmeticType()) // C99 6.5.3.3p1 + break; + else if (getLangOptions().CPlusPlus && // C++ [expr.unary.op]p6-7 + resultType->isEnumeralType()) + break; + else if (getLangOptions().CPlusPlus && // C++ [expr.unary.op]p6 + Opc == UnaryOperator::Plus && + resultType->isPointerType()) + break; + + return Diag(OpLoc, diag::err_typecheck_unary_expr, + resultType.getAsString()); case UnaryOperator::Not: // bitwise complement UsualUnaryConversions(Input); resultType = Input->getType(); diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index dbd21264f3b5..869fcc93528d 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -1843,8 +1843,7 @@ void Sema::AddOperatorCandidates(OverloadedOperatorKind Op, Scope *S, } // Add builtin overload candidates (C++ [over.built]). - if (NumArgs == 2) - return AddBuiltinBinaryOperatorCandidates(Op, Args, CandidateSet); + AddBuiltinOperatorCandidates(Op, Args, NumArgs, CandidateSet); } /// AddBuiltinCandidate - Add a candidate for a built-in @@ -2053,16 +2052,15 @@ void BuiltinCandidateTypeSet::AddTypesConvertedFrom(QualType Ty, } } -/// 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. +/// AddBuiltinOperatorCandidates - 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) { +Sema::AddBuiltinOperatorCandidates(OverloadedOperatorKind Op, + Expr **Args, unsigned NumArgs, + 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 @@ -2090,10 +2088,11 @@ Sema::AddBuiltinBinaryOperatorCandidates(OverloadedOperatorKind Op, 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_Plus || (Op == OO_Minus && NumArgs == 2) || Op == OO_Equal || Op == OO_PlusEqual || Op == OO_MinusEqual || Op == OO_Subscript || - Op == OO_ArrowStar) { - for (unsigned ArgIdx = 0; ArgIdx < 2; ++ArgIdx) + Op == OO_ArrowStar || Op == OO_PlusPlus || Op == OO_MinusMinus || + (Op == OO_Star && NumArgs == 1)) { + for (unsigned ArgIdx = 0; ArgIdx < NumArgs; ++ArgIdx) CandidateTypes.AddTypesConvertedFrom(Args[ArgIdx]->getType()); } @@ -2104,24 +2103,184 @@ Sema::AddBuiltinBinaryOperatorCandidates(OverloadedOperatorKind Op, assert(false && "Expected an overloaded operator"); break; + case OO_Star: // '*' is either unary or binary + if (NumArgs == 1) + goto UnaryStar; + else + goto BinaryStar; + break; + + case OO_Plus: // '+' is either unary or binary + if (NumArgs == 1) + goto UnaryPlus; + else + goto BinaryPlus; + break; + + case OO_Minus: // '-' is either unary or binary + if (NumArgs == 1) + goto UnaryMinus; + else + goto BinaryMinus; + break; + + case OO_Amp: // '&' is either unary or binary + if (NumArgs == 1) + goto UnaryAmp; + else + goto BinaryAmp; + + case OO_PlusPlus: + case OO_MinusMinus: + // C++ [over.built]p3: + // + // For every pair (T, VQ), where T is an arithmetic type, and VQ + // is either volatile or empty, there exist candidate operator + // functions of the form + // + // VQ T& operator++(VQ T&); + // T operator++(VQ T&, int); + // + // C++ [over.built]p4: + // + // For every pair (T, VQ), where T is an arithmetic type other + // than bool, and VQ is either volatile or empty, there exist + // candidate operator functions of the form + // + // VQ T& operator--(VQ T&); + // T operator--(VQ T&, int); + for (unsigned Arith = (Op == OO_PlusPlus? 0 : 1); + Arith < NumArithmeticTypes; ++Arith) { + QualType ArithTy = ArithmeticTypes[Arith]; + QualType ParamTypes[2] + = { Context.getReferenceType(ArithTy), Context.IntTy }; + + // Non-volatile version. + if (NumArgs == 1) + AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 1, CandidateSet); + else + AddBuiltinCandidate(ArithTy, ParamTypes, Args, 2, CandidateSet); + + // Volatile version + ParamTypes[0] = Context.getReferenceType(ArithTy.withVolatile()); + if (NumArgs == 1) + AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 1, CandidateSet); + else + AddBuiltinCandidate(ArithTy, ParamTypes, Args, 2, CandidateSet); + } + + // C++ [over.built]p5: + // + // 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&); + // T*VQ& operator--(T*VQ&); + // T* operator++(T*VQ&, int); + // T* operator--(T*VQ&, int); + for (BuiltinCandidateTypeSet::iterator Ptr = CandidateTypes.pointer_begin(); + Ptr != CandidateTypes.pointer_end(); ++Ptr) { + // Skip pointer types that aren't pointers to object types. + if (!(*Ptr)->getAsPointerType()->getPointeeType()->isObjectType()) + continue; + + QualType ParamTypes[2] = { + Context.getReferenceType(*Ptr), Context.IntTy + }; + + // Without volatile + if (NumArgs == 1) + AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 1, CandidateSet); + else + AddBuiltinCandidate(*Ptr, ParamTypes, Args, 2, CandidateSet); + + if (!Context.getCanonicalType(*Ptr).isVolatileQualified()) { + // With volatile + ParamTypes[0] = Context.getReferenceType((*Ptr).withVolatile()); + if (NumArgs == 1) + AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 1, CandidateSet); + else + AddBuiltinCandidate(*Ptr, ParamTypes, Args, 2, CandidateSet); + } + } + break; + + UnaryStar: + // C++ [over.built]p6: + // For every cv-qualified or cv-unqualified object type T, there + // exist candidate operator functions of the form + // + // T& operator*(T*); + // + // C++ [over.built]p7: + // For every function type T, there exist candidate operator + // functions of the form + // T& operator*(T*); + for (BuiltinCandidateTypeSet::iterator Ptr = CandidateTypes.pointer_begin(); + Ptr != CandidateTypes.pointer_end(); ++Ptr) { + QualType ParamTy = *Ptr; + QualType PointeeTy = ParamTy->getAsPointerType()->getPointeeType(); + AddBuiltinCandidate(Context.getReferenceType(PointeeTy), + &ParamTy, Args, 1, CandidateSet); + } + break; + + UnaryPlus: + // C++ [over.built]p8: + // For every type T, there exist candidate operator functions of + // the form + // + // T* operator+(T*); + for (BuiltinCandidateTypeSet::iterator Ptr = CandidateTypes.pointer_begin(); + Ptr != CandidateTypes.pointer_end(); ++Ptr) { + QualType ParamTy = *Ptr; + AddBuiltinCandidate(ParamTy, &ParamTy, Args, 1, CandidateSet); + } + + // Fall through + + UnaryMinus: + // C++ [over.built]p9: + // For every promoted arithmetic type T, there exist candidate + // operator functions of the form + // + // T operator+(T); + // T operator-(T); + for (unsigned Arith = FirstPromotedArithmeticType; + Arith < LastPromotedArithmeticType; ++Arith) { + QualType ArithTy = ArithmeticTypes[Arith]; + AddBuiltinCandidate(ArithTy, &ArithTy, Args, 1, CandidateSet); + } + break; + + case OO_Tilde: + // C++ [over.built]p10: + // For every promoted integral type T, there exist candidate + // operator functions of the form + // + // T operator~(T); + for (unsigned Int = FirstPromotedIntegralType; + Int < LastPromotedIntegralType; ++Int) { + QualType IntTy = ArithmeticTypes[Int]; + AddBuiltinCandidate(IntTy, &IntTy, Args, 1, CandidateSet); + } + 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"); + assert(false && "Special operators don't use AddBuiltinOperatorCandidates"); break; case OO_Comma: + UnaryAmp: + case OO_Arrow: // 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: @@ -2156,8 +2315,8 @@ Sema::AddBuiltinBinaryOperatorCandidates(OverloadedOperatorKind Op, // Fall through. isComparison = true; - case OO_Plus: - case OO_Minus: + BinaryPlus: + BinaryMinus: if (!isComparison) { // We didn't fall through, so we must have OO_Plus or OO_Minus. @@ -2201,8 +2360,8 @@ Sema::AddBuiltinBinaryOperatorCandidates(OverloadedOperatorKind Op, } // Fall through - case OO_Star: case OO_Slash: + BinaryStar: // C++ [over.built]p12: // // For every pair of promoted arithmetic types L and R, there @@ -2235,7 +2394,7 @@ Sema::AddBuiltinBinaryOperatorCandidates(OverloadedOperatorKind Op, break; case OO_Percent: - case OO_Amp: + BinaryAmp: case OO_Caret: case OO_Pipe: case OO_LessLess: @@ -2285,10 +2444,12 @@ Sema::AddBuiltinBinaryOperatorCandidates(OverloadedOperatorKind Op, 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); + if (!Context.getCanonicalType(*Enum).isVolatileQualified()) { + // volatile T& operator=(volatile T&, T) + ParamTypes[0] = Context.getReferenceType((*Enum).withVolatile()); + ParamTypes[1] = *Enum; + AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet); + } } // Fall through. @@ -2319,9 +2480,11 @@ Sema::AddBuiltinBinaryOperatorCandidates(OverloadedOperatorKind Op, 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); + if (!Context.getCanonicalType(*Ptr).isVolatileQualified()) { + // volatile version + ParamTypes[0] = Context.getReferenceType((*Ptr).withVolatile()); + AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet); + } } // Fall through. @@ -2396,13 +2559,26 @@ Sema::AddBuiltinBinaryOperatorCandidates(OverloadedOperatorKind Op, } break; + case OO_Exclaim: { + // C++ [over.operator]p23: + // + // There also exist candidate operator functions of the form + // + // bool operator!(bool); + // bool operator&&(bool, bool); [BELOW] + // bool operator||(bool, bool); [BELOW] + QualType ParamTy = Context.BoolTy; + AddBuiltinCandidate(ParamTy, &ParamTy, Args, 1, 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); [ABOVE] // bool operator&&(bool, bool); // bool operator||(bool, bool); QualType ParamTypes[2] = { Context.BoolTy, Context.BoolTy }; diff --git a/clang/test/SemaCXX/overloaded-builtin-operators.cpp b/clang/test/SemaCXX/overloaded-builtin-operators.cpp index af328da212bd..29d721c45bbf 100644 --- a/clang/test/SemaCXX/overloaded-builtin-operators.cpp +++ b/clang/test/SemaCXX/overloaded-builtin-operators.cpp @@ -25,6 +25,14 @@ yes& islong(unsigned long); // FIXME: shouldn't be needed no& islong(int); void f(Short s, Long l, Enum1 e1, Enum2 e2) { + // C++ [over.built]p8 + int i1 = +e1; + int i2 = -e2; + + // C++ [over.built]p10: + int i3 = ~s; + bool b1 = !s; + // C++ [over.built]p12 (void)static_cast(islong(s + l)); (void)static_cast(islong(s + s)); @@ -46,6 +54,12 @@ struct LongRef { }; void g(ShortRef sr, LongRef lr) { + // C++ [over.built]p3 + short s1 = sr++; + + // C++ [over.built]p3 + long l1 = lr--; + // C++ [over.built]p18 short& sr1 = (sr *= lr); volatile long& lr1 = (lr *= sr); @@ -65,7 +79,16 @@ struct ConstIntPtr { operator int const *(); }; -void test_with_ptrs(VolatileIntPtr vip, ConstIntPtr cip, ShortRef sr) { +struct VolatileIntPtrRef { + operator int volatile *&(); +}; + +struct ConstIntPtrRef { + operator int const *&(); +}; + +void test_with_ptrs(VolatileIntPtr vip, ConstIntPtr cip, ShortRef sr, + VolatileIntPtrRef vipr, ConstIntPtrRef cipr) { #if 0 // FIXME: Enable these tests once we have operator overloading for // operator[]. @@ -76,4 +99,19 @@ void test_with_ptrs(VolatileIntPtr vip, ConstIntPtr cip, ShortRef sr) { #endif bool b1 = (vip == cip); long p1 = vip - cip; + + // C++ [over.built]p5: + int volatile *vip1 = vipr++; + int const *cip1 = cipr++; + int volatile *&vipr1 = ++vipr; + int const *&cipr1 = --cipr; + + // C++ [over.built]p6: + int volatile &ivr = *vip; + + // C++ [over.built]p8: + int volatile *vip2 = +vip; + int i1 = +sr; + int i2 = -sr; } + diff --git a/clang/test/SemaCXX/overloaded-operator.cpp b/clang/test/SemaCXX/overloaded-operator.cpp index c540c2dafcbb..29486568185d 100644 --- a/clang/test/SemaCXX/overloaded-operator.cpp +++ b/clang/test/SemaCXX/overloaded-operator.cpp @@ -69,3 +69,31 @@ void enum_test(Enum1 enum1, Enum2 enum2, E1 e1, E2 e2) { float &f3 = (e1 == enum2); float &f4 = (enum1 == enum2); // expected-error{{non-const reference to type 'float' cannot be initialized with a temporary of type '_Bool'}} } + + +struct PostInc { + PostInc operator++(int); + PostInc& operator++(); +}; + +struct PostDec { + PostDec operator--(int); + PostDec& operator--(); +}; + +void incdec_test(PostInc pi, PostDec pd) { + const PostInc& pi1 = pi++; + const PostDec& pd1 = pd--; + PostInc &pi2 = ++pi; + PostDec &pd2 = --pd; +} + +struct SmartPtr { + int& operator*(); + // FIXME: spurious error: long& operator*() const; +}; + +void test_smartptr(SmartPtr ptr, const SmartPtr cptr) { + int &ir = *ptr; + // FIXME: reinstate long &lr = *cptr; +} diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index c8b8d57ef0cf..7c51fb387d05 100644 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -826,8 +826,8 @@ welcome!

      13.3.1.2 [over.match.oper] ✓ - - + + @@ -943,27 +943,27 @@ welcome!

- Most overloaded operators can only be called with function syntax, e.g., operator+(x). + Some overloaded operators can only be called with function syntax, e.g., operator[](x).     13.5.1 [over.unary] - + N/A + -     13.5.2 [over.binary] - + N/A + -     13.5.3 [over.ass] - + N/A @@ -971,7 +971,7 @@ welcome!

    13.5.4 [over.call] - + N/A @@ -979,7 +979,7 @@ welcome!

    13.5.5 [over.sub] - + N/A @@ -987,21 +987,29 @@ welcome!

    13.5.6 [over.ref] - + N/A + -     13.5.7 [over.inc] - + N/A + - -  13.6 [over.built] + +   13.6 [over.built] + N/A + + + + Missing pointer-to-member versions (p11, p16) and support for + the ternary operator (p24, p25). + 14 [temp]   14.1 [temp.param]   14.2 [temp.names]