Add semantic analysis for "blocks".
Highlights...
- 4 new AST nodes, BlockExpr, BlockStmtExpr, BlockExprExpr, BlockDeclRefExpr.
- Sema::ActOnBlockStart(), ActOnBlockError(), ActOnBlockStmtExpr(), ActOnBlockExprExpr(), ActOnBlockReturnStmt().
Next steps...
- hack Sema::ActOnIdentifierExpr() to deal with block decl refs.
- add attribute handler for byref decls.
- add test cases.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@55710 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp
index 30ada28..65f1d68 100644
--- a/lib/Sema/Sema.cpp
+++ b/lib/Sema/Sema.cpp
@@ -87,7 +87,7 @@
}
Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer)
- : PP(pp), Context(ctxt), Consumer(consumer), CurContext(0) {
+ : PP(pp), Context(ctxt), Consumer(consumer), CurContext(0), CurBlock(0) {
// Get IdentifierInfo objects for known functions for which we
// do extra checking.
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index 1ce4d80..faa16ca 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -64,6 +64,7 @@
class ObjCIvarDecl;
class ObjCMethodDecl;
class ObjCPropertyDecl;
+ struct BlockSemaInfo;
/// Sema - This implements semantic analysis and AST building for C.
class Sema : public Action {
@@ -75,6 +76,10 @@
/// CurContext - This is the current declaration context of parsing.
DeclContext *CurContext;
+ /// CurBlock - If inside of a block definition, this contains a pointer to
+ /// the active block object that represents it.
+ BlockSemaInfo *CurBlock;
+
/// LabelMap - This is a mapping from label identifiers to the LabelStmt for
/// it (which acts like the label decl in some ways). Forward referenced
/// labels have a LabelStmt created for them with a null location & SubStmt.
@@ -408,6 +413,7 @@
virtual StmtResult ActOnReturnStmt(SourceLocation ReturnLoc,
ExprTy *RetValExp);
+ StmtResult ActOnBlockReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp);
virtual StmtResult ActOnAsmStmt(SourceLocation AsmLoc,
bool IsSimple,
@@ -536,6 +542,27 @@
virtual ExprResult ActOnVAArg(SourceLocation BuiltinLoc,
ExprTy *expr, TypeTy *type,
SourceLocation RPLoc);
+
+ //===------------------------- "Block" Extension ------------------------===//
+
+ /// ActOnBlockStart - This callback is invoked when a block literal is
+ /// started.
+ 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);
+
+ /// 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);
// Act on C++ namespaces
virtual DeclTy *ActOnStartNamespaceDef(Scope *S, SourceLocation IdentLoc,
@@ -960,6 +987,27 @@
bool HadError() { return hadError; }
};
+/// BlockSemaInfo - When a block is being parsed, this contains information
+/// about the block. It is pointed to from Sema::CurBlock.
+struct BlockSemaInfo {
+ llvm::SmallVector<ParmVarDecl*, 8> Params;
+ llvm::SmallPtrSet<Decl*, 4> ByRefVars;
+ bool hasPrototype;
+ bool isVariadic;
+
+ /// TheScope - This is the scope for the block itself, which contains
+ /// arguments etc.
+ Scope *TheScope;
+
+ /// ReturnType - This will get set to block result type, by looking at
+ /// return types, if any, in the block body.
+ Type *ReturnType;
+
+ /// PrevBlockInfo - If this is nested inside another block, this points
+ /// to the outer block.
+ BlockSemaInfo *PrevBlockInfo;
+};
+
} // end namespace clang
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index 7886976..14fec20 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -21,6 +21,8 @@
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
+#include "clang/Parse/DeclSpec.h"
+#include "clang/Parse/Scope.h"
using namespace clang;
//===----------------------------------------------------------------------===//
@@ -2607,6 +2609,124 @@
return new ChooseExpr(BuiltinLoc, CondExpr, LHSExpr, RHSExpr, resType, RPLoc);
}
+//===----------------------------------------------------------------------===//
+// Clang Extensions.
+//===----------------------------------------------------------------------===//
+
+/// ActOnBlockStart - This callback is invoked when a block literal is started.
+void Sema::ActOnBlockStart(SourceLocation CaretLoc, Scope *BlockScope,
+ Declarator &ParamInfo) {
+ // Analyze block parameters.
+ BlockSemaInfo *BSI = new BlockSemaInfo();
+
+ // Add BSI to CurBlock.
+ BSI->PrevBlockInfo = CurBlock;
+ CurBlock = BSI;
+
+ BSI->ReturnType = 0;
+ BSI->TheScope = BlockScope;
+
+ // Analyze arguments to block.
+ assert(ParamInfo.getTypeObject(0).Kind == DeclaratorChunk::Function &&
+ "Not a function declarator!");
+ DeclaratorChunk::FunctionTypeInfo &FTI = ParamInfo.getTypeObject(0).Fun;
+
+ BSI->hasPrototype = FTI.hasPrototype;
+ BSI->isVariadic = true;
+
+ // Check for C99 6.7.5.3p10 - foo(void) is a non-varargs function that takes
+ // no arguments, not a function that takes a single void argument.
+ if (FTI.hasPrototype &&
+ FTI.NumArgs == 1 && !FTI.isVariadic && FTI.ArgInfo[0].Ident == 0 &&
+ (!((ParmVarDecl *)FTI.ArgInfo[0].Param)->getType().getCVRQualifiers() &&
+ ((ParmVarDecl *)FTI.ArgInfo[0].Param)->getType()->isVoidType())) {
+ // empty arg list, don't push any params.
+ BSI->isVariadic = false;
+ } else if (FTI.hasPrototype) {
+ for (unsigned i = 0, e = FTI.NumArgs; i != e; ++i)
+ BSI->Params.push_back((ParmVarDecl *)FTI.ArgInfo[i].Param);
+ BSI->isVariadic = FTI.isVariadic;
+ }
+}
+
+/// ActOnBlockError - If there is an error parsing a block, this callback
+/// is invoked to pop the information about the block from the action impl.
+void Sema::ActOnBlockError(SourceLocation CaretLoc, Scope *CurScope) {
+ // Ensure that CurBlock is deleted.
+ llvm::OwningPtr<BlockSemaInfo> CC(CurBlock);
+
+ // Pop off CurBlock, handle nested blocks.
+ CurBlock = CurBlock->PrevBlockInfo;
+
+ // FIXME: Delete the ParmVarDecl objects as well???
+
+}
+
+/// ActOnBlockStmtExpr - This is called when the body of a block statement
+/// literal was successfully completed. ^(int x){...}
+Sema::ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc, StmtTy *body,
+ Scope *CurScope) {
+ // Ensure that CurBlock is deleted.
+ llvm::OwningPtr<BlockSemaInfo> BSI(CurBlock);
+ llvm::OwningPtr<CompoundStmt> Body(static_cast<CompoundStmt*>(body));
+
+ // Pop off CurBlock, handle nested blocks.
+ CurBlock = CurBlock->PrevBlockInfo;
+
+ QualType RetTy = Context.VoidTy;
+ if (BSI->ReturnType)
+ RetTy = QualType(BSI->ReturnType, 0);
+
+ llvm::SmallVector<QualType, 8> ArgTypes;
+ for (unsigned i = 0, e = BSI->Params.size(); i != e; ++i)
+ ArgTypes.push_back(BSI->Params[i]->getType());
+
+ QualType BlockTy;
+ if (!BSI->hasPrototype)
+ BlockTy = Context.getFunctionTypeNoProto(RetTy);
+ else
+ BlockTy = Context.getFunctionType(RetTy, &ArgTypes[0], ArgTypes.size(),
+ BSI->isVariadic);
+
+ BlockTy = Context.getBlockPointerType(BlockTy);
+ return new BlockStmtExpr(CaretLoc, BlockTy,
+ &BSI->Params[0], BSI->Params.size(), Body.take());
+}
+
+/// ActOnBlockExprExpr - This is called when the body of a block
+/// expression literal was successfully completed. ^(int x)[foo bar: x]
+Sema::ExprResult Sema::ActOnBlockExprExpr(SourceLocation CaretLoc, ExprTy *body,
+ Scope *CurScope) {
+ // Ensure that CurBlock is deleted.
+ llvm::OwningPtr<BlockSemaInfo> BSI(CurBlock);
+ llvm::OwningPtr<Expr> Body(static_cast<Expr*>(body));
+
+ // Pop off CurBlock, handle nested blocks.
+ CurBlock = CurBlock->PrevBlockInfo;
+
+ if (BSI->ReturnType) {
+ Diag(CaretLoc, diag::err_return_in_block_expression);
+ return true;
+ }
+
+ QualType RetTy = Body->getType();
+
+ llvm::SmallVector<QualType, 8> ArgTypes;
+ for (unsigned i = 0, e = BSI->Params.size(); i != e; ++i)
+ ArgTypes.push_back(BSI->Params[i]->getType());
+
+ QualType BlockTy;
+ if (!BSI->hasPrototype)
+ BlockTy = Context.getFunctionTypeNoProto(RetTy);
+ else
+ BlockTy = Context.getFunctionType(RetTy, &ArgTypes[0], ArgTypes.size(),
+ BSI->isVariadic);
+
+ BlockTy = Context.getBlockPointerType(BlockTy);
+ return new BlockExprExpr(CaretLoc, BlockTy,
+ &BSI->Params[0], BSI->Params.size(), Body.take());
+}
+
/// ExprsMatchFnType - return true if the Exprs in array Args have
/// QualTypes that match the QualTypes of the arguments of the FnType.
/// The number of arguments has already been validated to match the number of
diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp
index 629ab34..98efc8a 100644
--- a/lib/Sema/SemaStmt.cpp
+++ b/lib/Sema/SemaStmt.cpp
@@ -588,6 +588,10 @@
Action::StmtResult
Sema::ActOnGotoStmt(SourceLocation GotoLoc, SourceLocation LabelLoc,
IdentifierInfo *LabelII) {
+ // If we are in a block, reject all gotos for now.
+ if (CurBlock)
+ return Diag(GotoLoc, diag::err_goto_in_block);
+
// Look up the record for this label identifier.
LabelStmt *&LabelDecl = LabelMap[LabelII];
@@ -630,10 +634,61 @@
return new BreakStmt(BreakLoc);
}
+/// ActOnBlockReturnStmt - Utilty routine to figure out block's return type.
+///
+Action::StmtResult
+Sema::ActOnBlockReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
+
+ // If this is the first return we've seen in the block, infer the type of
+ // the block from it.
+ if (CurBlock->ReturnType == 0) {
+ if (RetValExp)
+ CurBlock->ReturnType = RetValExp->getType().getTypePtr();
+ else
+ CurBlock->ReturnType = Context.VoidTy.getTypePtr();
+ return new ReturnStmt(ReturnLoc, RetValExp);
+ }
+
+ // Otherwise, verify that this result type matches the previous one. We are
+ // pickier with blocks than for normal functions because we don't have GCC
+ // compatibility to worry about here.
+ if (CurBlock->ReturnType->isVoidType()) {
+ if (RetValExp) {
+ Diag(ReturnLoc, diag::err_return_block_has_expr);
+ delete RetValExp;
+ RetValExp = 0;
+ }
+ return new ReturnStmt(ReturnLoc, RetValExp);
+ }
+
+ if (!RetValExp) {
+ Diag(ReturnLoc, diag::err_block_return_missing_expr);
+ return true;
+ }
+
+ // we have a non-void block with an expression, continue checking
+ QualType RetValType = RetValExp->getType();
+
+ // For now, restrict multiple return statements in a block to have
+ // strict compatible types only.
+ QualType BlockQT = QualType(CurBlock->ReturnType, 0);
+ if (Context.getCanonicalType(BlockQT).getTypePtr()
+ != Context.getCanonicalType(RetValType).getTypePtr()) {
+ DiagnoseAssignmentResult(Incompatible, ReturnLoc, BlockQT,
+ RetValType, RetValExp, "returning");
+ return true;
+ }
+
+ if (RetValExp) CheckReturnStackAddr(RetValExp, BlockQT, ReturnLoc);
+
+ return new ReturnStmt(ReturnLoc, (Expr*)RetValExp);
+}
Action::StmtResult
Sema::ActOnReturnStmt(SourceLocation ReturnLoc, ExprTy *rex) {
Expr *RetValExp = static_cast<Expr *>(rex);
+ if (CurBlock)
+ return ActOnBlockReturnStmt(ReturnLoc, RetValExp);
QualType FnRetType =
getCurFunctionDecl() ? getCurFunctionDecl()->getResultType() :
getCurMethodDecl()->getResultType();