From: Jordan Rose Date: Thu, 21 Jun 2012 05:54:55 +0000 (+0000) Subject: Pretend that enum constants have enum type when inferring a block return type. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=81a5641e5f446d0e879595d3659e76845c7a9410;p=clang 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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@158899 91177308-0d34-0410-b5e6-96231b3b80d8 --- 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(InsideExpr)) { + Decl *D = DRE->getDecl(); + if (EnumConstantDecl *ECD = dyn_cast(D)) { + EnumDecl *Enum = cast(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; }; +}