forked from OSchip/llvm-project
Add support for blocks with explicit return types.
llvm-svn: 63784
This commit is contained in:
parent
46353ece18
commit
82f071faa7
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -898,6 +898,7 @@ private:
|
|||
|
||||
|
||||
TypeTy *ParseTypeName();
|
||||
void ParseBlockId();
|
||||
AttributeList *ParseAttributes();
|
||||
void FuzzyParseMicrosoftDeclSpec();
|
||||
void ParseTypeofSpecifier(DeclSpec &DS);
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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,22 +749,23 @@ 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();
|
||||
if (!FnRetType->isDependentType() && !RetValExp->isTypeDependent()) {
|
||||
// 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();
|
||||
// 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.
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
if (RetValExp) CheckReturnStackAddr(RetValExp, BlockQT, ReturnLoc);
|
||||
|
||||
return Owned(new ReturnStmt(ReturnLoc, RetValExp));
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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}}
|
||||
}
|
|
@ -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}}
|
||||
}
|
||||
|
||||
|
|
|
@ -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}}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue