From: Douglas Gregor Date: Thu, 6 Nov 2008 23:29:22 +0000 (+0000) Subject: Initial, rudimentary implementation of operator overloading for binary X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=eaebc75ef6ff21fbc9f25ab4175cba465e4e0e43;p=clang 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 --- diff --git a/Driver/PrintParserCallbacks.cpp b/Driver/PrintParserCallbacks.cpp index 43dbf341e9..996051a21c 100644 --- a/Driver/PrintParserCallbacks.cpp +++ b/Driver/PrintParserCallbacks.cpp @@ -496,7 +496,8 @@ namespace { return 0; } - virtual ExprResult ActOnBinOp(SourceLocation TokLoc, tok::TokenKind Kind, + virtual ExprResult ActOnBinOp(Scope *S, SourceLocation TokLoc, + tok::TokenKind Kind, ExprTy *LHS, ExprTy *RHS) { llvm::cout << __FUNCTION__ << "\n"; return 0; diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index e3f183d195..b091e2062e 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -869,6 +869,8 @@ DIAG(err_ovl_no_viable_function_in_init_with_cands, ERROR, "no matching constructor for initialization of '%0'; candidates are:") DIAG(err_ovl_ambiguous_init, ERROR, "call to constructor of '%0' is ambiguous; candidates are:") +DIAG(err_ovl_ambiguous_oper, ERROR, + "use of overloaded operator '%0' is ambiguous; candidates are:") DIAG(err_unexpected_typedef, ERROR, "unexpected type name '%0': expected expression") diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index 345f186090..423150ff76 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -482,7 +482,8 @@ public: return 0; } - virtual ExprResult ActOnBinOp(SourceLocation TokLoc, tok::TokenKind Kind, + virtual ExprResult ActOnBinOp(Scope *S, SourceLocation TokLoc, + tok::TokenKind Kind, ExprTy *LHS, ExprTy *RHS) { return 0; } 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(D)) + AddOverloadCandidate(FD, Args, 2, CandidateSet); + else if (OverloadedFunctionDecl *Ovl + = dyn_cast_or_null(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. diff --git a/test/SemaCXX/overloaded-operator.cpp b/test/SemaCXX/overloaded-operator.cpp new file mode 100644 index 0000000000..52d31c0ba0 --- /dev/null +++ b/test/SemaCXX/overloaded-operator.cpp @@ -0,0 +1,30 @@ +// RUN: clang -fsyntax-only -verify %s +class X { }; + +X operator+(X, X); + +void f(X x) { + x = x + x; +} + +struct Y; +struct Z; + +struct Y { + Y(const Z&); +}; + +struct Z { + Z(const Y&); +}; + +Y operator+(Y, Y); +bool operator-(Y, Y); // expected-note{{candidate function}} +bool operator-(Z, Z); // expected-note{{candidate function}} + +void g(Y y, Z z) { + y = y + z; + bool b = y - z; // expected-error{{use of overloaded operator '-' is ambiguous; candidates are:}} +} + + diff --git a/www/cxx_status.html b/www/cxx_status.html index c53e2c4767..5d7ee88be7 100644 --- a/www/cxx_status.html +++ b/www/cxx_status.html @@ -816,7 +816,7 @@ welcome!

- Overloaded operators can only be called with function syntax, e.g., operator+(x, y). + Most overloaded operators can only be called with function syntax, e.g., operator+(x).     13.5.1 [over.unary] @@ -830,7 +830,7 @@ welcome!

    13.5.2 [over.binary] - +