From 296e8d5fdcf9946f51e866adc8d281379e51efe9 Mon Sep 17 00:00:00 2001 From: Steve Naroff Date: Thu, 28 Aug 2008 19:20:44 +0000 Subject: [PATCH] Add parser/action support for block literal expressions. Parser support for blocks is almost complete...just need to add support for the __block() storage class qualifier. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@55495 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticKinds.def | 4 ++ include/clang/Parse/Action.h | 21 +++++++ include/clang/Parse/DeclSpec.h | 1 + include/clang/Parse/Parser.h | 5 ++ include/clang/Parse/Scope.h | 8 ++- lib/AST/ASTContext.cpp | 6 +- lib/Parse/ParseExpr.cpp | 78 +++++++++++++++++++++++++ test/Parser/block-pointer-decl.c | 12 +++- 8 files changed, 129 insertions(+), 6 deletions(-) diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index f075a44143..3510e7dee6 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -1116,6 +1116,10 @@ DIAG(warn_selfcomparison,WARNING, // CHECK: use of uninitialized values DIAG(warn_uninit_val, WARNING, "use of uninitialized variable") +// Blocks +DIAG(err_expected_block_lbrace, ERROR, + "expected '{' in block literal") + // CFString checking DIAG(err_cfstring_literal_not_string_constant, ERROR, "CFString literal is not a string constant") diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index 2da798241f..d402ed212c 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -533,6 +533,27 @@ public: return 0; } + //===------------------------- "Block" Extension ------------------------===// + + /// ActOnBlockStart - This callback is invoked when a block literal is + /// started. The result pointer is passed into the block finalizers. + virtual void ActOnBlockStart(SourceLocation CaretLoc, Scope *CurScope, + Declarator &ParamInfo) {} + + /// ActOnBlockError - If there is an error parsing a block, this callback + /// is invoked to pop the information about the block from the action impl. + virtual void ActOnBlockError(SourceLocation CaretLoc, Scope *CurScope) {} + + /// ActOnBlockStmtExpr - This is called when the body of a block statement + /// literal was successfully completed. ^(int x){...} + virtual ExprResult ActOnBlockStmtExpr(SourceLocation CaretLoc, StmtTy *Body, + Scope *CurScope) { return 0; } + + /// ActOnBlockExprExpr - This is called when the body of a block + /// expression literal was successfully completed. ^(int x)[foo bar: x] + virtual ExprResult ActOnBlockExprExpr(SourceLocation CaretLoc, ExprTy *Body, + Scope *CurScope) { return 0; } + //===------------------------- C++ Declarations -------------------------===// /// ActOnStartNamespaceDef - This is called at the start of a namespace diff --git a/include/clang/Parse/DeclSpec.h b/include/clang/Parse/DeclSpec.h index 9edc649384..4f6c2fbb5b 100644 --- a/include/clang/Parse/DeclSpec.h +++ b/include/clang/Parse/DeclSpec.h @@ -481,6 +481,7 @@ struct DeclaratorChunk { case Reference: return Ref.AttrList; case Array: return 0; case Function: return 0; + case BlockPointer: return 0; // FIXME: Do blocks have attr list? } } diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 5fa62050e7..2d9f903be9 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -441,6 +441,11 @@ private: ExprResult ParseInitializer(); ExprResult ParseInitializerWithPotentialDesignator(); + //===--------------------------------------------------------------------===// + // clang Expressions + + ExprResult ParseBlockLiteralExpression(); // ^{...} + //===--------------------------------------------------------------------===// // Objective-C Expressions diff --git a/include/clang/Parse/Scope.h b/include/clang/Parse/Scope.h index 077809132c..30e70f8276 100644 --- a/include/clang/Parse/Scope.h +++ b/include/clang/Parse/Scope.h @@ -45,7 +45,13 @@ public: DeclScope = 0x08, /// CXXClassScope - The scope of a C++ struct/union/class definition. - CXXClassScope = 0x10 + CXXClassScope = 0x10, + + /// BlockScope - This is a scope that corresponds to a block object. + /// Blocks serve as top-level scopes for some objects like labels, they + /// also prevent things like break and continue. BlockScopes have the + /// other flags set as well. + BlockScope = 0x20 }; private: /// The parent scope for this scope. This is null for the translation-unit diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index a4b54f25e0..ad820901d5 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -613,8 +613,8 @@ QualType ASTContext::getPointerType(QualType T) { /// getBlockPointerType - Return the uniqued reference to the type for /// a pointer to the specified block. QualType ASTContext::getBlockPointerType(QualType T) { - assert(T->isFunctionType() && "closure of function types only"); - // Unique pointers, to guarantee there is only one closure of a particular + assert(T->isFunctionType() && "block of function types only"); + // Unique pointers, to guarantee there is only one block of a particular // structure. llvm::FoldingSetNodeID ID; BlockPointerType::Profile(ID, T); @@ -624,7 +624,7 @@ QualType ASTContext::getBlockPointerType(QualType T) { BlockPointerTypes.FindNodeOrInsertPos(ID, InsertPos)) return QualType(PT, 0); - // If the closure pointee type isn't canonical, this won't be a canonical + // If the block pointee type isn't canonical, this won't be a canonical // type either so fill in the canonical type field. QualType Canonical; if (!T->isCanonical()) { diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index d4b46982ac..b532078d40 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -21,6 +21,7 @@ #include "clang/Parse/Parser.h" #include "clang/Parse/DeclSpec.h" +#include "clang/Parse/Scope.h" #include "clang/Basic/Diagnostic.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/SmallString.h" @@ -380,6 +381,7 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, unsigned MinPrec) { /// [C++] 'reinterpret_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1] /// [C++] 'static_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1] /// [C++] 'this' [C++ 9.3.2] +/// [clang] '^' block-literal /// /// constant: [C99 6.4.4] /// integer-constant @@ -595,6 +597,11 @@ Parser::ExprResult Parser::ParseCastExpression(bool isUnaryExpression) { if (getLang().ObjC1) return ParsePostfixExpressionSuffix(ParseObjCMessageExpression()); // FALL THROUGH. + case tok::caret: + if (getLang().Blocks) + return ParsePostfixExpressionSuffix(ParseBlockLiteralExpression()); + Diag(Tok, diag::err_expected_expression); + return ExprResult(true); default: UnhandledToken: Diag(Tok, diag::err_expected_expression); @@ -1068,3 +1075,74 @@ bool Parser::ParseExpressionList(ExprListTy &Exprs, CommaLocsTy &CommaLocs) { CommaLocs.push_back(ConsumeToken()); } } + +/// ParseBlockLiteralExpression - Parse a block literal, which roughly looks +/// like ^(int x){ return x+1; } or ^(int y)foo(4, y, z) +/// +/// block-literal: +/// [clang] '^' block-args[opt] compound-statement +/// [clang] '^' block-args cast-expression +/// [clang] block-args: +/// [clang] '(' parameter-list ')' +/// +Parser::ExprResult Parser::ParseBlockLiteralExpression() { + assert(Tok.is(tok::caret) && "block literal starts with ^"); + SourceLocation CaretLoc = ConsumeToken(); + + // Enter a scope to hold everything within the block. This includes the + // argument decls, decls within the compound expression, etc. This also + // allows determining whether a variable reference inside the block is + // within or outside of the block. + EnterScope(Scope::BlockScope|Scope::FnScope|Scope::BreakScope| + Scope::ContinueScope|Scope::DeclScope); + + // Parse the return type if present. + DeclSpec DS; + Declarator ParamInfo(DS, Declarator::PrototypeContext); + + // If this block has arguments, parse them. There is no ambiguity here with + // the expression case, because the expression case requires a parameter list. + if (Tok.is(tok::l_paren)) { + ParseParenDeclarator(ParamInfo); + // Parse the pieces after the identifier as if we had "int(...)". + ParamInfo.SetIdentifier(0, CaretLoc); + if (ParamInfo.getInvalidType()) { + // If there was an error parsing the arguments, they may have tried to use + // ^(x+y) which requires an argument list. Just skip the whole block + // literal. + ExitScope(); + return true; + } + } else { + // Otherwise, pretend we saw (void). + ParamInfo.AddTypeInfo(DeclaratorChunk::getFunction(true, false, + 0, 0, CaretLoc)); + } + + // Inform sema that we are starting a block. + Actions.ActOnBlockStart(CaretLoc, CurScope, ParamInfo); + + ExprResult Result; + if (Tok.is(tok::l_brace)) { + StmtResult Stmt = ParseCompoundStatementBody(); + if (!Stmt.isInvalid) { + Result = Actions.ActOnBlockStmtExpr(CaretLoc, Stmt.Val, CurScope); + } else { + Actions.ActOnBlockError(CaretLoc, CurScope); + Result = true; + } + } else { + ExprResult Expr = ParseCastExpression(false); + if (!Expr.isInvalid) { + Result = Actions.ActOnBlockExprExpr(CaretLoc, Expr.Val, CurScope); + } else { + Actions.ActOnBlockError(CaretLoc, CurScope); + Diag(Tok, diag::err_expected_block_lbrace); + Result = true; + } + } + + ExitScope(); + return Result; +} + diff --git a/test/Parser/block-pointer-decl.c b/test/Parser/block-pointer-decl.c index eb7ebcb402..a9da3256a9 100644 --- a/test/Parser/block-pointer-decl.c +++ b/test/Parser/block-pointer-decl.c @@ -7,12 +7,20 @@ struct blockStruct { int blockTaker (int (^myBlock)(int), int other_input) { - return 0; + return 5 * myBlock (other_input); } int main (int argc, char **argv) { - int (^blockptr) (int); + int (^blockptr) (int) = ^(int inval) { + printf ("Inputs: %d, %d.\n", argc, inval); + return argc * inval; + }; + + + argc = 10; + printf ("I got: %d.\n", + blockTaker (blockptr, 6)); return 0; } -- 2.40.0