diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/AST/Decl.cpp | 9 | ||||
-rw-r--r-- | lib/Basic/IdentifierTable.cpp | 11 | ||||
-rw-r--r-- | lib/Parse/ParseDecl.cpp | 9 | ||||
-rw-r--r-- | lib/Parse/ParseExpr.cpp | 25 | ||||
-rw-r--r-- | lib/Parse/ParseExprCXX.cpp | 75 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 6 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 5 | ||||
-rw-r--r-- | lib/Sema/SemaDeclCXX.cpp | 239 |
8 files changed, 378 insertions, 1 deletions
diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 81fe766ff0..b0fe4d1b14 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -210,6 +210,15 @@ unsigned FunctionDecl::getMinRequiredArguments() const { return NumRequiredArgs; } +/// getOverloadedOperator - Which C++ overloaded operator this +/// function represents, if any. +OverloadedOperatorKind FunctionDecl::getOverloadedOperator() const { + if (getIdentifier()) + return getIdentifier()->getOverloadedOperatorID(); + else + return OO_None; +} + //===----------------------------------------------------------------------===// // TagdDecl Implementation //===----------------------------------------------------------------------===// diff --git a/lib/Basic/IdentifierTable.cpp b/lib/Basic/IdentifierTable.cpp index 8f0387e99e..e17f48ad3b 100644 --- a/lib/Basic/IdentifierTable.cpp +++ b/lib/Basic/IdentifierTable.cpp @@ -28,6 +28,7 @@ using namespace clang; IdentifierInfo::IdentifierInfo() { TokenID = tok::identifier; ObjCOrBuiltinID = 0; + OperatorID = 0; HasMacro = false; IsExtension = false; IsPoisoned = false; @@ -46,6 +47,7 @@ IdentifierTable::IdentifierTable(const LangOptions &LangOpts) // Populate the identifier table with info about keywords for the current // language. AddKeywords(LangOpts); + AddOverloadedOperators(); } // This cstor is intended to be used only for serialization. @@ -160,6 +162,15 @@ void IdentifierTable::AddKeywords(const LangOptions &LangOpts) { #include "clang/Basic/TokenKinds.def" } +/// AddOverloadedOperators - Register the name of all C++ overloadable +/// operators ("operator+", "operator[]", etc.) +void IdentifierTable::AddOverloadedOperators() { +#define OVERLOADED_OPERATOR(Name,Spelling,Token) \ + OverloadedOperators[OO_##Name] = &get(Spelling); \ + OverloadedOperators[OO_##Name]->setOverloadedOperatorID(OO_##Name); +#include "clang/Basic/OperatorKinds.def" +} + tok::PPKeywordKind IdentifierInfo::getPPKeywordID() const { // We use a perfect hash function here involving the length of the keyword, // the first and third character. For preprocessor ID's there are no diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index e1fbb6dfca..0b3063148b 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -1305,6 +1305,15 @@ void Parser::ParseDirectDeclarator(Declarator &D) { if (TypeTy *Type = ParseClassName()) D.SetDestructor(Type, II, TildeLoc); + } else if (Tok.is(tok::kw_operator)) { + SourceLocation OperatorLoc = Tok.getLocation(); + + // First try the name of an overloaded operator + if (IdentifierInfo *II = MaybeParseOperatorFunctionId()) { + D.SetIdentifier(II, OperatorLoc); + } else { + // This must be a user-defined conversion. + } } else if (Tok.is(tok::l_paren)) { // direct-declarator: '(' declarator ')' // direct-declarator: '(' attributes declarator ')' diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index b7376954d9..49c28eec75 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -356,7 +356,8 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, unsigned MinPrec) { /// [GNU] '__extension__' '__real' '__imag' /// /// primary-expression: [C99 6.5.1] -/// identifier +/// [C99] identifier +// [C++] id-expression /// constant /// string-literal /// [C++] boolean-literal [C++ 2.13.5] @@ -390,6 +391,16 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, unsigned MinPrec) { /// enumeration-constant -> identifier /// character-constant /// +/// id-expression: [C++ 5.1] +/// unqualified-id +/// qualified-id [TODO] +/// +/// unqualified-id: [C++ 5.1] +/// identifier +/// operator-function-id +/// conversion-function-id [TODO] +/// '~' class-name [TODO] +/// template-id [TODO] Parser::ExprResult Parser::ParseCastExpression(bool isUnaryExpression) { ExprResult Res; tok::TokenKind SavedKind = Tok.getKind(); @@ -461,6 +472,7 @@ Parser::ExprResult Parser::ParseCastExpression(bool isUnaryExpression) { } // primary-expression: identifier + // unqualified-id: identifier // constant: enumeration-constant // Consume the identifier so that we can see if it is followed by a '('. @@ -589,6 +601,17 @@ Parser::ExprResult Parser::ParseCastExpression(bool isUnaryExpression) { return ParsePostfixExpressionSuffix(Res); } + case tok::kw_operator: { + SourceLocation OperatorLoc = Tok.getLocation(); + if (IdentifierInfo *II = MaybeParseOperatorFunctionId()) { + Res = Actions.ActOnIdentifierExpr(CurScope, OperatorLoc, *II, + Tok.is(tok::l_paren)); + // These can be followed by postfix-expr pieces. + return ParsePostfixExpressionSuffix(Res); + } + break; + } + case tok::at: { SourceLocation AtLoc = ConsumeToken(); return ParseObjCAtExpression(AtLoc); diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index 2b9ab55032..fcb229b362 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -291,3 +291,78 @@ void Parser::ParseCXXSimpleTypeSpecifier(DeclSpec &DS) { ConsumeToken(); DS.Finish(Diags, PP.getSourceManager(), getLang()); } + +/// MaybeParseOperatorFunctionId - Attempts to parse a C++ overloaded +/// operator name (C++ [over.oper]). If successful, returns the +/// predefined identifier that corresponds to that overloaded +/// operator. Otherwise, returns NULL and does not consume any tokens. +/// +/// operator-function-id: [C++ 13.5] +/// 'operator' operator +/// +/// operator: one of +/// new delete new[] delete[] +/// + - * / % ^ & | ~ +/// ! = < > += -= *= /= %= +/// ^= &= |= << >> >>= <<= == != +/// <= >= && || ++ -- , ->* -> +/// () [] +IdentifierInfo *Parser::MaybeParseOperatorFunctionId() { + if (Tok.isNot(tok::kw_operator)) + return 0; + + OverloadedOperatorKind Op = OO_None; + switch (NextToken().getKind()) { + case tok::kw_new: + ConsumeToken(); // 'operator' + ConsumeToken(); // 'new' + if (Tok.is(tok::l_square)) { + ConsumeBracket(); // '[' + ExpectAndConsume(tok::r_square, diag::err_expected_rsquare); // ']' + Op = OO_Array_New; + } else { + Op = OO_New; + } + return &PP.getIdentifierTable().getOverloadedOperator(Op); + + case tok::kw_delete: + ConsumeToken(); // 'operator' + ConsumeToken(); // 'delete' + if (Tok.is(tok::l_square)) { + ConsumeBracket(); // '[' + ExpectAndConsume(tok::r_square, diag::err_expected_rsquare); // ']' + Op = OO_Array_Delete; + } else { + Op = OO_Delete; + } + return &PP.getIdentifierTable().getOverloadedOperator(Op); + +#define OVERLOADED_OPERATOR(Name,Spelling,Token) \ + case tok::Token: Op = OO_##Name; break; +#define OVERLOADED_OPERATOR_MULTI(Name,Spelling) +#include "clang/Basic/OperatorKinds.def" + + case tok::l_paren: + ConsumeToken(); // 'operator' + ConsumeParen(); // '(' + ExpectAndConsume(tok::r_paren, diag::err_expected_rparen); // ')' + return &PP.getIdentifierTable().getOverloadedOperator(OO_Call); + + case tok::l_square: + ConsumeToken(); // 'operator' + ConsumeBracket(); // '[' + ExpectAndConsume(tok::r_square, diag::err_expected_rsquare); // ']' + return &PP.getIdentifierTable().getOverloadedOperator(OO_Subscript); + + default: + break; + } + + if (Op == OO_None) + return 0; + else { + ExpectAndConsume(tok::kw_operator, diag::err_expected_operator); + ConsumeAnyToken(); // the operator itself + return &PP.getIdentifierTable().getOverloadedOperator(Op); + } +} diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 68d2eeefc5..cc2e3f2394 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -863,6 +863,12 @@ public: bool CheckDerivedToBaseConversion(QualType Derived, QualType Base, SourceLocation Loc, SourceRange Range); + //===--------------------------------------------------------------------===// + // C++ Overloaded Operators [C++ 13.5] + // + + bool CheckOverloadedOperatorDeclaration(FunctionDecl *FnDecl); + // Objective-C declarations. virtual DeclTy *ActOnStartClassInterface(SourceLocation AtInterfaceLoc, IdentifierInfo *ClassName, diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 38a5c3780b..3e4e2c5b2c 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -932,6 +932,11 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) { else if (CXXDestructorDecl *Destructor = dyn_cast<CXXDestructorDecl>(NewFD)) return ActOnDestructorDeclarator(Destructor); + // Extra checking for C++ overloaded operators (C++ [over.oper]). + if (NewFD->isOverloadedOperator() && + CheckOverloadedOperatorDeclaration(NewFD)) + NewFD->setInvalidDecl(); + // Merge the decl with the existing one if appropriate. Since C functions // are in a flat namespace, make sure we consider decls in outer scopes. if (PrevDecl && diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index bdc2fafce8..09f4cbd2a6 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -1576,3 +1576,242 @@ Sema::CheckReferenceInit(Expr *&Init, QualType &DeclType, return PerformImplicitConversion(Init, T1); } } + +/// CheckOverloadedOperatorDeclaration - Check whether the declaration +/// of this overloaded operator is well-formed. If so, returns false; +/// otherwise, emits appropriate diagnostics and returns true. +bool Sema::CheckOverloadedOperatorDeclaration(FunctionDecl *FnDecl) { + assert(FnDecl && FnDecl->getOverloadedOperator() != OO_None && + "Expected an overloaded operator declaration"); + + bool IsInvalid = false; + + OverloadedOperatorKind Op = FnDecl->getOverloadedOperator(); + + // C++ [over.oper]p5: + // The allocation and deallocation functions, operator new, + // operator new[], operator delete and operator delete[], are + // described completely in 3.7.3. The attributes and restrictions + // found in the rest of this subclause do not apply to them unless + // explicitly stated in 3.7.3. + // FIXME: Write a separate routine for checking this. For now, just + // allow it. + if (Op == OO_New || Op == OO_Array_New || + Op == OO_Delete || Op == OO_Array_Delete) + return false; + + // C++ [over.oper]p6: + // An operator function shall either be a non-static member + // function or be a non-member function and have at least one + // parameter whose type is a class, a reference to a class, an + // enumeration, or a reference to an enumeration. + CXXMethodDecl *MethodDecl = dyn_cast<CXXMethodDecl>(FnDecl); + if (MethodDecl) { + if (MethodDecl->isStatic()) { + Diag(FnDecl->getLocation(), + diag::err_operator_overload_static, + FnDecl->getName(), + SourceRange(FnDecl->getLocation())); + IsInvalid = true; + + // Pretend this isn't a member function; it'll supress + // additional, unnecessary error messages. + MethodDecl = 0; + } + } else { + bool ClassOrEnumParam = false; + for (FunctionDecl::param_iterator Param = FnDecl->param_begin(); + Param != FnDecl->param_end(); ++Param) { + QualType ParamType = (*Param)->getType(); + if (const ReferenceType *RefType = ParamType->getAsReferenceType()) + ParamType = RefType->getPointeeType(); + if (ParamType->isRecordType() || ParamType->isEnumeralType()) { + ClassOrEnumParam = true; + break; + } + } + + if (!ClassOrEnumParam) { + Diag(FnDecl->getLocation(), + diag::err_operator_overload_needs_class_or_enum, + FnDecl->getName(), + SourceRange(FnDecl->getLocation())); + IsInvalid = true; + } + } + + // C++ [over.oper]p8: + // An operator function cannot have default arguments (8.3.6), + // except where explicitly stated below. + // + // Only the function-call operator allows default arguments + // (C++ [over.call]p1). + if (Op != OO_Call) { + for (FunctionDecl::param_iterator Param = FnDecl->param_begin(); + Param != FnDecl->param_end(); ++Param) { + if (Expr *DefArg = (*Param)->getDefaultArg()) { + Diag((*Param)->getLocation(), + diag::err_operator_overload_default_arg, + DefArg->getSourceRange()); + IsInvalid = true; + } + } + } + + bool CanBeUnaryOperator = false; + bool CanBeBinaryOperator = false; + bool MustBeMemberOperator = false; + + switch (Op) { + case OO_New: + case OO_Delete: + case OO_Array_New: + case OO_Array_Delete: + assert(false && "Operators new, new[], delete, and delete[] handled above"); + return true; + + // Unary-only operators + case OO_Arrow: + MustBeMemberOperator = true; + // Fall through + + case OO_Tilde: + case OO_Exclaim: + CanBeUnaryOperator = true; + break; + + // Binary-only operators + case OO_Equal: + case OO_Subscript: + MustBeMemberOperator = true; + // Fall through + + case OO_Slash: + case OO_Percent: + case OO_Caret: + case OO_Pipe: + case OO_Less: + case OO_Greater: + case OO_PlusEqual: + case OO_MinusEqual: + case OO_StarEqual: + case OO_SlashEqual: + case OO_PercentEqual: + case OO_CaretEqual: + case OO_AmpEqual: + case OO_PipeEqual: + case OO_LessLess: + case OO_GreaterGreater: + case OO_LessLessEqual: + case OO_GreaterGreaterEqual: + case OO_EqualEqual: + case OO_ExclaimEqual: + case OO_LessEqual: + case OO_GreaterEqual: + case OO_AmpAmp: + case OO_PipePipe: + case OO_Comma: + CanBeBinaryOperator = true; + break; + + // Unary or binary operators + case OO_Amp: + case OO_Plus: + case OO_Minus: + case OO_Star: + case OO_PlusPlus: + case OO_MinusMinus: + case OO_ArrowStar: + CanBeUnaryOperator = true; + CanBeBinaryOperator = true; + break; + + case OO_Call: + MustBeMemberOperator = true; + break; + + case OO_None: + case NUM_OVERLOADED_OPERATORS: + assert(false && "Not an overloaded operator!"); + return true; + } + + // C++ [over.oper]p8: + // [...] Operator functions cannot have more or fewer parameters + // than the number required for the corresponding operator, as + // described in the rest of this subclause. + unsigned NumParams = FnDecl->getNumParams() + (MethodDecl? 1 : 0); + if (Op != OO_Call && + ((NumParams == 1 && !CanBeUnaryOperator) || + (NumParams == 2 && !CanBeBinaryOperator) || + (NumParams < 1) || (NumParams > 2))) { + // We have the wrong number of parameters. + std::string NumParamsStr = (llvm::APSInt(32) = NumParams).toString(10); + std::string NumParamsPlural; + if (NumParams != 1) + NumParamsPlural = "s"; + + diag::kind DK; + + if (CanBeUnaryOperator && CanBeBinaryOperator) + DK = diag::err_operator_overload_must_be_unary_or_binary; + else if (CanBeUnaryOperator) + DK = diag::err_operator_overload_must_be_unary; + else if (CanBeBinaryOperator) + DK = diag::err_operator_overload_must_be_binary; + else + assert(false && "All non-call overloaded operators are unary or binary!"); + + Diag(FnDecl->getLocation(), DK, + FnDecl->getName(), NumParamsStr, NumParamsPlural, + SourceRange(FnDecl->getLocation())); + IsInvalid = true; + } + + // Overloaded operators cannot be variadic. + if (FnDecl->getType()->getAsFunctionTypeProto()->isVariadic()) { + Diag(FnDecl->getLocation(), + diag::err_operator_overload_variadic, + SourceRange(FnDecl->getLocation())); + IsInvalid = true; + } + + // Some operators must be non-static member functions. + if (MustBeMemberOperator && !MethodDecl) { + Diag(FnDecl->getLocation(), + diag::err_operator_overload_must_be_member, + FnDecl->getName(), + SourceRange(FnDecl->getLocation())); + IsInvalid = true; + } + + // C++ [over.inc]p1: + // The user-defined function called operator++ implements the + // prefix and postfix ++ operator. If this function is a member + // function with no parameters, or a non-member function with one + // parameter of class or enumeration type, it defines the prefix + // increment operator ++ for objects of that type. 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. + if ((Op == OO_PlusPlus || Op == OO_MinusMinus) && NumParams == 2) { + ParmVarDecl *LastParam = FnDecl->getParamDecl(FnDecl->getNumParams() - 1); + bool ParamIsInt = false; + if (const BuiltinType *BT = LastParam->getType()->getAsBuiltinType()) + ParamIsInt = BT->getKind() == BuiltinType::Int; + + if (!ParamIsInt) { + Diag(LastParam->getLocation(), + diag::err_operator_overload_post_incdec_must_be_int, + MethodDecl? std::string() : std::string("second "), + (Op == OO_PlusPlus)? std::string("increment") + : std::string("decrement"), + Context.getCanonicalType(LastParam->getType()).getAsString(), + SourceRange(FnDecl->getLocation())); + IsInvalid = true; + } + } + + return IsInvalid; +} |