re PR c++/44311 ([C++0x] no error with switch over enum class and integer case)
authorJason Merrill <jason@redhat.com>
Wed, 25 May 2011 20:02:41 +0000 (16:02 -0400)
committerJason Merrill <jason@gcc.gnu.org>
Wed, 25 May 2011 20:02:41 +0000 (16:02 -0400)
PR c++/44311
* decl.c (case_conversion): New.
(finish_case_label): Use it.

From-SVN: r174231

gcc/cp/ChangeLog
gcc/cp/decl.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/cpp0x/constexpr-switch2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/enum15.C [new file with mode: 0644]

index c0677cc..1b61193 100644 (file)
@@ -1,5 +1,9 @@
 2011-05-25  Jason Merrill  <jason@redhat.com>
 
+       PR c++/44311
+       * decl.c (case_conversion): New.
+       (finish_case_label): Use it.
+
        * ptree.c (cxx_print_xnode): Handle ARGUMENT_PACK_SELECT.
 
        PR c++/45698
index 2b6a777..7fc1945 100644 (file)
@@ -2957,6 +2957,28 @@ pop_switch (void)
   free (cs);
 }
 
+/* Convert a case constant VALUE in a switch to the type TYPE of the switch
+   condition.  Note that if TYPE and VALUE are already integral we don't
+   really do the conversion because the language-independent
+   warning/optimization code will work better that way.  */
+
+static tree
+case_conversion (tree type, tree value)
+{
+  if (value == NULL_TREE)
+    return value;
+
+  if (cxx_dialect >= cxx0x
+      && (SCOPED_ENUM_P (type)
+         || !INTEGRAL_OR_UNSCOPED_ENUMERATION_TYPE_P (TREE_TYPE (value))))
+    {
+      if (INTEGRAL_OR_UNSCOPED_ENUMERATION_TYPE_P (type))
+       type = type_promotes_to (type);
+      value = perform_implicit_conversion (type, value, tf_warning_or_error);
+    }
+  return cxx_constant_value (value);
+}
+
 /* Note that we've seen a definition of a case label, and complain if this
    is a bad place for one.  */
 
@@ -2965,6 +2987,7 @@ finish_case_label (location_t loc, tree low_value, tree high_value)
 {
   tree cond, r;
   struct cp_binding_level *p;
+  tree type;
 
   if (processing_template_decl)
     {
@@ -2984,13 +3007,12 @@ finish_case_label (location_t loc, tree low_value, tree high_value)
   if (!check_switch_goto (switch_stack->level))
     return error_mark_node;
 
-  if (low_value)
-    low_value = cxx_constant_value (low_value);
-  if (high_value)
-    high_value = cxx_constant_value (high_value);
+  type = SWITCH_STMT_TYPE (switch_stack->switch_stmt);
+
+  low_value = case_conversion (type, low_value);
+  high_value = case_conversion (type, high_value);
 
-  r = c_add_case_label (loc, switch_stack->cases, cond,
-                       SWITCH_STMT_TYPE (switch_stack->switch_stmt),
+  r = c_add_case_label (loc, switch_stack->cases, cond, type,
                        low_value, high_value);
 
   /* After labels, make any new cleanups in the function go into their
index 2a30f6e..e8a335f 100644 (file)
@@ -1,5 +1,8 @@
 2011-05-25  Jason Merrill  <jason@redhat.com>
 
+       * g++.dg/cpp0x/enum15.C: New.
+       * g++.dg/cpp0x/constexpr-switch2.C: New.
+
        * g++.dg/cpp0x/variadic110.C: New.
 
        * g++.dg/cpp0x/auto9.C: Add typedef test.
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-switch2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-switch2.C
new file mode 100644 (file)
index 0000000..55cf2ad
--- /dev/null
@@ -0,0 +1,23 @@
+// Test for constexpr conversion in case context
+// { dg-options -std=c++0x }
+
+enum class E { e1, e2 };
+
+struct A
+{
+  E e;
+  constexpr operator E() { return e; }
+  constexpr A(E e): e(e) { }
+};
+
+E e;
+
+int main()
+{
+  switch (e)
+    {
+    case A(E::e1):
+    case A(E::e2):
+      ;
+    }
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/enum15.C b/gcc/testsuite/g++.dg/cpp0x/enum15.C
new file mode 100644 (file)
index 0000000..d653216
--- /dev/null
@@ -0,0 +1,20 @@
+// PR c++/44311
+// { dg-options -std=c++0x }
+
+enum class A { Val0, Val1 };
+
+void foo (A a, int i)
+{
+  switch (a)
+    {
+    case A::Val0: break;
+    case 1: break;             // { dg-error "" }
+    }
+
+  switch (i)
+    {
+    case A::Val0: break;       // { dg-error "" }
+    case 1: break;
+    case 2.0: break;
+    }
+}