[cfe-commits] r158899 - in /cfe/trunk: lib/Sema/SemaStmt.cpp test/SemaObjC/blocks.m

Jordan Rose jordan_rose at apple.com
Wed Jun 20 22:54:55 PDT 2012


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>

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





More information about the cfe-commits mailing list