From 6aa14d832704ae176c92d4e0f22dfb3f3d83a70a Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Wed, 21 Apr 2010 22:36:40 +0000 Subject: [PATCH] Implement parsing for message sends in Objective-C++. Message sends in Objective-C++ have a more complex grammar than in Objective-C (surprise!), because (1) The receiver of an instance message can be a qualified name such as ::I or identity::type. (2) Expressions in C++ can start with a type. The receiver grammar isn't actually ambiguous; it just takes a bit of work to parse past the type before deciding whether we have a type or expression. We do this in two places within the grammar: once for message sends and once when we're determining whether a []'d clause in an initializer list is a message send or a C99 designated initializer. This implementation of Objective-C++ message sends contains one known extension beyond GCC's implementation, which is to permit a typename-specifier as the receiver type for a class message, e.g., [typename compute_receiver_type::type method]; Note that the same effect can be achieved in GCC by way of a typedef, e.g., typedef typename computed_receiver_type::type Computed; [Computed method]; so this is merely a convenience. Note also that message sends still cannot involve dependent types or values. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@102031 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Parse/Parser.h | 32 +++++++++- lib/Parse/ParseExpr.cpp | 37 +++-------- lib/Parse/ParseExprCXX.cpp | 31 ++++++++++ lib/Parse/ParseInit.cpp | 92 +++++++++++++++++++--------- lib/Parse/ParseObjc.cpp | 115 ++++++++++++++++++++++++++++++++++- test/Parser/objc-init.m | 1 + test/SemaObjCXX/message.mm | 56 +++++++++++++++-- 7 files changed, 299 insertions(+), 65 deletions(-) diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 8d98f60550..cd63e611c9 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -41,6 +41,29 @@ public: virtual void print(llvm::raw_ostream &OS) const; }; +/// PrecedenceLevels - These are precedences for the binary/ternary +/// operators in the C99 grammar. These have been named to relate +/// with the C99 grammar productions. Low precedences numbers bind +/// more weakly than high numbers. +namespace prec { + enum Level { + Unknown = 0, // Not binary operator. + Comma = 1, // , + Assignment = 2, // =, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |= + Conditional = 3, // ? + LogicalOr = 4, // || + LogicalAnd = 5, // && + InclusiveOr = 6, // | + ExclusiveOr = 7, // ^ + And = 8, // & + Equality = 9, // ==, != + Relational = 10, // >=, <=, >, < + Shift = 11, // <<, >> + Additive = 12, // -, + + Multiplicative = 13, // *, /, % + PointerToMember = 14 // .*, ->* + }; +} /// Parser - This implements a parser for the C family of languages. After /// parsing units of the grammar, productions are invoked to handle whatever has @@ -460,9 +483,11 @@ private: //===--------------------------------------------------------------------===// // Diagnostic Emission and Error recovery. +public: DiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID); DiagnosticBuilder Diag(const Token &Tok, unsigned DiagID); +private: void SuggestParentheses(SourceLocation Loc, unsigned DK, SourceRange ParenRange); @@ -846,7 +871,7 @@ private: //===--------------------------------------------------------------------===// // C99 6.5: Expressions. - + OwningExprResult ParseExpression(); OwningExprResult ParseConstantExpression(); // Expr that doesn't include commas. @@ -857,7 +882,7 @@ private: OwningExprResult ParseExpressionWithLeadingExtension(SourceLocation ExtLoc); OwningExprResult ParseRHSOfBinaryExpression(OwningExprResult LHS, - unsigned MinPrec); + prec::Level MinPrec); OwningExprResult ParseCastExpression(bool isUnaryExpression, bool isAddressOfOperand, bool &NotCastExpr, @@ -954,6 +979,8 @@ private: // C++ 5.2.3: Explicit type conversion (functional notation) OwningExprResult ParseCXXTypeConstructExpression(const DeclSpec &DS); + bool isCXXSimpleTypeSpecifier() const; + /// ParseCXXSimpleTypeSpecifier - [C++ 7.1.5.2] Simple type specifiers. /// This should only be called when the current token is known to be part of /// simple-type-specifier. @@ -1011,6 +1038,7 @@ private: OwningExprResult ParseAssignmentExprWithObjCMessageExprStart( SourceLocation LBracloc, SourceLocation SuperLoc, TypeTy *ReceiverType, ExprArg ReceiverExpr); + bool ParseObjCXXMessageReceiver(bool &IsExpr, void *&TypeOrExpr); //===--------------------------------------------------------------------===// // C99 6.8: Statements and Blocks. diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index ef35dcb3f6..588825b01c 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -29,30 +29,6 @@ #include "llvm/ADT/SmallString.h" using namespace clang; -/// PrecedenceLevels - These are precedences for the binary/ternary operators in -/// the C99 grammar. These have been named to relate with the C99 grammar -/// productions. Low precedences numbers bind more weakly than high numbers. -namespace prec { - enum Level { - Unknown = 0, // Not binary operator. - Comma = 1, // , - Assignment = 2, // =, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |= - Conditional = 3, // ? - LogicalOr = 4, // || - LogicalAnd = 5, // && - InclusiveOr = 6, // | - ExclusiveOr = 7, // ^ - And = 8, // & - Equality = 9, // ==, != - Relational = 10, // >=, <=, >, < - Shift = 11, // <<, >> - Additive = 12, // -, + - Multiplicative = 13, // *, /, % - PointerToMember = 14 // .*, ->* - }; -} - - /// getBinOpPrecedence - Return the precedence of the specified binary operator /// token. This returns: /// @@ -297,10 +273,10 @@ Parser::OwningExprResult Parser::ParseConstantExpression() { /// ParseRHSOfBinaryExpression - Parse a binary expression that starts with /// LHS and has a precedence of at least MinPrec. Parser::OwningExprResult -Parser::ParseRHSOfBinaryExpression(OwningExprResult LHS, unsigned MinPrec) { - unsigned NextTokPrec = getBinOpPrecedence(Tok.getKind(), - GreaterThanIsOperator, - getLang().CPlusPlus0x); +Parser::ParseRHSOfBinaryExpression(OwningExprResult LHS, prec::Level MinPrec) { + prec::Level NextTokPrec = getBinOpPrecedence(Tok.getKind(), + GreaterThanIsOperator, + getLang().CPlusPlus0x); SourceLocation ColonLoc; while (1) { @@ -363,7 +339,7 @@ Parser::ParseRHSOfBinaryExpression(OwningExprResult LHS, unsigned MinPrec) { // Remember the precedence of this operator and get the precedence of the // operator immediately to the right of the RHS. - unsigned ThisPrec = NextTokPrec; + prec::Level ThisPrec = NextTokPrec; NextTokPrec = getBinOpPrecedence(Tok.getKind(), GreaterThanIsOperator, getLang().CPlusPlus0x); @@ -380,7 +356,8 @@ Parser::ParseRHSOfBinaryExpression(OwningExprResult LHS, unsigned MinPrec) { // is okay, to bind exactly as tightly. For example, compile A=B=C=D as // A=(B=(C=D)), where each paren is a level of recursion here. // The function takes ownership of the RHS. - RHS = ParseRHSOfBinaryExpression(move(RHS), ThisPrec + !isRightAssoc); + RHS = ParseRHSOfBinaryExpression(move(RHS), + static_cast(ThisPrec + !isRightAssoc)); if (RHS.isInvalid()) return move(RHS); diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index 8528f8fe19..74f4ebd618 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -749,6 +749,36 @@ bool Parser::ParseCXXCondition(OwningExprResult &ExprResult, return false; } +/// \brief Determine whether the current token starts a C++ +/// simple-type-specifier. +bool Parser::isCXXSimpleTypeSpecifier() const { + switch (Tok.getKind()) { + case tok::annot_typename: + case tok::kw_short: + case tok::kw_long: + case tok::kw_signed: + case tok::kw_unsigned: + case tok::kw_void: + case tok::kw_char: + case tok::kw_int: + case tok::kw_float: + case tok::kw_double: + case tok::kw_wchar_t: + case tok::kw_char16_t: + case tok::kw_char32_t: + case tok::kw_bool: + // FIXME: C++0x decltype support. + // GNU typeof support. + case tok::kw_typeof: + return true; + + default: + break; + } + + return false; +} + /// ParseCXXSimpleTypeSpecifier - [C++ 7.1.5.2] Simple type specifiers. /// This should only be called when the current token is known to be part of /// simple-type-specifier. @@ -837,6 +867,7 @@ void Parser::ParseCXXSimpleTypeSpecifier(DeclSpec &DS) { DS.SetTypeSpecType(DeclSpec::TST_bool, Loc, PrevSpec, DiagID); break; + // FIXME: C++0x decltype support. // GNU typeof support. case tok::kw_typeof: ParseTypeofSpecifier(DS); diff --git a/lib/Parse/ParseInit.cpp b/lib/Parse/ParseInit.cpp index 1a2a226e68..a382a9ae3c 100644 --- a/lib/Parse/ParseInit.cpp +++ b/lib/Parse/ParseInit.cpp @@ -34,6 +34,19 @@ static bool MayBeDesignationStart(tok::TokenKind K, Preprocessor &PP) { } } +static void CheckArrayDesignatorSyntax(Parser &P, SourceLocation Loc, + Designation &Desig) { + // If we have exactly one array designator, this used the GNU + // 'designation: array-designator' extension, otherwise there should be no + // designators at all! + if (Desig.getNumDesignators() == 1 && + (Desig.getDesignator(0).isArrayDesignator() || + Desig.getDesignator(0).isArrayRangeDesignator())) + P.Diag(Loc, diag::ext_gnu_missing_equal_designator); + else if (Desig.getNumDesignators() > 0) + P.Diag(Loc, diag::err_expected_equal_designator); +} + /// ParseInitializerWithPotentialDesignator - Parse the 'initializer' production /// checking to see if the token stream starts with a designator. /// @@ -124,10 +137,46 @@ Parser::OwningExprResult Parser::ParseInitializerWithPotentialDesignator() { // [4][foo bar] -> obsolete GNU designation with objc message send. // SourceLocation StartLoc = ConsumeBracket(); + OwningExprResult Idx(Actions); - // If Objective-C is enabled and this is a typename (class message send) or - // send to 'super', parse this as a message send expression. - if (getLang().ObjC1 && Tok.is(tok::identifier)) { + // If Objective-C is enabled and this is a typename (class message + // send) or send to 'super', parse this as a message send + // expression. We handle C++ and C separately, since C++ requires + // much more complicated parsing. + if (getLang().ObjC1 && getLang().CPlusPlus) { + // Send to 'super'. + if (Tok.is(tok::identifier) && Tok.getIdentifierInfo() == Ident_super && + NextToken().isNot(tok::period) && CurScope->isInObjcMethodScope()) { + CheckArrayDesignatorSyntax(*this, StartLoc, Desig); + return ParseAssignmentExprWithObjCMessageExprStart(StartLoc, + ConsumeToken(), 0, + ExprArg(Actions)); + } + + // Parse the receiver, which is either a type or an expression. + bool IsExpr; + void *TypeOrExpr; + if (ParseObjCXXMessageReceiver(IsExpr, TypeOrExpr)) { + SkipUntil(tok::r_square); + return ExprError(); + } + + // If the receiver was a type, we have a class message; parse + // the rest of it. + if (!IsExpr) { + CheckArrayDesignatorSyntax(*this, StartLoc, Desig); + return ParseAssignmentExprWithObjCMessageExprStart(StartLoc, + SourceLocation(), + TypeOrExpr, + ExprArg(Actions)); + } + + // If the receiver was an expression, we still don't know + // whether we have a message send or an array designator; just + // adopt the expression for further analysis below. + // FIXME: potentially-potentially evaluated expression above? + Idx = OwningExprResult(Actions, TypeOrExpr); + } else if (getLang().ObjC1 && Tok.is(tok::identifier)) { IdentifierInfo *II = Tok.getIdentifierInfo(); SourceLocation IILoc = Tok.getLocation(); TypeTy *ReceiverType; @@ -141,16 +190,7 @@ Parser::OwningExprResult Parser::ParseInitializerWithPotentialDesignator() { ReceiverType)) { case Action::ObjCSuperMessage: case Action::ObjCClassMessage: - // If we have exactly one array designator, this used the GNU - // 'designation: array-designator' extension, otherwise there should be no - // designators at all! - if (Desig.getNumDesignators() == 1 && - (Desig.getDesignator(0).isArrayDesignator() || - Desig.getDesignator(0).isArrayRangeDesignator())) - Diag(StartLoc, diag::ext_gnu_missing_equal_designator); - else if (Desig.getNumDesignators() > 0) - Diag(Tok, diag::err_expected_equal_designator); - + CheckArrayDesignatorSyntax(*this, StartLoc, Desig); if (Kind == Action::ObjCSuperMessage) return ParseAssignmentExprWithObjCMessageExprStart(StartLoc, ConsumeToken(), @@ -175,13 +215,19 @@ Parser::OwningExprResult Parser::ParseInitializerWithPotentialDesignator() { } } + // Parse the index expression, if we haven't already gotten one + // above (which can only happen in Objective-C++). // Note that we parse this as an assignment expression, not a constant // expression (allowing *=, =, etc) to handle the objc case. Sema needs // to validate that the expression is a constant. - OwningExprResult Idx(ParseAssignmentExpression()); - if (Idx.isInvalid()) { - SkipUntil(tok::r_square); - return move(Idx); + // FIXME: We also need to tell Sema that we're in a + // potentially-potentially evaluated context. + if (!Idx.get()) { + Idx = ParseAssignmentExpression(); + if (Idx.isInvalid()) { + SkipUntil(tok::r_square); + return move(Idx); + } } // Given an expression, we could either have a designator (if the next @@ -190,17 +236,7 @@ Parser::OwningExprResult Parser::ParseInitializerWithPotentialDesignator() { // an assignment-expression production. if (getLang().ObjC1 && Tok.isNot(tok::ellipsis) && Tok.isNot(tok::r_square)) { - - // If we have exactly one array designator, this used the GNU - // 'designation: array-designator' extension, otherwise there should be no - // designators at all! - if (Desig.getNumDesignators() == 1 && - (Desig.getDesignator(0).isArrayDesignator() || - Desig.getDesignator(0).isArrayRangeDesignator())) - Diag(StartLoc, diag::ext_gnu_missing_equal_designator); - else if (Desig.getNumDesignators() > 0) - Diag(Tok, diag::err_expected_equal_designator); - + CheckArrayDesignatorSyntax(*this, Tok.getLocation(), Desig); return ParseAssignmentExprWithObjCMessageExprStart(StartLoc, SourceLocation(), 0, move(Idx)); diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp index 2a71bf024b..7337445b64 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/lib/Parse/ParseObjc.cpp @@ -1711,6 +1711,92 @@ Parser::OwningExprResult Parser::ParseObjCAtExpression(SourceLocation AtLoc) { } } +/// \brirg Parse the receiver of an Objective-C++ message send. +/// +/// This routine parses the receiver of a message send in +/// Objective-C++ either as a type or as an expression. Note that this +/// routine must not be called to parse a send to 'super', since it +/// has no way to return such a result. +/// +/// \param IsExpr Whether the receiver was parsed as an expression. +/// +/// \param TypeOrExpr If the receiver was parsed as an expression (\c +/// IsExpr is true), the parsed expression. If the receiver was parsed +/// as a type (\c IsExpr is false), the parsed type. +/// +/// \returns True if an error occurred during parsing or semantic +/// analysis, in which case the arguments do not have valid +/// values. Otherwise, returns false for a successful parse. +/// +/// objc-receiver: [C++] +/// 'super' [not parsed here] +/// expression +/// simple-type-specifier +/// typename-specifier + +bool Parser::ParseObjCXXMessageReceiver(bool &IsExpr, void *&TypeOrExpr) { + if (Tok.is(tok::identifier) || Tok.is(tok::coloncolon) || + Tok.is(tok::kw_typename) || Tok.is(tok::annot_cxxscope)) + TryAnnotateTypeOrScopeToken(); + + if (!isCXXSimpleTypeSpecifier()) { + // objc-receiver: + // expression + OwningExprResult Receiver = ParseExpression(); + if (Receiver.isInvalid()) + return true; + + IsExpr = true; + TypeOrExpr = Receiver.take(); + return false; + } + + // objc-receiver: + // typename-specifier + // simple-type-specifier + // expression (that starts with one of the above) + DeclSpec DS; + ParseCXXSimpleTypeSpecifier(DS); + + if (Tok.is(tok::l_paren)) { + // If we see an opening parentheses at this point, we are + // actually parsing an expression that starts with a + // function-style cast, e.g., + // + // postfix-expression: + // simple-type-specifier ( expression-list [opt] ) + // typename-specifier ( expression-list [opt] ) + // + // Parse the remainder of this case, then the (optional) + // postfix-expression suffix, followed by the (optional) + // right-hand side of the binary expression. We have an + // instance method. + OwningExprResult Receiver = ParseCXXTypeConstructExpression(DS); + if (!Receiver.isInvalid()) + Receiver = ParsePostfixExpressionSuffix(move(Receiver)); + if (!Receiver.isInvalid()) + Receiver = ParseRHSOfBinaryExpression(move(Receiver), prec::Comma); + if (Receiver.isInvalid()) + return true; + + IsExpr = true; + TypeOrExpr = Receiver.take(); + return false; + } + + // We have a class message. Turn the simple-type-specifier or + // typename-specifier we parsed into a type and parse the + // remainder of the class message. + Declarator DeclaratorInfo(DS, Declarator::TypeNameContext); + TypeResult Type = Actions.ActOnTypeName(CurScope, DeclaratorInfo); + if (Type.isInvalid()) + return true; + + IsExpr = false; + TypeOrExpr = Type.get(); + return false; +} + /// objc-message-expr: /// '[' objc-receiver objc-message-args ']' /// @@ -1719,11 +1805,38 @@ Parser::OwningExprResult Parser::ParseObjCAtExpression(SourceLocation AtLoc) { /// expression /// class-name /// type-name +/// Parser::OwningExprResult Parser::ParseObjCMessageExpression() { assert(Tok.is(tok::l_square) && "'[' expected"); SourceLocation LBracLoc = ConsumeBracket(); // consume '[' - if (Tok.is(tok::identifier)) { + if (getLang().CPlusPlus) { + // We completely separate the C and C++ cases because C++ requires + // more complicated (read: slower) parsing. + + // Handle send to super. + // FIXME: This doesn't benefit from the same typo-correction we + // get in Objective-C. + if (Tok.is(tok::identifier) && Tok.getIdentifierInfo() == Ident_super && + NextToken().isNot(tok::period) && CurScope->isInObjcMethodScope()) + return ParseObjCMessageExpressionBody(LBracLoc, ConsumeToken(), 0, + ExprArg(Actions)); + + // Parse the receiver, which is either a type or an expression. + bool IsExpr; + void *TypeOrExpr; + if (ParseObjCXXMessageReceiver(IsExpr, TypeOrExpr)) { + SkipUntil(tok::r_square); + return ExprError(); + } + + if (IsExpr) + return ParseObjCMessageExpressionBody(LBracLoc, SourceLocation(), 0, + OwningExprResult(Actions, TypeOrExpr)); + + return ParseObjCMessageExpressionBody(LBracLoc, SourceLocation(), + TypeOrExpr, ExprArg(Actions)); + } else if (Tok.is(tok::identifier)) { IdentifierInfo *Name = Tok.getIdentifierInfo(); SourceLocation NameLoc = Tok.getLocation(); TypeTy *ReceiverType; diff --git a/test/Parser/objc-init.m b/test/Parser/objc-init.m index 0024f0278f..32ba948f47 100644 --- a/test/Parser/objc-init.m +++ b/test/Parser/objc-init.m @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -pedantic +// RUN: %clang_cc1 -fsyntax-only -verify -x objective-c++ %s // rdar://5707001 @interface NSNumber; diff --git a/test/SemaObjCXX/message.mm b/test/SemaObjCXX/message.mm index 0d5bef8bb7..91b11f146c 100644 --- a/test/SemaObjCXX/message.mm +++ b/test/SemaObjCXX/message.mm @@ -1,12 +1,13 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s @interface I1 -- (void)method; +- (int*)method; @end @implementation I1 -- (void)method { +- (int*)method { struct x { }; [x method]; // expected-error{{receiver type 'x' is not an Objective-C class}} + return 0; } @end @@ -15,15 +16,62 @@ typedef struct { int x; } ivar; @interface I2 { id ivar; } -- (void)method; +- (int*)method; + (void)method; @end +struct I2_holder { + I2_holder(); + + I2 *get(); +}; + +I2 *operator+(I2_holder, int); + @implementation I2 -- (void)method { +- (int*)method { [ivar method]; + + // Test instance messages that start with a simple-type-specifier. + [I2_holder().get() method]; + [I2_holder().get() + 17 method]; + return 0; } + (void)method { [ivar method]; // expected-error{{receiver type 'ivar' (aka 'ivar') is not an Objective-C class}} } @end + +// Class message sends +@interface I3 ++ (int*)method; +@end + +@interface I4 : I3 ++ (int*)otherMethod; +@end + +template +struct identity { + typedef T type; +}; + +@implementation I4 ++ (int *)otherMethod { + // Test class messages that use non-trivial simple-type-specifiers + // or typename-specifiers. + if (false) { + if (true) + return [typename identity::type method]; + + return [::I3 method]; + } + + int* ip1 = {[super method]}; + int* ip2 = {[::I3 method]}; + int* ip3 = {[typename identity::type method]}; + int* ip4 = {[typename identity::type().get() method]}; + int array[5] = {[3] = 2}; + return [super method]; +} +@end