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<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
diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp
index ef35dcb..588825b 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 @@
/// 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 @@
// 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 @@
// 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);
diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp
index 8528f8f..74f4ebd 100644
--- a/lib/Parse/ParseExprCXX.cpp
+++ b/lib/Parse/ParseExprCXX.cpp
@@ -749,6 +749,36 @@
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 @@
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 1a2a226..a382a9a 100644
--- a/lib/Parse/ParseInit.cpp
+++ b/lib/Parse/ParseInit.cpp
@@ -34,6 +34,19 @@
}
}
+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 @@
// [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 @@
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 @@
}
}
+ // 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 @@
// 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 2a71bf0..7337445 100644
--- a/lib/Parse/ParseObjc.cpp
+++ b/lib/Parse/ParseObjc.cpp
@@ -1711,6 +1711,92 @@
}
}
+/// \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 @@
/// 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;