aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorMarek Polacek <polacek@redhat.com>2020-12-02 10:47:49 -0500
committerMarek Polacek <polacek@redhat.com>2020-12-07 12:38:01 -0500
commit7482d5a3acb7a8a5564f5cddc4f9d2ebbaea75e4 (patch)
tree609461ed358a3da102a18a84eebcd01d2fbd1a94 /gcc
parent16a2a458809103d1c382c09c62b09697aac9b935 (diff)
downloadgcc-7482d5a3acb7a8a5564f5cddc4f9d2ebbaea75e4.zip
gcc-7482d5a3acb7a8a5564f5cddc4f9d2ebbaea75e4.tar.gz
gcc-7482d5a3acb7a8a5564f5cddc4f9d2ebbaea75e4.tar.bz2
c++: ICE with switch and scoped enum bit-fields [PR98043]
In this testcase we are crashing trying to gimplify a switch, because the types of the switch condition and case constants have different TYPE_PRECISIONs. This started with my r5-3726 fix: SWITCH_STMT_TYPE is supposed to be the original type of the switch condition before any conversions, so in the C++ FE we need to use unlowered_expr_type to get the unlowered type of enum bit-fields. Normally, the switch type is subject to integral promotions, but here we have a scoped enum type and those don't promote: enum class B { A }; struct C { B c : 8; }; switch (x.c) // type B case B::A: // type int, will be converted to B Here TREE_TYPE is "signed char" but SWITCH_STMT_TYPE is "B". When gimplifying this in gimplify_switch_expr, the index type is "B" and we convert all the case values to "B" in preprocess_case_label_vec, but SWITCH_COND is of type "signed char": gimple_switch_index should be the (possibly promoted) type, not the original type, so we gimplify the "x.c" SWITCH_COND to a SSA_NAME of type "signed char". And then we crash because the precision of the index type doesn't match the precision of the case value type. I think it makes sense to do the following; at the end of pop_switch we've already issued the switch warnings, and since scoped enums don't promote, it should be okay to use the type of SWITCH_STMT_COND. The r5-3726 change was about giving warnings for enum bit-fields anyway. gcc/cp/ChangeLog: PR c++/98043 * decl.c (pop_switch): If SWITCH_STMT_TYPE is a scoped enum type, set it to the type of SWITCH_STMT_COND. gcc/testsuite/ChangeLog: PR c++/98043 * g++.dg/cpp0x/enum41.C: New test.
Diffstat (limited to 'gcc')
-rw-r--r--gcc/cp/decl.c17
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/enum41.C32
2 files changed, 45 insertions, 4 deletions
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index a28e792..7da8c65 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -3690,17 +3690,17 @@ void
pop_switch (void)
{
struct cp_switch *cs = switch_stack;
- location_t switch_location;
/* Emit warnings as needed. */
- switch_location = cp_expr_loc_or_input_loc (cs->switch_stmt);
+ location_t switch_location = cp_expr_loc_or_input_loc (cs->switch_stmt);
+ tree cond = SWITCH_STMT_COND (cs->switch_stmt);
const bool bool_cond_p
= (SWITCH_STMT_TYPE (cs->switch_stmt)
&& TREE_CODE (SWITCH_STMT_TYPE (cs->switch_stmt)) == BOOLEAN_TYPE);
if (!processing_template_decl)
c_do_switch_warnings (cs->cases, switch_location,
- SWITCH_STMT_TYPE (cs->switch_stmt),
- SWITCH_STMT_COND (cs->switch_stmt), bool_cond_p);
+ SWITCH_STMT_TYPE (cs->switch_stmt), cond,
+ bool_cond_p);
/* For the benefit of block_may_fallthru remember if the switch body
case labels cover all possible values and if there are break; stmts. */
@@ -3711,6 +3711,15 @@ pop_switch (void)
SWITCH_STMT_ALL_CASES_P (cs->switch_stmt) = 1;
if (!cs->break_stmt_seen_p)
SWITCH_STMT_NO_BREAK_P (cs->switch_stmt) = 1;
+ /* Now that we're done with the switch warnings, set the switch type
+ to the type of the condition if the index type was of scoped enum type.
+ (Such types don't participate in the integer promotions.) We do this
+ because of bit-fields whose declared type is a scoped enum type:
+ gimplification will use the lowered index type, but convert the
+ case values to SWITCH_STMT_TYPE, which would have been the declared type
+ and verify_gimple_switch doesn't accept that. */
+ if (is_bitfield_expr_with_lowered_type (cond))
+ SWITCH_STMT_TYPE (cs->switch_stmt) = TREE_TYPE (cond);
gcc_assert (!cs->in_loop_body_p);
splay_tree_delete (cs->cases);
switch_stack = switch_stack->next;
diff --git a/gcc/testsuite/g++.dg/cpp0x/enum41.C b/gcc/testsuite/g++.dg/cpp0x/enum41.C
new file mode 100644
index 0000000..5f6ef13
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/enum41.C
@@ -0,0 +1,32 @@
+// PR c++/98043
+// { dg-do compile { target c++11 } }
+
+enum class B { A };
+struct C { B c : 8; };
+
+bool
+foo (C x)
+{
+ switch (x.c)
+ {
+ case B::A:
+ return false;
+ default:
+ return true;
+ }
+}
+
+enum E { X };
+struct D { E c : 7; };
+
+bool
+bar (D x)
+{
+ switch (x.c)
+ {
+ case E::X:
+ return false;
+ default:
+ return true;
+ }
+}