diff options
author | Jordan Rose <jordan_rose@apple.com> | 2012-06-21 05:54:55 +0000 |
---|---|---|
committer | Jordan Rose <jordan_rose@apple.com> | 2012-06-21 05:54:55 +0000 |
commit | 81a5641e5f446d0e879595d3659e76845c7a9410 (patch) | |
tree | 0d399123fc447800a52e33106f9e0b001b1463a4 | |
parent | 05233276b5b0d76e9ebd75eaa2709dff9b316590 (diff) |
Pretend that enum constants have enum type when inferring a block return type.
In C, enum constants have the type of the enum's underlying integer type,
rather than the type of the enum. (This is not true in C++.) This leads to
odd warnings when returning enum constants directly in blocks with inferred
return types. The easiest way out of this is to pretend that, like C++, enum
constants have enum type when being returned from a block.
<rdar://problem/11662489>
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@158899 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | lib/Sema/SemaStmt.cpp | 28 | ||||
-rw-r--r-- | test/SemaObjC/blocks.m | 127 |
2 files changed, 152 insertions, 3 deletions
diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index 8bf0b7326d..999be9109d 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -2128,10 +2128,32 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { return StmtError(); RetValExp = Result.take(); - if (!RetValExp->isTypeDependent()) + if (!RetValExp->isTypeDependent()) { ReturnT = RetValExp->getType(); - else + + // In C, enum constants have the type of their underlying integer type, + // not the enum. When inferring block return values, we should infer + // the enum type if an enum constant is used, unless the enum is + // anonymous (in which case there can be no variables of its type). + if (!getLangOpts().CPlusPlus) { + Expr *InsideExpr = RetValExp->IgnoreParenImpCasts(); + if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(InsideExpr)) { + Decl *D = DRE->getDecl(); + if (EnumConstantDecl *ECD = dyn_cast<EnumConstantDecl>(D)) { + EnumDecl *Enum = cast<EnumDecl>(ECD->getDeclContext()); + if (Enum->getDeclName() || Enum->getTypedefNameForAnonDecl()) { + ReturnT = Context.getTypeDeclType(Enum); + ExprResult Casted = ImpCastExprToType(RetValExp, ReturnT, + CK_IntegralCast); + assert(Casted.isUsable()); + RetValExp = Casted.take(); + } + } + } + } + } else { ReturnT = Context.DependentTy; + } } else { if (RetValExp) { // C++11 [expr.lambda.prim]p4 bans inferring the result from an @@ -2147,7 +2169,7 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { if (!CurCap->ReturnType.isNull() && !CurCap->ReturnType->isDependentType() && !ReturnT->isDependentType() && - !Context.hasSameType(ReturnT, CurCap->ReturnType)) { + !Context.hasSameType(ReturnT, CurCap->ReturnType)) { Diag(ReturnLoc, diag::err_typecheck_missing_return_type_incompatible) << ReturnT << CurCap->ReturnType << (getCurLambda() != 0); diff --git a/test/SemaObjC/blocks.m b/test/SemaObjC/blocks.m index 7beec19ea7..a6a6a3b368 100644 --- a/test/SemaObjC/blocks.m +++ b/test/SemaObjC/blocks.m @@ -73,3 +73,130 @@ void foo10() { NSLog(@"%@", myBlock); } + +// In C, enum constants have the type of the underlying integer type, not the +// enumeration they are part of. We pretend the constants have enum type when +// inferring block return types, so that they can be mixed-and-matched with +// other expressions of enum type. +enum CStyleEnum { + CSE_Value = 1 +}; +enum CStyleEnum getCSE(); +typedef enum CStyleEnum (^cse_block_t)(); + +void testCStyleEnumInference(bool arg) { + cse_block_t a; + + // No warnings here. + a = ^{ return CSE_Value; }; + a = ^{ return getCSE(); }; + + a = ^{ // expected-error {{incompatible block pointer types assigning to 'cse_block_t' (aka 'enum CStyleEnum (^)()') from 'int (^)(void)'}} + return 1; + }; + + // No warnings here. + a = ^{ if (arg) return CSE_Value; else return CSE_Value; }; + a = ^{ if (arg) return getCSE(); else return getCSE(); }; + a = ^{ if (arg) return CSE_Value; else return getCSE(); }; + a = ^{ if (arg) return getCSE(); else return CSE_Value; }; + + // Technically these two blocks should return 'int'. + // The first case is easy to handle -- just don't cast the enum constant + // to the enum type. However, the second guess would require going back + // and REMOVING the cast from the first return statement, which isn't really + // feasible (there may be more than one previous return statement with enum + // type). For symmetry, we just treat them the same way. + a = ^{ // expected-error {{incompatible block pointer types assigning to 'cse_block_t' (aka 'enum CStyleEnum (^)()') from 'int (^)(void)'}} + if (arg) + return 1; + else + return CSE_Value; // expected-error {{return type 'enum CStyleEnum' must match previous return type 'int'}} + }; + + a = ^{ + if (arg) + return CSE_Value; + else + return 1; // expected-error {{return type 'int' must match previous return type 'enum CStyleEnum'}} + }; +} + + +enum FixedTypeEnum : unsigned { + FTE_Value = 1U +}; +enum FixedTypeEnum getFTE(); +typedef enum FixedTypeEnum (^fte_block_t)(); + +void testFixedTypeEnumInference(bool arg) { + fte_block_t a; + + // No warnings here. + a = ^{ return FTE_Value; }; + a = ^{ return getFTE(); }; + + // Since we fixed the underlying type of the enum, this is considered a + // compatible block type. + a = ^{ + return 1U; + }; + + // No warnings here. + a = ^{ if (arg) return FTE_Value; else return FTE_Value; }; + a = ^{ if (arg) return getFTE(); else return getFTE(); }; + a = ^{ if (arg) return FTE_Value; else return getFTE(); }; + a = ^{ if (arg) return getFTE(); else return FTE_Value; }; + + // Technically these two blocks should return 'unsigned'. + // The first case is easy to handle -- just don't cast the enum constant + // to the enum type. However, the second guess would require going back + // and REMOVING the cast from the first return statement, which isn't really + // feasible (there may be more than one previous return statement with enum + // type). For symmetry, we just treat them the same way. + a = ^{ + if (arg) + return 1U; + else + return FTE_Value; // expected-error{{return type 'enum FixedTypeEnum' must match previous return type 'unsigned int'}} + }; + + a = ^{ + if (arg) + return FTE_Value; + else + return 1U; // expected-error{{return type 'unsigned int' must match previous return type 'enum FixedTypeEnum'}} + }; +} + + +enum { + AnonymousValue = 1 +}; + +enum : short { + FixedAnonymousValue = 1 +}; + +typedef enum { + TDE_Value +} TypeDefEnum; + +typedef enum : short { + TDFTE_Value +} TypeDefFixedTypeEnum; + + +typedef int (^int_block_t)(); +typedef short (^short_block_t)(); +void testAnonymousEnumTypes() { + int_block_t IB; + IB = ^{ return AnonymousValue; }; + IB = ^{ return TDE_Value; }; // expected-error {{incompatible block pointer types assigning to 'int_block_t' (aka 'int (^)()') from 'TypeDefEnum (^)(void)'}} + IB = ^{ return CSE_Value; }; // expected-error {{incompatible block pointer types assigning to 'int_block_t' (aka 'int (^)()') from 'enum CStyleEnum (^)(void)'}} + + short_block_t SB; + SB = ^{ return FixedAnonymousValue; }; + // This is not an error anyway since the enum has a fixed underlying type. + SB = ^{ return TDFTE_Value; }; +} |