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
diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp
index 8bf0b73..999be91 100644
--- a/lib/Sema/SemaStmt.cpp
+++ b/lib/Sema/SemaStmt.cpp
@@ -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);
diff --git a/test/SemaObjC/blocks.m b/test/SemaObjC/blocks.m
index 7beec19..a6a6a3b 100644
--- a/test/SemaObjC/blocks.m
+++ b/test/SemaObjC/blocks.m
@@ -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; };
+}