diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index d8c0ef7e68..b5caa6b77b 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -1022,8 +1022,14 @@ DIAG(err_typecheck_call_not_function, ERROR, "called object is not a function or function pointer") DIAG(err_typecheck_call_too_few_args, ERROR, "too few arguments to function") +DIAG(err_typecheck_block_too_few_args, ERROR, + "too few arguments to block") DIAG(err_typecheck_call_too_many_args, ERROR, "too many arguments to function") +DIAG(err_typecheck_block_too_many_args, ERROR, + "too many arguments to block call") +DIAG(err_typecheck_closure_too_many_args, ERROR, + "too many arguments to closure call") DIAG(err_typecheck_call_invalid_ordered_compare, ERROR, "ordered compare requires two args of floating point type ('%0' and '%1')") DIAG(err_typecheck_cond_expect_scalar, ERROR, @@ -1145,6 +1151,9 @@ DIAG(err_goto_in_block, ERROR, DIAG(err_return_in_block_expression, ERROR, "return not allowed in block expression literal") +DIAG(err_ret_local_block, ERROR, + "returning block that lives on the local stack") + // CFString checking DIAG(err_cfstring_literal_not_string_constant, ERROR, "CFString literal is not a string constant") diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 4caf16c4b0..62364bab31 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -1781,7 +1781,19 @@ bool ASTContext::isObjCObjectPointerType(QualType Ty) const { //===----------------------------------------------------------------------===// /// typesAreBlockCompatible - This routine is called when comparing two -/// block types. Types must be strictly compatible here. +/// block types. Types must be strictly compatible here. For example, +/// C unfortunately doesn't produce an error for the following: +/// +/// int (*emptyArgFunc)(); +/// int (*intArgList)(int) = emptyArgFunc; +/// +/// For blocks, we will produce an error for the following (similar to C++): +/// +/// int (^emptyArgBlock)(); +/// int (^intArgBlock)(int) = emptyArgBlock; +/// +/// FIXME: When the dust settles on this integration, fold this into mergeTypes. +/// bool ASTContext::typesAreBlockCompatible(QualType lhs, QualType rhs) { if (lhs.getCVRQualifiers() != rhs.getCVRQualifiers()) return false; diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index f72ff4f6ca..9452db4677 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -423,6 +423,12 @@ Expr::isLvalueResult Expr::isLvalue(ASTContext &Ctx) const { return LV_Valid; break; } + case BlockDeclRefExprClass: { + const BlockDeclRefExpr *BDR = cast(this); + if (BDR->isByRef() && isa(BDR->getDecl())) + return LV_Valid; + break; + } case MemberExprClass: { // C99 6.5.2.3p4 const MemberExpr *m = cast(this); return m->isArrow() ? LV_Valid : m->getBase()->isLvalue(Ctx); @@ -1453,4 +1459,6 @@ Stmt::child_iterator BlockExprExpr::child_begin() { Stmt::child_iterator BlockExprExpr::child_end() { return reinterpret_cast(&BodyExpr)+1; } +Stmt::child_iterator BlockDeclRefExpr::child_begin(){return child_iterator();} +Stmt::child_iterator BlockDeclRefExpr::child_end() { return child_iterator();} diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index 1c1c594edc..8c59039ac2 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -711,11 +711,15 @@ Sema::CheckReturnStackAddr(Expr *RetValExp, QualType lhsType, SourceLocation ReturnLoc) { // Perform checking for returned stack addresses. - if (lhsType->isPointerType()) { + if (lhsType->isPointerType() || lhsType->isBlockPointerType()) { if (DeclRefExpr *DR = EvalAddr(RetValExp)) Diag(DR->getLocStart(), diag::warn_ret_stack_addr, DR->getDecl()->getIdentifier()->getName(), RetValExp->getSourceRange()); + + if (BlockExpr *C = dyn_cast_or_null(EvalAddr(RetValExp))) + Diag(C->getLocStart(), diag::err_ret_local_block, + C->getSourceRange()); } // Perform checking for stack values returned by reference. else if (lhsType->isReferenceType()) { @@ -751,7 +755,8 @@ Sema::CheckReturnStackAddr(Expr *RetValExp, QualType lhsType, /// * taking the address of an array element where the array is on the stack static DeclRefExpr* EvalAddr(Expr *E) { // We should only be called for evaluating pointer expressions. - assert((E->getType()->isPointerType() || + assert((E->getType()->isPointerType() || + E->getType()->isBlockPointerType() || E->getType()->isObjCQualifiedIdType()) && "EvalAddr only works on pointers"); @@ -814,7 +819,9 @@ static DeclRefExpr* EvalAddr(Expr *E) { Expr* SubExpr = cast(E)->getSubExpr(); QualType T = SubExpr->getType(); - if (T->isPointerType() || T->isObjCQualifiedIdType()) + if (SubExpr->getType()->isPointerType() || + SubExpr->getType()->isBlockPointerType() || + SubExpr->getType()->isObjCQualifiedIdType()) return EvalAddr(SubExpr); else if (T->isArrayType()) return EvalVal(SubExpr); @@ -832,7 +839,7 @@ static DeclRefExpr* EvalAddr(Expr *E) { if (C->getOpcode() == CXXCastExpr::ReinterpretCast) { Expr *S = C->getSubExpr(); - if (S->getType()->isPointerType()) + if (S->getType()->isPointerType() || S->getType()->isBlockPointerType()) return EvalAddr(S); else return NULL; diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 02169e6db8..8a297ac1d7 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -299,6 +299,24 @@ Sema::ActOnStringLiteral(const Token *StringToks, unsigned NumStringToks) { StringToks[NumStringToks-1].getLocation()); } +/// DeclDefinedWithinScope - Return true if the specified decl is defined at or +/// within the 'Within' scope. The current Scope is CurScope. +/// +/// NOTE: This method is extremely inefficient (linear scan), this should not be +/// used in common cases. +/// +static bool DeclDefinedWithinScope(ScopedDecl *D, Scope *Within, + Scope *CurScope) { + while (1) { + assert(CurScope && "CurScope not nested within 'Within'?"); + + // Check this scope for the decl. + if (CurScope->isDeclScope(D)) return true; + + if (CurScope == Within) return false; + CurScope = CurScope->getParent(); + } +} /// ActOnIdentifierExpr - The parser read an identifier in expression context, /// validate it per-C99 6.5.1. HasTrailingLParen indicates whether this @@ -350,17 +368,6 @@ Sema::ExprResult Sema::ActOnIdentifierExpr(Scope *S, SourceLocation Loc, } } - if (ValueDecl *VD = dyn_cast(D)) { - // check if referencing an identifier with __attribute__((deprecated)). - if (VD->getAttr()) - Diag(Loc, diag::warn_deprecated, VD->getName()); - - // Only create DeclRefExpr's for valid Decl's. - if (VD->isInvalidDecl()) - return true; - return new DeclRefExpr(VD, VD->getType(), Loc); - } - if (CXXFieldDecl *FD = dyn_cast(D)) { if (CXXMethodDecl *MD = dyn_cast(CurContext)) { if (MD->isStatic()) @@ -383,7 +390,6 @@ Sema::ExprResult Sema::ActOnIdentifierExpr(Scope *S, SourceLocation Loc, return Diag(Loc, diag::err_invalid_non_static_member_use, FD->getName()); } - if (isa(D)) return Diag(Loc, diag::err_unexpected_typedef, II.getName()); if (isa(D)) @@ -391,8 +397,36 @@ Sema::ExprResult Sema::ActOnIdentifierExpr(Scope *S, SourceLocation Loc, if (isa(D)) return Diag(Loc, diag::err_unexpected_namespace, II.getName()); - assert(0 && "Invalid decl"); - abort(); + // Make the DeclRefExpr or BlockDeclRefExpr for the decl. + ValueDecl *VD = cast(D); + + // check if referencing an identifier with __attribute__((deprecated)). + if (VD->getAttr()) + Diag(Loc, diag::warn_deprecated, VD->getName()); + + // Only create DeclRefExpr's for valid Decl's. + if (VD->isInvalidDecl()) + return true; + + // If this reference is not in a block or if the referenced variable is + // within the block, create a normal DeclRefExpr. + // + // FIXME: This will create BlockDeclRefExprs for global variables, + // function references, enums constants, etc which is suboptimal :) and breaks + // things like "integer constant expression" tests. + // + if (!CurBlock || DeclDefinedWithinScope(VD, CurBlock->TheScope, S)) + return new DeclRefExpr(VD, VD->getType(), Loc); + + // If we are in a block and the variable is outside the current block, + // bind the variable reference with a BlockDeclRefExpr. + + // If the variable is in the byref set, bind it directly, otherwise it will be + // bound by-copy, thus we make it const within the closure. + if (!CurBlock->ByRefVars.count(VD)) + VD->getType().addConst(); + + return new BlockDeclRefExpr(VD, VD->getType(), Loc, false); } Sema::ExprResult Sema::ActOnPredefinedExpr(SourceLocation Loc, @@ -983,14 +1017,19 @@ ActOnCallExpr(ExprTy *fn, SourceLocation LParenLoc, // of arguments and function on error. llvm::OwningPtr TheCall(new CallExpr(Fn, Args, NumArgs, Context.BoolTy, RParenLoc)); - - // C99 6.5.2.2p1 - "The expression that denotes the called function shall have - // type pointer to function". - const PointerType *PT = Fn->getType()->getAsPointerType(); - if (PT == 0) - return Diag(LParenLoc, diag::err_typecheck_call_not_function, - Fn->getSourceRange()); - const FunctionType *FuncT = PT->getPointeeType()->getAsFunctionType(); + const FunctionType *FuncT; + if (!Fn->getType()->isBlockPointerType()) { + // C99 6.5.2.2p1 - "The expression that denotes the called function shall + // have type pointer to function". + const PointerType *PT = Fn->getType()->getAsPointerType(); + if (PT == 0) + return Diag(LParenLoc, diag::err_typecheck_call_not_function, + Fn->getSourceRange()); + FuncT = PT->getPointeeType()->getAsFunctionType(); + } else { // This is a block call. + FuncT = Fn->getType()->getAsBlockPointerType()->getPointeeType()-> + getAsFunctionType(); + } if (FuncT == 0) return Diag(LParenLoc, diag::err_typecheck_call_not_function, Fn->getSourceRange()); @@ -1012,7 +1051,10 @@ ActOnCallExpr(ExprTy *fn, SourceLocation LParenLoc, NumArgsToCheck = NumArgsInProto; TheCall->setNumArgs(NumArgsInProto); } else - return Diag(RParenLoc, diag::err_typecheck_call_too_few_args, + return Diag(RParenLoc, + !Fn->getType()->isBlockPointerType() + ? diag::err_typecheck_call_too_few_args + : diag::err_typecheck_block_too_few_args, Fn->getSourceRange()); } @@ -1021,7 +1063,10 @@ ActOnCallExpr(ExprTy *fn, SourceLocation LParenLoc, if (NumArgs > NumArgsInProto) { if (!Proto->isVariadic()) { Diag(Args[NumArgsInProto]->getLocStart(), - diag::err_typecheck_call_too_many_args, Fn->getSourceRange(), + !Fn->getType()->isBlockPointerType() + ? diag::err_typecheck_call_too_many_args + : diag::err_typecheck_block_too_many_args, + Fn->getSourceRange(), SourceRange(Args[NumArgsInProto]->getLocStart(), Args[NumArgs-1]->getLocEnd())); // This deletes the extra arguments. @@ -1529,8 +1574,8 @@ Sema::CheckAssignmentConstraints(QualType lhsType, QualType rhsType) { if (isa(rhsType)) return CheckPointerTypesForAssignment(lhsType, rhsType); - if (const BlockPointerType *BPT = rhsType->getAsBlockPointerType()) - if (BPT->getPointeeType()->isVoidType()) + if (rhsType->getAsBlockPointerType()) + if (lhsType->getAsPointerType()->getPointeeType()->isVoidType()) return BlockVoidPointer; return Incompatible; diff --git a/test/Sema/block-call.c b/test/Sema/block-call.c new file mode 100644 index 0000000000..3be4e6c438 --- /dev/null +++ b/test/Sema/block-call.c @@ -0,0 +1,55 @@ +// RUN: clang -fsyntax-only -verify %s + +int (*FP)(); +int (^IFP) (); +int (^II) (int); +int main() { + int (*FPL) (int) = FP; // C doesn't consider this an error. + + // For Blocks, the ASTContext::typesAreBlockCompatible() makes sure this is an error. + int (^PFR) (int) = IFP; // expected-error {{incompatible block pointer types initializing 'int (^)()', expected 'int (^)(int)'}} + PFR = II; // OK + + int (^IFP) () = PFR; // expected-error {{incompatible block pointer types initializing 'int (^)(int)', expected 'int (^)()'}} + + + const int (^CIC) () = IFP; // expected-error {{incompatible block pointer types initializing 'int (^)()', expected 'int const (^)()'}} + + + const int (^CICC) () = CIC; + + int * const (^IPCC) () = 0; + + int * const (^IPCC1) () = IPCC; + + int * (^IPCC2) () = IPCC; // expected-error {{incompatible block pointer types initializing 'int *const (^)()', expected 'int *(^)()'}} + + int (^IPCC3) (const int) = PFR; // expected-error {{incompatible block pointer types initializing 'int (^)(int)', expected 'int (^)(int const)'}} + + + int (^IPCC4) (int, char (^CArg) (double)); + + + int (^IPCC5) (int, char (^CArg) (double)) = IPCC4; + + int (^IPCC6) (int, char (^CArg) (float)) = IPCC4; // expected-error {{incompatible block pointer types initializing 'int (^)(int, char (^)(double))', expected 'int (^)(int, char (^)(float))'}} + + IPCC2 = 0; + IPCC2 = 1; // expected-error {{invalid conversion assigning integer 'int', expected block pointer 'int *(^)()'}} + int (^x)() = 0; + int (^y)() = 3; // expected-error {{invalid conversion initializing integer 'int', expected block pointer 'int (^)()'}} + int a = 1; + int (^z)() = a+4; // expected-error {{invalid conversion initializing integer 'int', expected block pointer 'int (^)()'}} +} + +int blah() { + int (^IFP) (float); + char (^PCP)(double, double, char); + + IFP(1.0); + IFP (1.0, 2.0); // expected-error {{too many arguments to block call}} + + char ch = PCP(1.0, 2.0, 'a'); + return PCP(1.0, 2.0); // expected-error {{too few arguments to block}} +} + diff --git a/test/Sema/block-misc.c b/test/Sema/block-misc.c new file mode 100644 index 0000000000..92632f3aaa --- /dev/null +++ b/test/Sema/block-misc.c @@ -0,0 +1,50 @@ +// RUN: clang -fsyntax-only -verify %s +void donotwarn(); + +int (^IFP) (); +int (^II) (int); +int test1() { + int (^PFR) (int) = 0; // OK + PFR = II; // OK + + if (PFR == II) // OK + donotwarn(); + + if (PFR == IFP) // expected-error {{comparison of distinct block types}} + donotwarn(); + + if (PFR == (int (^) (int))IFP) // OK + donotwarn(); + + if (PFR == 0) // OK + donotwarn(); + + if (PFR) // OK + donotwarn(); + + if (!PFR) // OK + donotwarn(); + + return PFR != IFP; // expected-error {{comparison of distinct block types}} +} + +int test2(double (^S)()) { + double (^I)(int) = (void*) S; + (void*)I = (void *)S; // expected-error {{expression is not assignable}} + + void *pv = I; + + pv = S; + + I(1); + + return (void*)I == (void *)S; +} + +int^ x; // expected-error {{block pointer to non-function type is invalid}} +int^^ x1; // expected-error {{block pointer to non-function type is invalid}} + +int test3() { + char *^ y; // expected-error {{block pointer to non-function type is invalid}} +} +