diff options
author | Douglas Gregor <dgregor@apple.com> | 2008-11-06 23:29:22 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2008-11-06 23:29:22 +0000 |
commit | eaebc75ef6ff21fbc9f25ab4175cba465e4e0e43 (patch) | |
tree | 519fb271b86ee36b26d943a7011d0697ffdbaec1 /lib | |
parent | 1cd1b1e987f5e2f060d7972b13d83239b36d77d6 (diff) |
Initial, rudimentary implementation of operator overloading for binary
operators. For example, one can now write "x + y" where x or y is a
class or enumeration type, and Clang will perform overload resolution
for "+" based on the overloaded operators it finds.
The other kinds of overloadable operators in C++ will follow this same
approach.
Three major issues remain:
1) We don't find member operators
2) Since we don't have user-defined conversion operators, we can't
call any of the built-in overloaded operators in C++ [over.built].
3) Once we've done the semantic checks, we drop the overloaded
operator on the floor; it doesn't get into the AST at all.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@58821 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Parse/ParseExpr.cpp | 4 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 8 | ||||
-rw-r--r-- | lib/Sema/SemaExpr.cpp | 178 |
3 files changed, 151 insertions, 39 deletions
diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 49c28eec75..4515e4b4e9 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -319,8 +319,8 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, unsigned MinPrec) { if (!LHS.isInvalid) { // Combine the LHS and RHS into the LHS (e.g. build AST). if (TernaryMiddle.isInvalid) - LHS = Actions.ActOnBinOp(OpToken.getLocation(), OpToken.getKind(), - LHS.Val, RHS.Val); + LHS = Actions.ActOnBinOp(CurScope, OpToken.getLocation(), + OpToken.getKind(), LHS.Val, RHS.Val); else LHS = Actions.ActOnConditionalOp(OpToken.getLocation(), ColonLoc, LHS.Val, TernaryMiddle.Val, RHS.Val); diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index cc2e3f2394..f9dd25788c 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -630,9 +630,13 @@ public: InitListDesignations &Designators, SourceLocation RParenLoc); - virtual ExprResult ActOnBinOp(SourceLocation TokLoc, tok::TokenKind Kind, + virtual ExprResult ActOnBinOp(Scope *S, SourceLocation TokLoc, + tok::TokenKind Kind, ExprTy *LHS,ExprTy *RHS); - + ExprResult CreateBuiltinBinOp(SourceLocation TokLoc, + unsigned Opc, + Expr *lhs, Expr *rhs); + /// ActOnConditionalOp - Parse a ?: operation. Note that 'LHS' may be null /// in the case of a the GNU conditional expr extension. virtual ExprResult ActOnConditionalOp(SourceLocation QuestionLoc, diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 80ecf3312a..d3650c8ae1 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -2626,104 +2626,212 @@ static inline UnaryOperator::Opcode ConvertTokenKindToUnaryOpcode( return Opc; } -// Binary Operators. 'Tok' is the token for the operator. -Action::ExprResult Sema::ActOnBinOp(SourceLocation TokLoc, tok::TokenKind Kind, - ExprTy *LHS, ExprTy *RHS) { - BinaryOperator::Opcode Opc = ConvertTokenKindToBinaryOpcode(Kind); - Expr *lhs = (Expr *)LHS, *rhs = (Expr*)RHS; - - assert((lhs != 0) && "ActOnBinOp(): missing left expression"); - assert((rhs != 0) && "ActOnBinOp(): missing right expression"); - +/// CreateBuiltinBinOp - Creates a new built-in binary operation with +/// operator @p Opc at location @c TokLoc. This routine only supports +/// built-in operations; ActOnBinOp handles overloaded operators. +Action::ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc, + unsigned Op, + Expr *lhs, Expr *rhs) { QualType ResultTy; // Result type of the binary operator. QualType CompTy; // Computation type for compound assignments (e.g. '+=') - + BinaryOperator::Opcode Opc = (BinaryOperator::Opcode)Op; + switch (Opc) { default: assert(0 && "Unknown binary expr!"); case BinaryOperator::Assign: - ResultTy = CheckAssignmentOperands(lhs, rhs, TokLoc, QualType()); + ResultTy = CheckAssignmentOperands(lhs, rhs, OpLoc, QualType()); break; case BinaryOperator::Mul: case BinaryOperator::Div: - ResultTy = CheckMultiplyDivideOperands(lhs, rhs, TokLoc); + ResultTy = CheckMultiplyDivideOperands(lhs, rhs, OpLoc); break; case BinaryOperator::Rem: - ResultTy = CheckRemainderOperands(lhs, rhs, TokLoc); + ResultTy = CheckRemainderOperands(lhs, rhs, OpLoc); break; case BinaryOperator::Add: - ResultTy = CheckAdditionOperands(lhs, rhs, TokLoc); + ResultTy = CheckAdditionOperands(lhs, rhs, OpLoc); break; case BinaryOperator::Sub: - ResultTy = CheckSubtractionOperands(lhs, rhs, TokLoc); + ResultTy = CheckSubtractionOperands(lhs, rhs, OpLoc); break; case BinaryOperator::Shl: case BinaryOperator::Shr: - ResultTy = CheckShiftOperands(lhs, rhs, TokLoc); + ResultTy = CheckShiftOperands(lhs, rhs, OpLoc); break; case BinaryOperator::LE: case BinaryOperator::LT: case BinaryOperator::GE: case BinaryOperator::GT: - ResultTy = CheckCompareOperands(lhs, rhs, TokLoc, true); + ResultTy = CheckCompareOperands(lhs, rhs, OpLoc, true); break; case BinaryOperator::EQ: case BinaryOperator::NE: - ResultTy = CheckCompareOperands(lhs, rhs, TokLoc, false); + ResultTy = CheckCompareOperands(lhs, rhs, OpLoc, false); break; case BinaryOperator::And: case BinaryOperator::Xor: case BinaryOperator::Or: - ResultTy = CheckBitwiseOperands(lhs, rhs, TokLoc); + ResultTy = CheckBitwiseOperands(lhs, rhs, OpLoc); break; case BinaryOperator::LAnd: case BinaryOperator::LOr: - ResultTy = CheckLogicalOperands(lhs, rhs, TokLoc); + ResultTy = CheckLogicalOperands(lhs, rhs, OpLoc); break; case BinaryOperator::MulAssign: case BinaryOperator::DivAssign: - CompTy = CheckMultiplyDivideOperands(lhs, rhs, TokLoc, true); + CompTy = CheckMultiplyDivideOperands(lhs, rhs, OpLoc, true); if (!CompTy.isNull()) - ResultTy = CheckAssignmentOperands(lhs, rhs, TokLoc, CompTy); + ResultTy = CheckAssignmentOperands(lhs, rhs, OpLoc, CompTy); break; case BinaryOperator::RemAssign: - CompTy = CheckRemainderOperands(lhs, rhs, TokLoc, true); + CompTy = CheckRemainderOperands(lhs, rhs, OpLoc, true); if (!CompTy.isNull()) - ResultTy = CheckAssignmentOperands(lhs, rhs, TokLoc, CompTy); + ResultTy = CheckAssignmentOperands(lhs, rhs, OpLoc, CompTy); break; case BinaryOperator::AddAssign: - CompTy = CheckAdditionOperands(lhs, rhs, TokLoc, true); + CompTy = CheckAdditionOperands(lhs, rhs, OpLoc, true); if (!CompTy.isNull()) - ResultTy = CheckAssignmentOperands(lhs, rhs, TokLoc, CompTy); + ResultTy = CheckAssignmentOperands(lhs, rhs, OpLoc, CompTy); break; case BinaryOperator::SubAssign: - CompTy = CheckSubtractionOperands(lhs, rhs, TokLoc, true); + CompTy = CheckSubtractionOperands(lhs, rhs, OpLoc, true); if (!CompTy.isNull()) - ResultTy = CheckAssignmentOperands(lhs, rhs, TokLoc, CompTy); + ResultTy = CheckAssignmentOperands(lhs, rhs, OpLoc, CompTy); break; case BinaryOperator::ShlAssign: case BinaryOperator::ShrAssign: - CompTy = CheckShiftOperands(lhs, rhs, TokLoc, true); + CompTy = CheckShiftOperands(lhs, rhs, OpLoc, true); if (!CompTy.isNull()) - ResultTy = CheckAssignmentOperands(lhs, rhs, TokLoc, CompTy); + ResultTy = CheckAssignmentOperands(lhs, rhs, OpLoc, CompTy); break; case BinaryOperator::AndAssign: case BinaryOperator::XorAssign: case BinaryOperator::OrAssign: - CompTy = CheckBitwiseOperands(lhs, rhs, TokLoc, true); + CompTy = CheckBitwiseOperands(lhs, rhs, OpLoc, true); if (!CompTy.isNull()) - ResultTy = CheckAssignmentOperands(lhs, rhs, TokLoc, CompTy); + ResultTy = CheckAssignmentOperands(lhs, rhs, OpLoc, CompTy); break; case BinaryOperator::Comma: - ResultTy = CheckCommaOperands(lhs, rhs, TokLoc); + ResultTy = CheckCommaOperands(lhs, rhs, OpLoc); break; } if (ResultTy.isNull()) return true; if (CompTy.isNull()) - return new BinaryOperator(lhs, rhs, Opc, ResultTy, TokLoc); + return new BinaryOperator(lhs, rhs, Opc, ResultTy, OpLoc); else - return new CompoundAssignOperator(lhs, rhs, Opc, ResultTy, CompTy, TokLoc); + return new CompoundAssignOperator(lhs, rhs, Opc, ResultTy, CompTy, OpLoc); +} + +// Binary Operators. 'Tok' is the token for the operator. +Action::ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc, + tok::TokenKind Kind, + ExprTy *LHS, ExprTy *RHS) { + BinaryOperator::Opcode Opc = ConvertTokenKindToBinaryOpcode(Kind); + Expr *lhs = (Expr *)LHS, *rhs = (Expr*)RHS; + + assert((lhs != 0) && "ActOnBinOp(): missing left expression"); + assert((rhs != 0) && "ActOnBinOp(): missing right expression"); + + if (getLangOptions().CPlusPlus && + (lhs->getType()->isRecordType() || lhs->getType()->isEnumeralType() || + rhs->getType()->isRecordType() || rhs->getType()->isEnumeralType())) { + // 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[] = { + OO_Star, OO_Slash, OO_Percent, + OO_Plus, OO_Minus, + OO_LessLess, OO_GreaterGreater, + OO_Less, OO_Greater, OO_LessEqual, OO_GreaterEqual, + OO_EqualEqual, OO_ExclaimEqual, + OO_Amp, + OO_Caret, + OO_Pipe, + OO_AmpAmp, + OO_PipePipe, + OO_Equal, OO_StarEqual, + OO_SlashEqual, OO_PercentEqual, + OO_PlusEqual, OO_MinusEqual, + OO_LessLessEqual, OO_GreaterGreaterEqual, + OO_AmpEqual, OO_CaretEqual, + OO_PipeEqual, + OO_Comma + }; + OverloadedOperatorKind OverOp = OverOps[Opc]; + + // Lookup this operator. + Decl *D = LookupDecl(&PP.getIdentifierTable().getOverloadedOperator(OverOp), + Decl::IDNS_Ordinary, S); + + // Add any overloaded operators we find to the overload set. + Expr *Args[2] = { lhs, rhs }; + if (FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D)) + AddOverloadCandidate(FD, Args, 2, CandidateSet); + else if (OverloadedFunctionDecl *Ovl + = dyn_cast_or_null<OverloadedFunctionDecl>(D)) + AddOverloadCandidates(Ovl, Args, 2, CandidateSet); + + // FIXME: Add builtin overload candidates (C++ [over.built]). + + // Perform overload resolution. + OverloadCandidateSet::iterator Best; + switch (BestViableFunction(CandidateSet, Best)) { + case OR_Success: { + // FIXME: We might find a built-in candidate here. + 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; + + // 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); + } + + case OR_No_Viable_Function: + // No viable function; fall through to handling this as a + // built-in operator. + break; + + case OR_Ambiguous: + Diag(TokLoc, + diag::err_ovl_ambiguous_oper, + BinaryOperator::getOpcodeStr(Opc), + lhs->getSourceRange(), rhs->getSourceRange()); + PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/true); + return true; + } + + // There was no viable overloaded operator; fall through. + } + + + // Build a built-in binary operation. + return CreateBuiltinBinOp(TokLoc, Opc, lhs, rhs); } // Unary Operators. 'Tok' is the token for the operator. |