On Jun 20, 2012, at 10:54 PM, Jordan Rose <[email protected]> wrote:
> Author: jrose > Date: Thu Jun 21 00:54:55 2012 > New Revision: 158899 > > URL: http://llvm.org/viewvc/llvm-project?rev=158899&view=rev > Log: > 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> While possibly convenient, this is a source-compatibility-breaking change to something that has been stable for *years*. I'd support doing this for enumerators of enumerations with a fixed underlying type, because that's a new extension in Objective-C. - Doug > Modified: > cfe/trunk/lib/Sema/SemaStmt.cpp > cfe/trunk/test/SemaObjC/blocks.m > > Modified: cfe/trunk/lib/Sema/SemaStmt.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaStmt.cpp?rev=158899&r1=158898&r2=158899&view=diff > ============================================================================== > --- cfe/trunk/lib/Sema/SemaStmt.cpp (original) > +++ cfe/trunk/lib/Sema/SemaStmt.cpp Thu Jun 21 00:54:55 2012 > @@ -2128,10 +2128,32 @@ > 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 @@ > 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); > > Modified: cfe/trunk/test/SemaObjC/blocks.m > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjC/blocks.m?rev=158899&r1=158898&r2=158899&view=diff > ============================================================================== > --- cfe/trunk/test/SemaObjC/blocks.m (original) > +++ cfe/trunk/test/SemaObjC/blocks.m Thu Jun 21 00:54:55 2012 > @@ -73,3 +73,130 @@ > 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; }; > +} > > > _______________________________________________ > cfe-commits mailing list > [email protected] > http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits _______________________________________________ cfe-commits mailing list [email protected] http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
