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.

llvm-svn: 102031
This commit is contained in:
Douglas Gregor 2010-04-21 22:36:40 +00:00
parent 8e7ebea0b9
commit 8d4de67e1d
7 changed files with 299 additions and 65 deletions

View 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.

View File

@ -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<prec::Level>(ThisPrec + !isRightAssoc));
if (RHS.isInvalid())
return move(RHS);

View 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);

View 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.
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));

View 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;

View 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;

View 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