diff options
author | Douglas Gregor <dgregor@apple.com> | 2011-08-04 15:30:47 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2011-08-04 15:30:47 +0000 |
commit | ae7902c4293d9de8b9591759513f0d075f45022a (patch) | |
tree | 23abb7b0681efb30fe972f0562d0ec54c03f1e30 | |
parent | edc2220d6986e302354dbc86cdeb4764ea5ce810 (diff) |
Parsing of C++0x lambda expressions, from John Freeman with help from
David Blaikie!
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@136876 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/Basic/DiagnosticParseKinds.td | 9 | ||||
-rw-r--r-- | include/clang/Parse/Parser.h | 13 | ||||
-rw-r--r-- | include/clang/Sema/DeclSpec.h | 47 | ||||
-rw-r--r-- | lib/Parse/ParseDecl.cpp | 5 | ||||
-rw-r--r-- | lib/Parse/ParseDeclCXX.cpp | 4 | ||||
-rw-r--r-- | lib/Parse/ParseExpr.cpp | 10 | ||||
-rw-r--r-- | lib/Parse/ParseExprCXX.cpp | 261 | ||||
-rw-r--r-- | test/Parser/cxx0x-attributes.cpp | 2 | ||||
-rw-r--r-- | test/Parser/cxx0x-lambda-expressions.cpp | 27 | ||||
-rw-r--r-- | test/Parser/objc-messaging-neg-1.m | 1 | ||||
-rw-r--r-- | test/Parser/objcxx-lambda-expressions-neg.mm | 5 | ||||
-rw-r--r-- | test/Parser/objcxx0x-lambda-expressions.mm | 26 |
12 files changed, 404 insertions, 6 deletions
diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index 3764a40915..27b4f850ee 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -487,6 +487,15 @@ def err_paren_sizeof_parameter_pack : Error< def err_sizeof_parameter_pack : Error< "expected parenthesized parameter pack name in 'sizeof...' expression">; +// C++0x lambda expressions +def err_expected_comma_or_rsquare : Error< + "expected ',' or ']' in lambda capture list">; +def err_this_captured_by_reference : Error< + "'this' cannot be captured by reference">; +def err_expected_capture : Error< + "expected variable name or 'this' in lambda capture list">; +def err_expected_lambda_body : Error<"expected body of lambda expression">; + // Availability attribute def err_expected_version : Error< "expected a version of the form 'major[.minor[.subminor]]'">; diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 83b0cd455e..af3f40ec40 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -1186,6 +1186,17 @@ private: bool IsTypename = false); //===--------------------------------------------------------------------===// + // C++0x 5.1.2: Lambda expressions + + // [...] () -> type {...} + ExprResult ParseLambdaExpression(); + ExprResult TryParseLambdaExpression(); + llvm::Optional<unsigned> ParseLambdaIntroducer(LambdaIntroducer &Intro); + bool TryParseLambdaIntroducer(LambdaIntroducer &Intro); + ExprResult ParseLambdaExpressionAfterIntroducer( + LambdaIntroducer &Intro); + + //===--------------------------------------------------------------------===// // C++ 5.2p1: C++ Casts ExprResult ParseCXXCasts(); @@ -1226,7 +1237,7 @@ private: //===--------------------------------------------------------------------===// // C++0x 8: Function declaration trailing-return-type - TypeResult ParseTrailingReturnType(); + TypeResult ParseTrailingReturnType(SourceRange &Range); //===--------------------------------------------------------------------===// // C++ 2.13.5: C++ Boolean Literals diff --git a/include/clang/Sema/DeclSpec.h b/include/clang/Sema/DeclSpec.h index ad96c45844..cdd7dbaa6d 100644 --- a/include/clang/Sema/DeclSpec.h +++ b/include/clang/Sema/DeclSpec.h @@ -1828,6 +1828,53 @@ private: SourceLocation LastLocation; }; +/// LambdaCaptureDefault - The default, if any, capture method for a +/// lambda expression. +enum LambdaCaptureDefault { + LCD_None, + LCD_ByCopy, + LCD_ByRef +}; + +/// LambdaCaptureKind - The different capture forms in a lambda +/// introducer: 'this' or a copied or referenced variable. +enum LambdaCaptureKind { + LCK_This, + LCK_ByCopy, + LCK_ByRef +}; + +/// LambdaCapture - An individual capture in a lambda introducer. +struct LambdaCapture { + LambdaCaptureKind Kind; + SourceLocation Loc; + IdentifierInfo* Id; + + LambdaCapture(LambdaCaptureKind Kind, + SourceLocation Loc, + IdentifierInfo* Id = 0) + : Kind(Kind), Loc(Loc), Id(Id) + {} +}; + +/// LambdaIntroducer - Represents a complete lambda introducer. +struct LambdaIntroducer { + SourceRange Range; + LambdaCaptureDefault Default; + llvm::SmallVector<LambdaCapture, 4> Captures; + + LambdaIntroducer() + : Default(LCD_None) {} + + /// addCapture - Append a capture in a lambda introducer. + void addCapture(LambdaCaptureKind Kind, + SourceLocation Loc, + IdentifierInfo* Id = 0) { + Captures.push_back(LambdaCapture(Kind, Loc, Id)); + } + +}; + } // end namespace clang #endif diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index f946f798c7..1e318fe66e 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -3745,7 +3745,10 @@ void Parser::ParseFunctionDeclarator(SourceLocation LParenLoc, Declarator &D, // Parse trailing-return-type[opt]. if (getLang().CPlusPlus0x && Tok.is(tok::arrow)) { - TrailingReturnType = ParseTrailingReturnType().get(); + SourceRange Range; + TrailingReturnType = ParseTrailingReturnType(Range).get(); + if (Range.getEnd().isValid()) + EndLoc = Range.getEnd(); } } diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index 344a433792..df2c5f8e62 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -2442,7 +2442,7 @@ ExceptionSpecificationType Parser::ParseDynamicExceptionSpecification( /// ParseTrailingReturnType - Parse a trailing return type on a new-style /// function declaration. -TypeResult Parser::ParseTrailingReturnType() { +TypeResult Parser::ParseTrailingReturnType(SourceRange &Range) { assert(Tok.is(tok::arrow) && "expected arrow"); ConsumeToken(); @@ -2454,8 +2454,6 @@ TypeResult Parser::ParseTrailingReturnType() { // // struct X is parsed as class definition because of the trailing // brace. - - SourceRange Range; return ParseTypeName(&Range); } diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 3cd1f3987a..42d56c3693 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -1115,6 +1115,16 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression, NotCastExpr, isTypeCast); } case tok::l_square: + if (getLang().CPlusPlus0x) { + if (getLang().ObjC1) { + Res = TryParseLambdaExpression(); + if (Res.isInvalid()) + Res = ParseObjCMessageExpression(); + break; + } + Res = ParseLambdaExpression(); + break; + } if (getLang().ObjC1) { Res = ParseObjCMessageExpression(); break; diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index d88356ca33..757d86e5d6 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -15,6 +15,7 @@ #include "clang/Parse/Parser.h" #include "RAIIObjectsForParser.h" #include "clang/Sema/DeclSpec.h" +#include "clang/Sema/Scope.h" #include "clang/Sema/ParsedTemplate.h" #include "llvm/Support/ErrorHandling.h" @@ -504,6 +505,266 @@ ExprResult Parser::ParseCXXIdExpression(bool isAddressOfOperand) { } +/// ParseLambdaExpression - Parse a C++0x lambda expression. +/// +/// lambda-expression: +/// lambda-introducer lambda-declarator[opt] compound-statement +/// +/// lambda-introducer: +/// '[' lambda-capture[opt] ']' +/// +/// lambda-capture: +/// capture-default +/// capture-list +/// capture-default ',' capture-list +/// +/// capture-default: +/// '&' +/// '=' +/// +/// capture-list: +/// capture +/// capture-list ',' capture +/// +/// capture: +/// identifier +/// '&' identifier +/// 'this' +/// +/// lambda-declarator: +/// '(' parameter-declaration-clause ')' attribute-specifier[opt] +/// 'mutable'[opt] exception-specification[opt] +/// trailing-return-type[opt] +/// +ExprResult Parser::ParseLambdaExpression() { + // Parse lambda-introducer. + LambdaIntroducer Intro; + + llvm::Optional<unsigned> DiagID(ParseLambdaIntroducer(Intro)); + if (DiagID) { + Diag(Tok, DiagID.getValue()); + SkipUntil(tok::r_square); + } + + return ParseLambdaExpressionAfterIntroducer(Intro); +} + +/// TryParseLambdaExpression - Use lookahead and potentially tentative +/// parsing to determine if we are looking at a C++0x lambda expression, and parse +/// it if we are. +/// +/// If we are not looking at a lambda expression, returns ExprError(). +ExprResult Parser::TryParseLambdaExpression() { + assert(getLang().CPlusPlus0x + && Tok.is(tok::l_square) + && "Not at the start of a possible lambda expression."); + + const Token Next = NextToken(), After = GetLookAheadToken(2); + + // If lookahead indicates this is a lambda... + if (Next.is(tok::r_square) || // [] + Next.is(tok::equal) || // [= + (Next.is(tok::amp) && // [&] or [&, + (After.is(tok::r_square) || + After.is(tok::comma))) || + (Next.is(tok::identifier) && // [identifier] + After.is(tok::r_square))) { + return ParseLambdaExpression(); + } + + // If lookahead indicates this is an Objective-C message... + if (Next.is(tok::identifier) && After.is(tok::identifier)) { + return ExprError(); + } + + LambdaIntroducer Intro; + if (TryParseLambdaIntroducer(Intro)) + return ExprError(); + return ParseLambdaExpressionAfterIntroducer(Intro); +} + +/// ParseLambdaExpression - Parse a lambda introducer. +/// +/// Returns a DiagnosticID if it hit something unexpected. +llvm::Optional<unsigned> Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro) { + typedef llvm::Optional<unsigned> DiagResult; + + assert(Tok.is(tok::l_square) && "Lambda expressions begin with '['."); + Intro.Range.setBegin(ConsumeBracket()); + + bool first = true; + + // Parse capture-default. + if (Tok.is(tok::amp) && + (NextToken().is(tok::comma) || NextToken().is(tok::r_square))) { + Intro.Default = LCD_ByRef; + ConsumeToken(); + first = false; + } else if (Tok.is(tok::equal)) { + Intro.Default = LCD_ByCopy; + ConsumeToken(); + first = false; + } + + while (Tok.isNot(tok::r_square)) { + if (!first) { + if (Tok.isNot(tok::comma)) + return DiagResult(diag::err_expected_comma_or_rsquare); + ConsumeToken(); + } + + first = false; + + // Parse capture. + LambdaCaptureKind Kind = LCK_ByCopy; + SourceLocation Loc; + IdentifierInfo* Id = 0; + + if (Tok.is(tok::kw_this)) { + Kind = LCK_This; + Loc = ConsumeToken(); + } else { + if (Tok.is(tok::amp)) { + Kind = LCK_ByRef; + ConsumeToken(); + } + + if (Tok.is(tok::identifier)) { + Id = Tok.getIdentifierInfo(); + Loc = ConsumeToken(); + } else if (Tok.is(tok::kw_this)) { + // FIXME: If we want to suggest a fixit here, will need to return more + // than just DiagnosticID. Perhaps full DiagnosticBuilder that can be + // Clear()ed to prevent emission in case of tentative parsing? + return DiagResult(diag::err_this_captured_by_reference); + } else { + return DiagResult(diag::err_expected_capture); + } + } + + Intro.addCapture(Kind, Loc, Id); + } + + Intro.Range.setEnd(MatchRHSPunctuation(tok::r_square, + Intro.Range.getBegin())); + + return DiagResult(); +} + +/// TryParseLambdaExpression - Tentatively parse a lambda introducer. +/// +/// Returns true if it hit something unexpected. +bool Parser::TryParseLambdaIntroducer(LambdaIntroducer &Intro) { + TentativeParsingAction PA(*this); + + llvm::Optional<unsigned> DiagID(ParseLambdaIntroducer(Intro)); + + if (DiagID) { + PA.Revert(); + return true; + } + + PA.Commit(); + return false; +} + +/// ParseLambdaExpressionAfterIntroducer - Parse the rest of a lambda +/// expression. +ExprResult Parser::ParseLambdaExpressionAfterIntroducer( + LambdaIntroducer &Intro) { + // Parse lambda-declarator[opt]. + DeclSpec DS(AttrFactory); + Declarator D(DS, Declarator::PrototypeContext); + + if (Tok.is(tok::l_paren)) { + ParseScope PrototypeScope(this, + Scope::FunctionPrototypeScope | + Scope::DeclScope); + + SourceLocation DeclLoc = ConsumeParen(), DeclEndLoc; + + // Parse parameter-declaration-clause. + ParsedAttributes Attr(AttrFactory); + llvm::SmallVector<DeclaratorChunk::ParamInfo, 16> ParamInfo; + SourceLocation EllipsisLoc; + + if (Tok.isNot(tok::r_paren)) + ParseParameterDeclarationClause(D, Attr, ParamInfo, EllipsisLoc); + + DeclEndLoc = MatchRHSPunctuation(tok::r_paren, DeclLoc); + + // Parse 'mutable'[opt]. + SourceLocation MutableLoc; + if (Tok.is(tok::kw_mutable)) { + MutableLoc = ConsumeToken(); + DeclEndLoc = MutableLoc; + } + + // Parse exception-specification[opt]. + ExceptionSpecificationType ESpecType = EST_None; + SourceRange ESpecRange; + llvm::SmallVector<ParsedType, 2> DynamicExceptions; + llvm::SmallVector<SourceRange, 2> DynamicExceptionRanges; + ExprResult NoexceptExpr; + ESpecType = MaybeParseExceptionSpecification(ESpecRange, + DynamicExceptions, + DynamicExceptionRanges, + NoexceptExpr); + + if (ESpecType != EST_None) + DeclEndLoc = ESpecRange.getEnd(); + + // Parse attribute-specifier[opt]. + MaybeParseCXX0XAttributes(Attr, &DeclEndLoc); + + // Parse trailing-return-type[opt]. + ParsedType TrailingReturnType; + if (Tok.is(tok::arrow)) { + SourceRange Range; + TrailingReturnType = ParseTrailingReturnType(Range).get(); + if (Range.getEnd().isValid()) + DeclEndLoc = Range.getEnd(); + } + + PrototypeScope.Exit(); + + D.AddTypeInfo(DeclaratorChunk::getFunction(/*hasProto=*/true, + /*isVariadic=*/EllipsisLoc.isValid(), + EllipsisLoc, + ParamInfo.data(), ParamInfo.size(), + DS.getTypeQualifiers(), + /*RefQualifierIsLValueRef=*/true, + /*RefQualifierLoc=*/SourceLocation(), + MutableLoc, + ESpecType, ESpecRange.getBegin(), + DynamicExceptions.data(), + DynamicExceptionRanges.data(), + DynamicExceptions.size(), + NoexceptExpr.isUsable() ? + NoexceptExpr.get() : 0, + DeclLoc, DeclEndLoc, D, + TrailingReturnType), + Attr, DeclEndLoc); + } + + // Parse compound-statement. + if (Tok.is(tok::l_brace)) { + // FIXME: Rename BlockScope -> ClosureScope if we decide to continue using + // it. + ParseScope BodyScope(this, Scope::BlockScope | Scope::FnScope | + Scope::BreakScope | Scope::ContinueScope | + Scope::DeclScope); + + StmtResult Stmt(ParseCompoundStatementBody()); + + BodyScope.Exit(); + } else { + Diag(Tok, diag::err_expected_lambda_body); + } + + return ExprEmpty(); +} + /// ParseCXXCasts - This handles the various ways to cast expressions to another /// type. /// diff --git a/test/Parser/cxx0x-attributes.cpp b/test/Parser/cxx0x-attributes.cpp index f65e290709..0285ee1e9a 100644 --- a/test/Parser/cxx0x-attributes.cpp +++ b/test/Parser/cxx0x-attributes.cpp @@ -17,7 +17,7 @@ int comma_attr [[,]]; // expected-error {{expected identifier}} int scope_attr [[foo::]]; // expected-error {{expected identifier}} int & [[]] ref_attr = after_attr; // expected-error {{an attribute list cannot appear here}} class foo { - void after_const_attr () const [[]]; // expected-error {{expected expression}} + void after_const_attr () const [[]]; // expected-error {{expected body of lambda expression}} expected-error {{array has incomplete element type 'void'}} }; extern "C++" [[]] { } // expected-error {{an attribute list cannot appear here}} [[]] template <typename T> void before_template_attr (); // expected-error {{an attribute list cannot appear here}} diff --git a/test/Parser/cxx0x-lambda-expressions.cpp b/test/Parser/cxx0x-lambda-expressions.cpp new file mode 100644 index 0000000000..54281e3082 --- /dev/null +++ b/test/Parser/cxx0x-lambda-expressions.cpp @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %s + +class C { + + int f() { + int foo, bar; + + []; // expected-error {{expected body of lambda expression}} + [+] {}; // expected-error {{expected variable name or 'this' in lambda capture list}} + [foo+] {}; // expected-error {{expected ',' or ']' in lambda capture list}} + [foo,&this] {}; // expected-error {{'this' cannot be captured by reference}} + [&this] {}; // expected-error {{'this' cannot be captured by reference}} + [&,] {}; // expected-error {{ expected variable name or 'this' in lambda capture list}} + [=,] {}; // expected-error {{ expected variable name or 'this' in lambda capture list}} + [] {}; + [=] (int i) {}; + [&] (int) mutable -> void {}; + [foo,bar] () { return 3; }; + [=,&foo] () {}; + [&,foo] () {}; + [this] () {}; + + return 1; + } + +}; + diff --git a/test/Parser/objc-messaging-neg-1.m b/test/Parser/objc-messaging-neg-1.m index 4ddadb816f..bb496e9691 100644 --- a/test/Parser/objc-messaging-neg-1.m +++ b/test/Parser/objc-messaging-neg-1.m @@ -9,4 +9,5 @@ int main() { [a bla:0 6:7]; // expected-error {{expected ']'}} [A foo bar]; // expected-error {{expected ':'}} [A foo bar bar1]; // expected-error {{expected ':'}} + [] {}; // expected-error {{expected expression}} } diff --git a/test/Parser/objcxx-lambda-expressions-neg.mm b/test/Parser/objcxx-lambda-expressions-neg.mm new file mode 100644 index 0000000000..864cc6b8fb --- /dev/null +++ b/test/Parser/objcxx-lambda-expressions-neg.mm @@ -0,0 +1,5 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +int main() { + []{}; // expected-error {{expected expression}} +} diff --git a/test/Parser/objcxx0x-lambda-expressions.mm b/test/Parser/objcxx0x-lambda-expressions.mm new file mode 100644 index 0000000000..d100e2e488 --- /dev/null +++ b/test/Parser/objcxx0x-lambda-expressions.mm @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %s + +class C { + + void f() { + int foo, bar; + + // fail to parse as a lambda introducer, so we get objc message parsing errors instead + [foo,+] {}; // expected-error {{expected expression}} + + []; // expected-error {{expected body of lambda expression}} + [=,foo+] {}; // expected-error {{expected ',' or ']' in lambda capture list}} + [&this] {}; // expected-error {{address expression must be an lvalue}} + [] {}; + [=] (int i) {}; + [&] (int) mutable -> void {}; + // FIXME: this error occurs because we do not yet handle lambda scopes + // properly. I did not anticipate it because I thought it was a semantic (not + // syntactic) check. + [foo,bar] () { return 3; }; // expected-error {{void function 'f' should not return a value}} + [=,&foo] () {}; + [this] () {}; + } + +}; + |