From 61f40a2b67fc2046768e14f66b617e564cbcc3d8 Mon Sep 17 00:00:00 2001 From: Steve Naroff Date: Wed, 10 Sep 2008 19:17:48 +0000 Subject: [PATCH] More semantic analysis for blocks... git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@56064 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Parse/Scope.h | 43 ++++++++++--- lib/Sema/SemaChecking.cpp | 4 +- lib/Sema/SemaExpr.cpp | 11 +++- test/Sema/block-literal.c | 116 ++++++++++++++++++++++++++++++++++++ 4 files changed, 162 insertions(+), 12 deletions(-) create mode 100644 test/Sema/block-literal.c diff --git a/include/clang/Parse/Scope.h b/include/clang/Parse/Scope.h index d0626ffc90..17a61c78f1 100644 --- a/include/clang/Parse/Scope.h +++ b/include/clang/Parse/Scope.h @@ -77,6 +77,10 @@ private: /// preceeding BreakParent/ContinueParent if this scope is not one, or null if /// there is no containing break/continue scope. Scope *BreakParent, *ContinueParent; + + /// BlockParent - This is a direct link to the immediately containing + /// BlockScope if this scope is not one, or null if there is none. + Scope *BlockParent; /// DeclsInScope - This keeps track of all declarations in this scope. When /// the declaration is added to the scope, it is set as the current @@ -96,6 +100,10 @@ public: /// unsigned getFlags() const { return Flags; } + /// isBlockScope - Return true if this scope does not correspond to a + /// closure. + bool isBlockScope() const { return Flags & BlockScope; } + /// getParent - Return the scope that this is nested in. /// const Scope *getParent() const { return AnyParent; } @@ -107,15 +115,32 @@ public: Scope *getFnParent() { return FnParent; } /// getContinueParent - Return the closest scope that a continue statement - /// would be affected by. - const Scope *getContinueParent() const { return ContinueParent; } - Scope *getContinueParent() { return ContinueParent; } + /// would be affected by. If the closest scope is a closure scope, we know + /// that there is no loop *inside* the closure. + Scope *getContinueParent() { + if (ContinueParent && !ContinueParent->isBlockScope()) + return ContinueParent; + return 0; + } + + const Scope *getContinueParent() const { + return const_cast(this)->getContinueParent(); + } /// getBreakParent - Return the closest scope that a break statement - /// would be affected by. - const Scope *getBreakParent() const { return BreakParent; } - Scope *getBreakParent() { return BreakParent; } - + /// would be affected by. If the closest scope is a block scope, we know + /// that there is no loop *inside* the block. + Scope *getBreakParent() { + if (BreakParent && !BreakParent->isBlockScope()) + return BreakParent; + return 0; + } + const Scope *getBreakParent() const { + return const_cast(this)->getBreakParent(); + } + + Scope *getBlockParent() { return BlockParent; } + const Scope *getBlockParent() const { return BlockParent; } typedef DeclSetTy::iterator decl_iterator; decl_iterator decl_begin() const { return DeclsInScope.begin(); } @@ -158,14 +183,16 @@ public: FnParent = AnyParent->FnParent; BreakParent = AnyParent->BreakParent; ContinueParent = AnyParent->ContinueParent; + BlockParent = AnyParent->BlockParent; } else { - FnParent = BreakParent = ContinueParent = 0; + FnParent = BreakParent = ContinueParent = BlockParent = 0; } // If this scope is a function or contains breaks/continues, remember it. if (Flags & FnScope) FnParent = this; if (Flags & BreakScope) BreakParent = this; if (Flags & ContinueScope) ContinueParent = this; + if (Flags & BlockScope) BlockParent = this; DeclsInScope.clear(); } diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index 8c59039ac2..00b469b7c0 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -717,7 +717,7 @@ Sema::CheckReturnStackAddr(Expr *RetValExp, QualType lhsType, DR->getDecl()->getIdentifier()->getName(), RetValExp->getSourceRange()); - if (BlockExpr *C = dyn_cast_or_null(EvalAddr(RetValExp))) + if (BlockExpr *C = dyn_cast_or_null(RetValExp)) Diag(C->getLocStart(), diag::err_ret_local_block, C->getSourceRange()); } @@ -797,7 +797,7 @@ static DeclRefExpr* EvalAddr(Expr *E) { assert (Base->getType()->isPointerType()); return EvalAddr(Base); } - + // For conditional operators we need to see if either the LHS or RHS are // valid DeclRefExpr*s. If one of them is valid, we return it. case Stmt::ConditionalOperatorClass: { diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 80b33f2087..aa8f194e8f 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -1296,11 +1296,13 @@ inline QualType Sema::CheckConditionalOperands( // C99 6.5.15 } // C99 6.5.15p6 - "if one operand is a null pointer constant, the result has // the type of the other operand." - if (lexT->isPointerType() && rex->isNullPointerConstant(Context)) { + if ((lexT->isPointerType() || lexT->isBlockPointerType()) && + rex->isNullPointerConstant(Context)) { ImpCastExprToType(rex, lexT); // promote the null to a pointer. return lexT; } - if (rexT->isPointerType() && lex->isNullPointerConstant(Context)) { + if ((rexT->isPointerType() || rexT->isBlockPointerType()) && + lex->isNullPointerConstant(Context)) { ImpCastExprToType(lex, rexT); // promote the null to a pointer. return rexT; } @@ -1381,6 +1383,11 @@ inline QualType Sema::CheckConditionalOperands( // C99 6.5.15 return compositeType; } } + // Selection between block pointer types is ok as long as they are the same. + if (lexT->isBlockPointerType() && rexT->isBlockPointerType() && + Context.getCanonicalType(lexT) == Context.getCanonicalType(rexT)) + return lexT; + // Otherwise, the operands are not compatible. Diag(questionLoc, diag::err_typecheck_cond_incompatible_operands, lexT.getAsString(), rexT.getAsString(), diff --git a/test/Sema/block-literal.c b/test/Sema/block-literal.c new file mode 100644 index 0000000000..a3b9562ee9 --- /dev/null +++ b/test/Sema/block-literal.c @@ -0,0 +1,116 @@ +// RUN: clang -fsyntax-only %s -verify + +void I( void (^)(void)); +void (^noop)(void); + +void nothing(); +int printf(const char*, ...); + +typedef void (^T) (void); + +void takeclosure(T); +int takeintint(int (^C)(int)) { return C(4); } + +T somefunction() { + if (^{ }) + nothing(); + + noop = ^{}; + + noop = ^{printf("\nClosure\n"); }; + + I(^{ }); + + noop = ^noop; // expected-error {{incompatible block pointer types}} + + return ^{printf("\nClosure\n"); }; // expected-error {{returning block that lives on the local stack}} +} +void test2() { + int x = 4; + + takeclosure(^{ printf("%d\n", x); }); + + while (1) { + takeclosure(^{ + break; // expected-error {{'break' statement not in loop or switch statement}} + continue; // expected-error {{'continue' statement not in loop statement}} + while(1) break; // ok + goto foo; // expected-error {{goto not allowed}} + }); + break; + } + +foo: + takeclosure(^{ x = 4; }); // expected-error {{expression is not assignable}} + + takeclosure(^test2()); + takeclosure(^(void)(void)printf("hello world!\n")); + +} + + +void (^test3())(void) { + return ^{}; // expected-error {{returning block that lives on the local stack}} +} + +void test4() { + void (^noop)(void) = ^{}; + void (*noop2)() = 0; +} + +void *X; + +void test_arguments() { + takeintint(^(int x)(x+1)); + + // Closure expr of statement expr. + takeintint(^(int x)({ return 42; })); // expected-error {{return not allowed in block expression literal}} + + int y; + takeintint(^(int x)(x+y)); +#if 0 + // FIXME: this causes clang to crash. + X = ^(x+r); // expected-error {{expected ')' in argument list}} +#endif + int (^c)(char); + (1 ? c : 0)('x'); + (1 ? 0 : c)('x'); + + (1 ? c : c)('x'); +} + +#if 0 +// Old syntax. FIXME: convert/test. +void test_byref() { + int i; + + X = ^{| g |}; // expected-error {{use of undeclared identifier 'g'}} + + X = ^{| i,i,i | }; + + X = ^{|i| i = 0; }; + +} + +// TODO: global closures someday. +void *A = ^{}; +void *B = ^(int){ A = 0; }; + + +// Closures can not take return types at this point. +void test_retvals() { + // Explicit return value. + ^int{}; // expected-error {{closure with explicit return type requires argument list}} + X = ^void(){}; + + // Optional specification of return type. + X = ^char{ return 'x'; }; // expected-error {{closure with explicit return type requires argument list}} + + X = ^/*missing declspec*/ *() { return (void*)0; }; + X = ^void*() { return (void*)0; }; + + //X = ^char(short c){ if (c) return c; else return (int)4; }; + +} + +#endif -- 2.40.0