/// 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
///
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; }
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<Scope*>(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<Scope*>(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(); }
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();
}
DR->getDecl()->getIdentifier()->getName(),
RetValExp->getSourceRange());
- if (BlockExpr *C = dyn_cast_or_null<BlockExpr>(EvalAddr(RetValExp)))
+ if (BlockExpr *C = dyn_cast_or_null<BlockExpr>(RetValExp))
Diag(C->getLocStart(), diag::err_ret_local_block,
C->getSourceRange());
}
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: {
}
// 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;
}
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(),
--- /dev/null
+// 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