From: Douglas Gregor Date: Thu, 6 Nov 2008 22:13:31 +0000 (+0000) Subject: Parsing, ASTs, and semantic analysis for the declaration of overloaded X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=1cd1b1e987f5e2f060d7972b13d83239b36d77d6;p=clang Parsing, ASTs, and semantic analysis for the declaration of overloaded operators in C++. Overloaded operators can be called directly via their operator-function-ids, e.g., "operator+(foo, bar)", but we don't yet implement the semantics of operator overloading to handle, e.g., "foo + bar". git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@58817 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index e23b4d8419..6348ce66e2 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -14,6 +14,7 @@ #ifndef LLVM_CLANG_AST_DECL_H #define LLVM_CLANG_AST_DECL_H +#include "clang/Basic/OperatorKinds.h" #include "clang/AST/DeclBase.h" #include "clang/Parse/AccessSpecifier.h" #include "llvm/ADT/SmallVector.h" @@ -542,6 +543,14 @@ public: StorageClass getStorageClass() const { return StorageClass(SClass); } bool isInline() const { return IsInline; } + /// isOverloadedOperator - Whether this function declaration + /// represents an C++ overloaded operator, e.g., "operator+". + bool isOverloadedOperator() const { + return getOverloadedOperator() != OO_None; + }; + + OverloadedOperatorKind getOverloadedOperator() const; + // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return D->getKind() >= FunctionFirst && D->getKind() <= FunctionLast; diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index fedee73b9c..e3f183d195 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -1288,6 +1288,28 @@ DIAG(err_duplicate_base_class, ERROR, DIAG(err_ambiguous_derived_to_base_conv, ERROR, "ambiguous conversion from derived class '%0' to base class '%1':%2") +// C++ operator overloading +DIAG(err_expected_operator, ERROR, + "expected 'operator' keyword") +DIAG(err_operator_overload_needs_class_or_enum, ERROR, + "non-member overloaded operator '%0' must have at least one parameter of class or enumeration type (or reference thereof)") +DIAG(err_operator_overload_variadic, ERROR, + "overloaded operator cannot be variadic") +DIAG(err_operator_overload_static, ERROR, + "overloaded operator '%0' cannot be a static member function") +DIAG(err_operator_overload_default_arg, ERROR, + "a parameter of an overloaded operator cannot have a default argument") +DIAG(err_operator_overload_must_be_unary, ERROR, + "overloaded operator '%0' must be a unary operator (has %1 parameter%2)") +DIAG(err_operator_overload_must_be_binary, ERROR, + "overloaded operator '%0' must be a binary operator (has %1 parameter%2)") +DIAG(err_operator_overload_must_be_unary_or_binary, ERROR, + "overloaded operator '%0' must be a unary or binary operator (has %1 parameter%2)") +DIAG(err_operator_overload_must_be_member, ERROR, + "overloaded operator '%0' must be a non-static member function") +DIAG(err_operator_overload_post_incdec_must_be_int, ERROR, + "%0parameter of overloaded post-%1 operator must have type 'int' (not '%2')") + DIAG(warn_not_compound_assign, WARNING, "use of unary operator that may be intended as compound assignment (%0=)") diff --git a/include/clang/Basic/IdentifierTable.h b/include/clang/Basic/IdentifierTable.h index acdf3dae8f..d84ce07d41 100644 --- a/include/clang/Basic/IdentifierTable.h +++ b/include/clang/Basic/IdentifierTable.h @@ -15,6 +15,7 @@ #ifndef LLVM_CLANG_BASIC_IDENTIFIERTABLE_H #define LLVM_CLANG_BASIC_IDENTIFIERTABLE_H +#include "clang/Basic/OperatorKinds.h" #include "clang/Basic/TokenKinds.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/SmallString.h" @@ -49,11 +50,12 @@ class IdentifierInfo { // First NUM_OBJC_KEYWORDS values are for Objective-C, the remaining values // are for builtins. unsigned ObjCOrBuiltinID :10; + unsigned OperatorID : 6; // C++ overloaded operator. bool HasMacro : 1; // True if there is a #define for this. bool IsExtension : 1; // True if identifier is a lang extension. bool IsPoisoned : 1; // True if identifier is poisoned. bool IsCPPOperatorKeyword : 1; // True if ident is a C++ operator keyword. - // 10 bits left in 32-bit word. + // 4 bits left in 32-bit word. void *FETokenInfo; // Managed by the language front-end. IdentifierInfo(const IdentifierInfo&); // NONCOPYABLE. void operator=(const IdentifierInfo&); // NONASSIGNABLE. @@ -120,6 +122,16 @@ public: && "ID too large for field!"); } + /// getOverloadedOperatorID - Get the C++ overloaded operator that + /// corresponds to this identifier. + OverloadedOperatorKind getOverloadedOperatorID() const { + return OverloadedOperatorKind(OperatorID); + } + void setOverloadedOperatorID(OverloadedOperatorKind ID) { + OperatorID = ID; + assert(OperatorID == (unsigned)ID && "ID too large for field!"); + } + /// get/setExtension - Initialize information about whether or not this /// language token is an extension. This controls extension warnings, and is /// only valid if a custom token ID is set. @@ -161,6 +173,11 @@ class IdentifierTable { // BumpPtrAllocator! typedef llvm::StringMap HashTableTy; HashTableTy HashTable; + + /// OverloadedOperators - Identifiers corresponding to each of the + /// overloadable operators in C++. + IdentifierInfo *OverloadedOperators[NUM_OVERLOADED_OPERATORS]; + public: /// IdentifierTable ctor - Create the identifier table, populating it with /// info about the language keywords for the language specified by LangOpts. @@ -180,7 +197,12 @@ public: const char *NameBytes = &Name[0]; return get(NameBytes, NameBytes+Name.size()); } - + + /// getOverloadedOperator - Retrieve the identifier + IdentifierInfo &getOverloadedOperator(OverloadedOperatorKind Op) { + return *OverloadedOperators[Op]; + } + typedef HashTableTy::const_iterator iterator; typedef HashTableTy::const_iterator const_iterator; @@ -194,6 +216,7 @@ public: void PrintStats() const; void AddKeywords(const LangOptions &LangOpts); + void AddOverloadedOperators(); /// Emit - Serialize this IdentifierTable to a bitstream. This should /// be called AFTER objects that externally reference the identifiers in the diff --git a/include/clang/Basic/OperatorKinds.def b/include/clang/Basic/OperatorKinds.def new file mode 100644 index 0000000000..6226172c54 --- /dev/null +++ b/include/clang/Basic/OperatorKinds.def @@ -0,0 +1,68 @@ +//===--- OperatorKinds.def - C++ Overloaded Operator Database ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the OverloadedOperator database, which includes +// all of the overloadable C++ operators. +// +//===----------------------------------------------------------------------===// + +#ifndef OVERLOADED_OPERATOR +# define OVERLOADED_OPERATOR(Name,Spelling,Token) +#endif + +#ifndef OVERLOADED_OPERATOR_MULTI +# define OVERLOADED_OPERATOR_MULTI(Name,Spelling) \ + OVERLOADED_OPERATOR(Name,Spelling,unknown) +#endif + +OVERLOADED_OPERATOR_MULTI(New , "operator new") +OVERLOADED_OPERATOR_MULTI(Delete , "operator delete") +OVERLOADED_OPERATOR_MULTI(Array_New , "operator new[]") +OVERLOADED_OPERATOR_MULTI(Array_Delete , "operator delete[]") +OVERLOADED_OPERATOR(Plus , "operator+" , plus) +OVERLOADED_OPERATOR(Minus , "operator-" , minus) +OVERLOADED_OPERATOR(Star , "operator*" , star) +OVERLOADED_OPERATOR(Slash , "operator/" , slash) +OVERLOADED_OPERATOR(Percent , "operator%" , percent) +OVERLOADED_OPERATOR(Caret , "operator^" , caret) +OVERLOADED_OPERATOR(Amp , "operator&" , amp) +OVERLOADED_OPERATOR(Pipe , "operator|" , pipe) +OVERLOADED_OPERATOR(Tilde , "operator~" , tilde) +OVERLOADED_OPERATOR(Exclaim , "operator!" , exclaim) +OVERLOADED_OPERATOR(Equal , "operator=" , equal) +OVERLOADED_OPERATOR(Less , "operator<" , less) +OVERLOADED_OPERATOR(Greater , "operator>" , greater) +OVERLOADED_OPERATOR(PlusEqual , "operator+=" , plusequal) +OVERLOADED_OPERATOR(MinusEqual , "operator-=" , minusequal) +OVERLOADED_OPERATOR(StarEqual , "operator*=" , starequal) +OVERLOADED_OPERATOR(SlashEqual , "operator/=" , slashequal) +OVERLOADED_OPERATOR(PercentEqual , "operator%=" , percentequal) +OVERLOADED_OPERATOR(CaretEqual , "operator^=" , caretequal) +OVERLOADED_OPERATOR(AmpEqual , "operator&=" , ampequal) +OVERLOADED_OPERATOR(PipeEqual , "operator|=" , pipeequal) +OVERLOADED_OPERATOR(LessLess , "operator<<" , lessless) +OVERLOADED_OPERATOR(GreaterGreater , "operator>>" , greatergreater) +OVERLOADED_OPERATOR(LessLessEqual , "operator<<=" , lesslessequal) +OVERLOADED_OPERATOR(GreaterGreaterEqual , "operator>>=" , greatergreaterequal) +OVERLOADED_OPERATOR(EqualEqual , "operator==" , equalequal) +OVERLOADED_OPERATOR(ExclaimEqual , "operator!=" , exclaimequal) +OVERLOADED_OPERATOR(LessEqual , "operator<=" , lessequal) +OVERLOADED_OPERATOR(GreaterEqual , "operator>=" , greaterequal) +OVERLOADED_OPERATOR(AmpAmp , "operator&&" , ampamp) +OVERLOADED_OPERATOR(PipePipe , "operator||" , pipepipe) +OVERLOADED_OPERATOR(PlusPlus , "operator++" , plusplus) +OVERLOADED_OPERATOR(MinusMinus , "operator--" , minusminus) +OVERLOADED_OPERATOR(Comma , "operator," , comma) +OVERLOADED_OPERATOR(ArrowStar , "operator->*" , arrowstar) +OVERLOADED_OPERATOR(Arrow , "operator->" , arrow) +OVERLOADED_OPERATOR_MULTI(Call , "operator()") +OVERLOADED_OPERATOR_MULTI(Subscript , "operator[]") + +#undef OVERLOADED_OPERATOR_MULTI +#undef OVERLOADED_OPERATOR diff --git a/include/clang/Basic/OperatorKinds.h b/include/clang/Basic/OperatorKinds.h new file mode 100644 index 0000000000..3d085dba75 --- /dev/null +++ b/include/clang/Basic/OperatorKinds.h @@ -0,0 +1,31 @@ +//===--- OperatorKinds.h - C++ Overloaded Operators -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines C++ overloaded operators. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_BASIC_OPERATOR_KINDS_H +#define LLVM_CLANG_BASIC_OPERATOR_KINDS_H + +namespace clang { + +/// OverloadedOperatorKind - Enumeration specifying the different kinds of +/// C++ overloaded operators. +enum OverloadedOperatorKind { + OO_None, //< Not an overloaded operator +#define OVERLOADED_OPERATOR(Name,Spelling,Token) OO_##Name, +#include "clang/Basic/OperatorKinds.def" + NUM_OVERLOADED_OPERATORS +}; + + +} // end namespace clang + +#endif diff --git a/include/clang/Parse/AccessSpecifier.h b/include/clang/Parse/AccessSpecifier.h index a35725ab3f..8d2cee8ea4 100644 --- a/include/clang/Parse/AccessSpecifier.h +++ b/include/clang/Parse/AccessSpecifier.h @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// // -// This file defines interfaces used for Declaration Specifiers and Declarators. +// This file defines interfaces used for C++ access specifiers. // //===----------------------------------------------------------------------===// diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index adcc936216..eb342ce6dc 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -728,6 +728,10 @@ private: void ParseBaseClause(DeclTy *ClassDecl); BaseResult ParseBaseSpecifier(DeclTy *ClassDecl); AccessSpecifier getAccessSpecifierIfPresent() const; + + //===--------------------------------------------------------------------===// + // C++ 13.5: Overloaded operators [over.oper] + IdentifierInfo *MaybeParseOperatorFunctionId(); }; } // end namespace clang 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(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(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; +} diff --git a/test/SemaCXX/overloaded-operator-decl.cpp b/test/SemaCXX/overloaded-operator-decl.cpp new file mode 100644 index 0000000000..b755ebc98e --- /dev/null +++ b/test/SemaCXX/overloaded-operator-decl.cpp @@ -0,0 +1,37 @@ +// RUN: clang -fsyntax-only -verify %s +struct X { + X(); + X(int); +}; + +X operator+(X, X); +X operator-(X, X) { X x; return x; } + +struct Y { + Y operator-() const; + void operator()(int x = 17) const; + int operator[](int); + + static int operator+(Y, Y); // expected-error{{overloaded operator 'operator+' cannot be a static member function}} +}; + + +void f(X x) { + x = operator+(x, x); +} + +X operator+(int, float); // expected-error{{non-member overloaded operator 'operator+' must have at least one parameter of class or enumeration type (or reference thereof)}} + +X operator*(X, X = 5); // expected-error{{a parameter of an overloaded operator cannot have a default argument}} + +X operator/(X, X, ...); // expected-error{{overloaded operator cannot be variadic}} + +X operator%(Y); // expected-error{{overloaded operator 'operator%' must be a binary operator (has 1 parameter)}} + +void operator()(Y&, int, int); // expected-error{{overloaded operator 'operator()' must be a non-static member function}} + +typedef int INT; +typedef float FLOAT; +Y& operator++(Y&); +Y operator++(Y&, INT); +X operator++(X&, FLOAT); // expected-error{{second parameter of overloaded post-increment operator must have type 'int' (not 'float')}} diff --git a/www/cxx_status.html b/www/cxx_status.html index a194f44acf..c53e2c4767 100644 --- a/www/cxx_status.html +++ b/www/cxx_status.html @@ -810,14 +810,70 @@ welcome!

  13.4 [over.over] -  13.5 [over.oper] -    13.5.1 [over.unary] -    13.5.2 [over.binary] -    13.5.3 [over.ass] -    13.5.4 [over.call] -    13.5.5 [over.sub] -    13.5.6 [over.ref] -    13.5.7 [over.inc] + +   13.5 [over.oper] + + + + + Overloaded operators can only be called with function syntax, e.g., operator+(x, y). + + +     13.5.1 [over.unary] + + + + + + + +     13.5.2 [over.binary] + + + + + + + +     13.5.3 [over.ass] + + + + + + + +     13.5.4 [over.call] + + + + + + + +     13.5.5 [over.sub] + + + + + + + +     13.5.6 [over.ref] + + + + + + + +     13.5.7 [over.inc] + + + + + +   13.6 [over.built] 14 [temp]   14.1 [temp.param]