]> granicus.if.org Git - clang/commitdiff
More semantic analysis for blocks...
authorSteve Naroff <snaroff@apple.com>
Wed, 10 Sep 2008 19:17:48 +0000 (19:17 +0000)
committerSteve Naroff <snaroff@apple.com>
Wed, 10 Sep 2008 19:17:48 +0000 (19:17 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@56064 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Parse/Scope.h
lib/Sema/SemaChecking.cpp
lib/Sema/SemaExpr.cpp
test/Sema/block-literal.c [new file with mode: 0644]

index d0626ffc90face446c1efa8b9b8c43db26180821..17a61c78f1e6d7793bc505a3f083d5bfd1f31a71 100644 (file)
@@ -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<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(); }
@@ -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();
   }      
index 8c59039ac2ea3369293f61a0c5dc95f4ee8569c0..00b469b7c05cff7d866e14029ba0c1f127659f73 100644 (file)
@@ -717,7 +717,7 @@ Sema::CheckReturnStackAddr(Expr *RetValExp, QualType lhsType,
            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());
   }
@@ -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: {
index 80b33f208794ceea97b17f997baa552a00f2ce77..aa8f194e8f981f5eff6f87ff4861e6af9c6d9356 100644 (file)
@@ -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 (file)
index 0000000..a3b9562
--- /dev/null
@@ -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