From: Mike Stump Date: Wed, 4 Feb 2009 22:31:32 +0000 (+0000) Subject: Add support for blocks with explicit return types. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=98eb8a7a702b95183ed015706b1f1c66f5cb27a4;p=clang Add support for blocks with explicit return types. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@63784 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index 2a860a849d..28e63b353a 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -772,7 +772,7 @@ public: /// ActOnBlockArguments - This callback allows processing of block arguments. /// If there are no arguments, this is still invoked. - virtual void ActOnBlockArguments(Declarator &ParamInfo) {} + virtual void ActOnBlockArguments(Declarator &ParamInfo, Scope *CurScope) {} /// ActOnBlockError - If there is an error parsing a block, this callback /// is invoked to pop the information about the block from the action impl. diff --git a/include/clang/Parse/DeclSpec.h b/include/clang/Parse/DeclSpec.h index d4d363b010..7fb427fe72 100644 --- a/include/clang/Parse/DeclSpec.h +++ b/include/clang/Parse/DeclSpec.h @@ -695,7 +695,8 @@ public: ForContext, // Declaration within first part of a for loop. ConditionContext, // Condition declaration in a C++ if/switch/while/for. TemplateParamContext,// Within a template parameter list. - CXXCatchContext // C++ catch exception-declaration + CXXCatchContext, // C++ catch exception-declaration + BlockLiteralContext // Block literal declarator. }; /// DeclaratorKind - The kind of declarator this represents. @@ -813,14 +814,15 @@ public: /// parameter lists. bool mayOmitIdentifier() const { return Context == TypeNameContext || Context == PrototypeContext || - Context == TemplateParamContext || Context == CXXCatchContext; + Context == TemplateParamContext || Context == CXXCatchContext || + Context == BlockLiteralContext; } /// mayHaveIdentifier - Return true if the identifier is either optional or /// required. This is true for normal declarators and prototypes, but not /// typenames. bool mayHaveIdentifier() const { - return Context != TypeNameContext; + return Context != TypeNameContext && Context != BlockLiteralContext; } /// mayBeFollowedByCXXDirectInit - Return true if the declarator can be diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index c34ac3478c..eec08cc0a2 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -898,6 +898,7 @@ private: TypeTy *ParseTypeName(); + void ParseBlockId(); AttributeList *ParseAttributes(); void FuzzyParseMicrosoftDeclSpec(); void ParseTypeofSpecifier(DeclSpec &DS); diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 64c4c31fc4..d20f294bc6 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -1211,11 +1211,29 @@ bool Parser::ParseExpressionList(ExprListTy &Exprs, CommaLocsTy &CommaLocs) { } } +/// ParseBlockId - Parse a block-id, which roughly looks like int (int x). +/// +/// [clang] block-id: +/// [clang] specifier-qualifier-list block-declarator +/// +void Parser::ParseBlockId() { + // Parse the specifier-qualifier-list piece. + DeclSpec DS; + ParseSpecifierQualifierList(DS); + + // Parse the block-declarator. + Declarator DeclaratorInfo(DS, Declarator::BlockLiteralContext); + ParseDeclarator(DeclaratorInfo); + // Inform sema that we are starting a block. + Actions.ActOnBlockArguments(DeclaratorInfo, CurScope); +} + /// ParseBlockLiteralExpression - Parse a block literal, which roughly looks /// like ^(int x){ return x+1; } /// /// block-literal: /// [clang] '^' block-args[opt] compound-statement +/// [clang] '^' block-id compound-statement /// [clang] block-args: /// [clang] '(' parameter-list ')' /// @@ -1235,7 +1253,7 @@ Parser::OwningExprResult Parser::ParseBlockLiteralExpression() { // Parse the return type if present. DeclSpec DS; - Declarator ParamInfo(DS, Declarator::PrototypeContext); + Declarator ParamInfo(DS, Declarator::BlockLiteralContext); // If this block has arguments, parse them. There is no ambiguity here with // the expression case, because the expression case requires a parameter list. @@ -1244,20 +1262,24 @@ Parser::OwningExprResult Parser::ParseBlockLiteralExpression() { // Parse the pieces after the identifier as if we had "int(...)". ParamInfo.SetIdentifier(0, CaretLoc); if (ParamInfo.getInvalidType()) { - // If there was an error parsing the arguments, they may have tried to use - // ^(x+y) which requires an argument list. Just skip the whole block - // literal. + // If there was an error parsing the arguments, they may have + // tried to use ^(x+y) which requires an argument list. Just + // skip the whole block literal. return ExprError(); } + // Inform sema that we are starting a block. + Actions.ActOnBlockArguments(ParamInfo, CurScope); + } else if (! Tok.is(tok::l_brace)) { + ParseBlockId(); } else { // Otherwise, pretend we saw (void). ParamInfo.AddTypeInfo(DeclaratorChunk::getFunction(true, false, 0, 0, 0, CaretLoc, ParamInfo)); + // Inform sema that we are starting a block. + Actions.ActOnBlockArguments(ParamInfo, CurScope); } - // Inform sema that we are starting a block. - Actions.ActOnBlockArguments(ParamInfo); OwningExprResult Result(Actions, true); if (Tok.is(tok::l_brace)) { diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 4879ac8458..975c5e1c59 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -1209,7 +1209,7 @@ public: /// ActOnBlockArguments - This callback allows processing of block arguments. /// If there are no arguments, this is still invoked. - virtual void ActOnBlockArguments(Declarator &ParamInfo); + virtual void ActOnBlockArguments(Declarator &ParamInfo, Scope *CurScope); /// ActOnBlockError - If there is an error parsing a block, this callback /// is invoked to pop the information about the block from the action impl. diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 66710370af..3ee6e52e2e 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -4179,12 +4179,36 @@ void Sema::ActOnBlockStart(SourceLocation CaretLoc, Scope *BlockScope) { PushDeclContext(BlockScope, BSI->TheDecl); } -void Sema::ActOnBlockArguments(Declarator &ParamInfo) { +void Sema::ActOnBlockArguments(Declarator &ParamInfo, Scope *CurScope) { + assert(ParamInfo.getIdentifier() == 0 && "block-id should have no identifier!"); + + if (ParamInfo.getNumTypeObjects() == 0 + || ParamInfo.getTypeObject(0).Kind != DeclaratorChunk::Function) { + QualType T = GetTypeForDeclarator(ParamInfo, CurScope); + + // The type is entirely optional as well, if none, use DependentTy. + if (T.isNull()) + T = Context.DependentTy; + + // The parameter list is optional, if there was none, assume (). + if (!T->isFunctionType()) + T = Context.getFunctionType(T, NULL, 0, 0, 0); + + CurBlock->hasPrototype = true; + CurBlock->isVariadic = false; + Type *RetTy = T.getTypePtr()->getAsFunctionType()->getResultType() + .getTypePtr(); + + if (!RetTy->isDependentType()) + CurBlock->ReturnType = RetTy; + return; + } + // Analyze arguments to block. assert(ParamInfo.getTypeObject(0).Kind == DeclaratorChunk::Function && "Not a function declarator!"); DeclaratorChunk::FunctionTypeInfo &FTI = ParamInfo.getTypeObject(0).Fun; - + CurBlock->hasPrototype = FTI.hasPrototype; CurBlock->isVariadic = true; @@ -4200,6 +4224,13 @@ void Sema::ActOnBlockArguments(Declarator &ParamInfo) { for (unsigned i = 0, e = FTI.NumArgs; i != e; ++i) CurBlock->Params.push_back((ParmVarDecl *)FTI.ArgInfo[i].Param); CurBlock->isVariadic = FTI.isVariadic; + QualType T = GetTypeForDeclarator (ParamInfo, CurScope); + + Type* RetTy = T.getTypePtr()->getAsFunctionType()->getResultType() + .getTypePtr(); + + if (!RetTy->isDependentType()) + CurBlock->ReturnType = RetTy; } CurBlock->TheDecl->setArgs(&CurBlock->Params[0], CurBlock->Params.size()); diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index cf26b90bc4..7697dbda00 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -731,8 +731,8 @@ Sema::ActOnBlockReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { CurBlock->ReturnType = RetValExp->getType().getTypePtr(); } else CurBlock->ReturnType = Context.VoidTy.getTypePtr(); - return Owned(new ReturnStmt(ReturnLoc, RetValExp)); } + QualType FnRetType = QualType(CurBlock->ReturnType, 0); // 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 @@ -749,21 +749,22 @@ Sema::ActOnBlockReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { if (!RetValExp) return StmtError(Diag(ReturnLoc, diag::err_block_return_missing_expr)); - // 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()) { - // FIXME: non-localizable string in diagnostic - DiagnoseAssignmentResult(Incompatible, ReturnLoc, BlockQT, - RetValType, RetValExp, "returning"); - return StmtError(); - } + if (!FnRetType->isDependentType() && !RetValExp->isTypeDependent()) { + // we have a non-void block with an expression, continue checking + QualType RetValType = RetValExp->getType(); + + // C99 6.8.6.4p3(136): The return statement is not an assignment. The + // overlap restriction of subclause 6.5.16.1 does not apply to the case of + // function return. - if (RetValExp) CheckReturnStackAddr(RetValExp, BlockQT, ReturnLoc); + // In C++ the return statement is handled via a copy initialization. + // the C version of which boils down to CheckSingleAssignmentConstraints. + // FIXME: Leaks RetValExp. + if (PerformCopyInitialization(RetValExp, FnRetType, "returning")) + return StmtError(); + + if (RetValExp) CheckReturnStackAddr(RetValExp, FnRetType, ReturnLoc); + } return Owned(new ReturnStmt(ReturnLoc, RetValExp)); } diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 9d78d0adbf..e6d18338a4 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -251,9 +251,20 @@ QualType Sema::ConvertDeclSpecToType(const DeclSpec &DS) { return Result; } -/// GetTypeForDeclarator - Convert the type for the specified declarator to Type -/// instances. Skip the outermost Skip type objects. +/// GetTypeForDeclarator - Convert the type for the specified +/// declarator to Type instances. Skip the outermost Skip type +/// objects. QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S, unsigned Skip) { + bool OmittedReturnType = false; + + if (D.getContext() == Declarator::BlockLiteralContext + && Skip == 0 + && !D.getDeclSpec().hasTypeSpecifier() + && (D.getNumTypeObjects() == 0 + || (D.getNumTypeObjects() == 1 + && D.getTypeObject(0).Kind == DeclaratorChunk::Function))) + OmittedReturnType = true; + // long long is a C99 feature. if (!getLangOptions().C99 && !getLangOptions().CPlusPlus0x && D.getDeclSpec().getTypeSpecWidth() == DeclSpec::TSW_longlong) @@ -265,9 +276,16 @@ QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S, unsigned Skip) { switch (D.getKind()) { case Declarator::DK_Abstract: case Declarator::DK_Normal: - case Declarator::DK_Operator: - T = ConvertDeclSpecToType(D.getDeclSpec()); + case Declarator::DK_Operator: { + const DeclSpec& DS = D.getDeclSpec(); + if (OmittedReturnType) + // We default to a dependent type initially. Can be modified by + // the first return statement. + T = Context.DependentTy; + else + T = ConvertDeclSpecToType(DS); break; + } case Declarator::DK_Constructor: case Declarator::DK_Destructor: @@ -279,8 +297,9 @@ QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S, unsigned Skip) { break; } - // Walk the DeclTypeInfo, building the recursive type as we go. DeclTypeInfos - // are ordered from the identifier out, which is opposite of what we want :). + // Walk the DeclTypeInfo, building the recursive type as we go. + // DeclTypeInfos are ordered from the identifier out, which is + // opposite of what we want :). for (unsigned i = Skip, e = D.getNumTypeObjects(); i != e; ++i) { DeclaratorChunk &DeclType = D.getTypeObject(e-i-1+Skip); switch (DeclType.Kind) { diff --git a/test/Sema/block-explicit-return-type.c b/test/Sema/block-explicit-return-type.c new file mode 100644 index 0000000000..a0d5da6ee8 --- /dev/null +++ b/test/Sema/block-explicit-return-type.c @@ -0,0 +1,81 @@ +// RUN: clang -ObjC -fsyntax-only %s -verify -fblocks +// FIXME: should compile +// Test for blocks with explicit return type specified. + +typedef float * PF; +float gf; + +@interface NSView + - (id) some_method_that_returns_id; +@end + +NSView *some_object; + +void some_func (NSView * (^) (id)); + +typedef struct dispatch_item_s *dispatch_item_t; +typedef void (^completion_block_t)(void); + +typedef double (^myblock)(int); +double test(myblock I); + +int main() +{ + __block int x = 1; + __block int y = 2; + + (void)^void *{ return 0; }; + + (void)^float(float y){ return y; }; + + (void)^double (float y, double d) + { + if (y) + return d; + else + return y; + }; + + const char * (^chb) (int flag, const char *arg, char *arg1) = ^ const char * (int flag, const char *arg, char *arg1) { + if (flag) + return 0; + if (flag == 1) + return arg; + else if (flag == 2) + return ""; + return arg1; + }; + + (void)^PF { return &gf; }; + + some_func(^ NSView * (id whatever) { return [some_object some_method_that_returns_id]; }); + + double res = test(^(int z){x = y+z; return (double)z; }); +} + +void func() +{ + completion_block_t X; + + completion_block_t (^blockx)(dispatch_item_t) = ^completion_block_t (dispatch_item_t item) { + return X; + }; + + completion_block_t (^blocky)(dispatch_item_t) = ^(dispatch_item_t item) { + return X; + }; + + blockx = blocky; + +} + + +// intent: block taking int returning block that takes char,int and returns int +int (^(^block)(double x))(char, short); + +void foo() { + int one = 1; + block = ^(double x){ return ^(char c, short y) { return one + c + y; };}; // expected-error {{returning block that lives on the local stack}} + // or: + block = ^(double x){ return ^(char c, short y) { return one + (int)c + y; };}; // expected-error {{returning block that lives on the local stack}} +} diff --git a/test/Sema/block-return.c b/test/Sema/block-return.c index e835c96317..64c29935f5 100644 --- a/test/Sema/block-return.c +++ b/test/Sema/block-return.c @@ -3,24 +3,23 @@ typedef void (^CL)(void); CL foo() { - - short y; - + short y; short (^add1)(void) = ^{ return y+1; }; // expected-warning {{incompatible block pointer types initializing 'int (^)(void)', expected 'short (^)(void)'}} - CL X = ^{ - if (2) - return; + CL X = ^{ + if (2) + return; return 1; // expected-error {{void block should not return a value}} }; - int (^Y) (void) = ^{ + + int (^Y) (void) = ^{ if (3) return 1; else return; // expected-error {{non-void block should return a value}} }; - char *(^Z)(void) = ^{ + char *(^Z)(void) = ^{ if (3) return ""; else @@ -28,20 +27,20 @@ CL foo() { }; double (^A)(void) = ^ { // expected-warning {{incompatible block pointer types initializing 'float (^)(void)', expected 'double (^)(void)'}} - if (1) - return (float)1.0; + if (1) + return (float)1.0; else if (2) - return (double)2.0; // expected-error {{incompatible type returning 'double', expected 'float'}} - return 1; // expected-error {{incompatible type returning 'int', expected 'float'}} + return (double)2.0; + return 1; }; - - char *(^B)(void) = ^{ + char *(^B)(void) = ^{ if (3) return ""; else - return 2; // expected-error {{incompatible type returning 'int', expected 'char *'}} + return 2; // expected-warning {{incompatible integer to pointer conversion returning 'int', expected 'char *'}} }; + return ^{ return 1; }; // expected-warning {{incompatible block pointer types returning 'int (^)(void)', expected 'CL'}} expected-error {{returning block that lives on the local stack}} } diff --git a/test/Sema/block-syntax-error.c b/test/Sema/block-syntax-error.c index c98f1fd2aa..5f6c204a47 100644 --- a/test/Sema/block-syntax-error.c +++ b/test/Sema/block-syntax-error.c @@ -3,7 +3,5 @@ void (^noop)(void); void somefunction() { - noop = ^int *{}; // expected-error {{expected expression}} - - noop = ^noop; // expected-error {{expected expression}} + noop = ^noop; // expected-error {{type name requires a specifier or qualifier}} expected-error {{expected expression}} }