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/lib/AST/Decl.cpp b/lib/AST/Decl.cpp
index 81fe766..b0fe4d1 100644
--- a/lib/AST/Decl.cpp
+++ b/lib/AST/Decl.cpp
@@ -210,6 +210,15 @@
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 8f0387e..e17f48a 100644
--- a/lib/Basic/IdentifierTable.cpp
+++ b/lib/Basic/IdentifierTable.cpp
@@ -28,6 +28,7 @@
IdentifierInfo::IdentifierInfo() {
TokenID = tok::identifier;
ObjCOrBuiltinID = 0;
+ OperatorID = 0;
HasMacro = false;
IsExtension = false;
IsPoisoned = false;
@@ -46,6 +47,7 @@
// 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 @@
#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 e1fbb6d..0b30631 100644
--- a/lib/Parse/ParseDecl.cpp
+++ b/lib/Parse/ParseDecl.cpp
@@ -1305,6 +1305,15 @@
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 b737695..49c28ee 100644
--- a/lib/Parse/ParseExpr.cpp
+++ b/lib/Parse/ParseExpr.cpp
@@ -356,7 +356,8 @@
/// [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 @@
/// 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 @@
}
// 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 @@
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 2b9ab55..fcb229b 100644
--- a/lib/Parse/ParseExprCXX.cpp
+++ b/lib/Parse/ParseExprCXX.cpp
@@ -291,3 +291,78 @@
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 68d2eee..cc2e3f2 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -863,6 +863,12 @@
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 38a5c37..3e4e2c5 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -932,6 +932,11 @@
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 bdc2faf..09f4cbd 100644
--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -1576,3 +1576,242 @@
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;
+}