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; };
+}