]> granicus.if.org Git - clang/commitdiff
Implement parsing for message sends in Objective-C++. Message sends in
authorDouglas Gregor <dgregor@apple.com>
Wed, 21 Apr 2010 22:36:40 +0000 (22:36 +0000)
committerDouglas Gregor <dgregor@apple.com>
Wed, 21 Apr 2010 22:36:40 +0000 (22:36 +0000)
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<I>::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<T>::type method];

Note that the same effect can be achieved in GCC by way of a typedef,
e.g.,

  typedef typename computed_receiver_type<T>::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
lib/Parse/ParseExpr.cpp
lib/Parse/ParseExprCXX.cpp
lib/Parse/ParseInit.cpp
lib/Parse/ParseObjc.cpp
test/Parser/objc-init.m
test/SemaObjCXX/message.mm

index 8d98f605505a3342ecaa48c66734afa212b03c29..cd63e611c9003b79eb51251c38e51535e99121e1 100644 (file)
@@ -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.
index ef35dcb3f6ca25ef7d4b2e763f6dc39e0ff5459a..588825b01ca4fbcecdaf449374d43121a78188df 100644 (file)
 #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<prec::Level>(ThisPrec + !isRightAssoc));
       if (RHS.isInvalid())
         return move(RHS);
 
index 8528f8fe190c8969a5db16c37c9e129a764b4636..74f4ebd6181919b68d080f543f44852dafafb41f 100644 (file)
@@ -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);
index 1a2a226e68328e6bea73e466e1daad24afa36130..a382a9ae3c40e2e4d1023bcdd968ad3a8fbe1d09 100644 (file)
@@ -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.  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));
+      }
 
-    // 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)) {
+      // 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));
index 2a71bf024bffba70654b15ed03b42719250ac54a..7337445b64d1d8247ebf2f569ad6f59467610a2b 100644 (file)
@@ -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;
index 0024f0278f16a3ca2a05c6af21c85c0d0b7f990a..32ba948f472de6b387b315735f7e62474036c5f2 100644 (file)
@@ -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;
index 0d5bef8bb7a7d0376b20616215ceb8dc27b6863d..91b11f146cc1ff1556aee2e00b942e24feb96e8c 100644 (file)
@@ -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<typename T>
+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<I3>::type method];
+
+    return [::I3 method];
+  }
+
+  int* ip1 = {[super method]};
+  int* ip2 = {[::I3 method]};
+  int* ip3 = {[typename identity<I3>::type method]};
+  int* ip4 = {[typename identity<I2_holder>::type().get() method]};
+  int array[5] = {[3] = 2};
+  return [super method];
+}
+@end