[gcc r14-8641] c: Fix ICEs casting expressions with integer constant operands to bool [PR111059, PR111911]

Joseph Myers jsm28@gcc.gnu.org
Wed Jan 31 01:25:13 GMT 2024


https://gcc.gnu.org/g:35de88e2ed0aa78f6e3306c8560cd6bb15ce0ffe

commit r14-8641-g35de88e2ed0aa78f6e3306c8560cd6bb15ce0ffe
Author: Joseph Myers <josmyers@redhat.com>
Date:   Wed Jan 31 01:24:21 2024 +0000

    c: Fix ICEs casting expressions with integer constant operands to bool [PR111059, PR111911]
    
    C front-end bugs 111059 and 111911 both report ICEs with conversions
    to boolean of expressions with integer constant operands that can
    appear in an integer constant expression as long as they are not
    evaluated (such as division by zero).
    
    The issue is a nested C_MAYBE_CONST_EXPR, with the inner one generated
    in build_binary_op to indicate that a subexpression has been fully
    folded and should not be folded again, and the outer one in
    build_c_cast to indicate that the expression has integer constant
    operands.  To avoid the inner one from build_binary_op,
    c_objc_common_truthvalue_conversion should be given an argument
    properly marked as having integer constant operands rather than that
    information having been removed by the caller - but because c_convert
    would then also wrap a C_MAYBE_CONST_EXPR with a NOP_EXPR converting
    to boolean, it seems most convenient to have
    c_objc_common_truthvalue_conversion produce the NE_EXPR directly in
    the desired type (boolean in this case), before generating any
    C_MAYBE_CONST_EXPR there, rather than it always producing a comparison
    in integer_type_node and doing a conversion to boolean in the caller.
    
    The same issue as in those PRs also applies for conversion to enums
    with a boolean fixed underlying type; that case is also fixed and
    tests added for it.  Note that not all the tests added failed before
    the patch (in particular, the issue was specific to casts and did not
    apply for implicit conversions, but some tests of those are added as
    well).
    
    Bootstrapped with no regressions for x86_64-pc-linux-gnu.
    
            PR c/111059
            PR c/111911
    
    gcc/c/
            * c-tree.h (c_objc_common_truthvalue_conversion): Add third
            argument.
            * c-convert.cc (c_convert): For conversions to boolean, pass third
            argument to c_objc_common_truthvalue_conversion rather than
            converting here.
            * c-typeck.cc (build_c_cast): Ensure arguments with integer
            operands are marked as such for conversion to boolean.
            (c_objc_common_truthvalue_conversion): Add third argument TYPE.
    
    gcc/testsuite/
            * gcc.c-torture/compile/pr111059-1.c,
            gcc.c-torture/compile/pr111059-2.c,
            gcc.c-torture/compile/pr111059-3.c,
            gcc.c-torture/compile/pr111059-4.c,
            gcc.c-torture/compile/pr111059-5.c,
            gcc.c-torture/compile/pr111059-6.c,
            gcc.c-torture/compile/pr111059-7.c,
            gcc.c-torture/compile/pr111059-8.c,
            gcc.c-torture/compile/pr111059-9.c,
            gcc.c-torture/compile/pr111059-10.c,
            gcc.c-torture/compile/pr111059-11.c,
            gcc.c-torture/compile/pr111059-12.c,
            gcc.c-torture/compile/pr111911-1.c,
            gcc.c-torture/compile/pr111911-2.c: New tests.

Diff:
---
 gcc/c/c-convert.cc                                |  3 +--
 gcc/c/c-tree.h                                    |  3 ++-
 gcc/c/c-typeck.cc                                 | 31 ++++++++++++++++++-----
 gcc/testsuite/gcc.c-torture/compile/pr111059-1.c  |  5 ++++
 gcc/testsuite/gcc.c-torture/compile/pr111059-10.c |  9 +++++++
 gcc/testsuite/gcc.c-torture/compile/pr111059-11.c |  9 +++++++
 gcc/testsuite/gcc.c-torture/compile/pr111059-12.c |  9 +++++++
 gcc/testsuite/gcc.c-torture/compile/pr111059-2.c  |  5 ++++
 gcc/testsuite/gcc.c-torture/compile/pr111059-3.c  |  5 ++++
 gcc/testsuite/gcc.c-torture/compile/pr111059-4.c  |  5 ++++
 gcc/testsuite/gcc.c-torture/compile/pr111059-5.c  |  5 ++++
 gcc/testsuite/gcc.c-torture/compile/pr111059-6.c  |  5 ++++
 gcc/testsuite/gcc.c-torture/compile/pr111059-7.c  |  9 +++++++
 gcc/testsuite/gcc.c-torture/compile/pr111059-8.c  |  9 +++++++
 gcc/testsuite/gcc.c-torture/compile/pr111059-9.c  |  9 +++++++
 gcc/testsuite/gcc.c-torture/compile/pr111911-1.c  |  7 +++++
 gcc/testsuite/gcc.c-torture/compile/pr111911-2.c  | 11 ++++++++
 17 files changed, 130 insertions(+), 9 deletions(-)

diff --git a/gcc/c/c-convert.cc b/gcc/c/c-convert.cc
index 3e52926f276a..7c1064c6691b 100644
--- a/gcc/c/c-convert.cc
+++ b/gcc/c/c-convert.cc
@@ -150,8 +150,7 @@ c_convert (tree type, tree expr, bool init_const)
 
     case BOOLEAN_TYPE:
     convert_to_boolean:
-      return fold_convert_loc
-	(loc, type, c_objc_common_truthvalue_conversion (input_location, expr));
+      return c_objc_common_truthvalue_conversion (input_location, expr, type);
 
     case POINTER_TYPE:
       /* The type nullptr_t may be converted to a pointer type.  The result is
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index cf29534c0915..1fba9c8dae76 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -755,7 +755,8 @@ bool c_type_variably_modified_p (tree t)
 
 
 extern bool char_type_p (tree);
-extern tree c_objc_common_truthvalue_conversion (location_t, tree);
+extern tree c_objc_common_truthvalue_conversion (location_t, tree,
+						 tree = integer_type_node);
 extern tree require_complete_type (location_t, tree);
 extern bool same_translation_unit_p (const_tree, const_tree);
 extern int comptypes (tree, tree);
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 66c6abc9f076..12d1a5a5fa19 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -6363,6 +6363,22 @@ build_c_cast (location_t loc, tree type, tree expr)
 		    " from %qT to %qT", otype, type);
 
       ovalue = value;
+      /* If converting to boolean a value with integer operands that
+	 is not itself represented as an INTEGER_CST, the call below
+	 to note_integer_operands may insert a C_MAYBE_CONST_EXPR, but
+	 build_binary_op as called by c_common_truthvalue_conversion
+	 may also insert a C_MAYBE_CONST_EXPR to indicate that a
+	 subexpression has been fully folded.  To avoid nested
+	 C_MAYBE_CONST_EXPR, ensure that
+	 c_objc_common_truthvalue_conversion receives an argument
+	 properly marked as having integer operands in that case.  */
+      if (int_operands
+	  && TREE_CODE (value) != INTEGER_CST
+	  && (TREE_CODE (type) == BOOLEAN_TYPE
+	      || (TREE_CODE (type) == ENUMERAL_TYPE
+		  && ENUM_UNDERLYING_TYPE (type) != NULL_TREE
+		  && TREE_CODE (ENUM_UNDERLYING_TYPE (type)) == BOOLEAN_TYPE)))
+	value = note_integer_operands (value);
       value = convert (type, value);
 
       /* Ignore any integer overflow caused by the cast.  */
@@ -13509,11 +13525,11 @@ build_binary_op (location_t location, enum tree_code code,
 }
 
 
-/* Convert EXPR to be a truth-value, validating its type for this
+/* Convert EXPR to be a truth-value (type TYPE), validating its type for this
    purpose.  LOCATION is the source location for the expression.  */
 
 tree
-c_objc_common_truthvalue_conversion (location_t location, tree expr)
+c_objc_common_truthvalue_conversion (location_t location, tree expr, tree type)
 {
   bool int_const, int_operands;
 
@@ -13556,14 +13572,17 @@ c_objc_common_truthvalue_conversion (location_t location, tree expr)
   if (int_operands && TREE_CODE (expr) != INTEGER_CST)
     {
       expr = remove_c_maybe_const_expr (expr);
-      expr = build2 (NE_EXPR, integer_type_node, expr,
+      expr = build2 (NE_EXPR, type, expr,
 		     convert (TREE_TYPE (expr), integer_zero_node));
       expr = note_integer_operands (expr);
     }
   else
-    /* ??? Should we also give an error for vectors rather than leaving
-       those to give errors later?  */
-    expr = c_common_truthvalue_conversion (location, expr);
+    {
+      /* ??? Should we also give an error for vectors rather than leaving
+	 those to give errors later?  */
+      expr = c_common_truthvalue_conversion (location, expr);
+      expr = fold_convert_loc (location, type, expr);
+    }
 
   if (TREE_CODE (expr) == INTEGER_CST && int_operands && !int_const)
     {
diff --git a/gcc/testsuite/gcc.c-torture/compile/pr111059-1.c b/gcc/testsuite/gcc.c-torture/compile/pr111059-1.c
new file mode 100644
index 000000000000..5788cb49ebaf
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/compile/pr111059-1.c
@@ -0,0 +1,5 @@
+void
+f ()
+{
+  (_Bool) (1 << -1);
+}
diff --git a/gcc/testsuite/gcc.c-torture/compile/pr111059-10.c b/gcc/testsuite/gcc.c-torture/compile/pr111059-10.c
new file mode 100644
index 000000000000..fdd9a29ecf5b
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/compile/pr111059-10.c
@@ -0,0 +1,9 @@
+/* { dg-options "-std=gnu23 -Wno-div-by-zero" } */
+
+enum e : bool { X };
+
+enum e
+f ()
+{
+  return 0 / 0;
+}
diff --git a/gcc/testsuite/gcc.c-torture/compile/pr111059-11.c b/gcc/testsuite/gcc.c-torture/compile/pr111059-11.c
new file mode 100644
index 000000000000..81260345a2c3
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/compile/pr111059-11.c
@@ -0,0 +1,9 @@
+/* { dg-options "-std=gnu23 -Wno-overflow" } */
+
+enum e : bool { X };
+
+void
+f ()
+{
+  (enum e) (__INT_MAX__ + 1);
+}
diff --git a/gcc/testsuite/gcc.c-torture/compile/pr111059-12.c b/gcc/testsuite/gcc.c-torture/compile/pr111059-12.c
new file mode 100644
index 000000000000..d11fcf654ce6
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/compile/pr111059-12.c
@@ -0,0 +1,9 @@
+/* { dg-options "-std=gnu23 -Wno-overflow" } */
+
+enum e : bool { X };
+
+enum e
+f ()
+{
+  return __INT_MAX__ + 1;
+}
diff --git a/gcc/testsuite/gcc.c-torture/compile/pr111059-2.c b/gcc/testsuite/gcc.c-torture/compile/pr111059-2.c
new file mode 100644
index 000000000000..785e291e1d8f
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/compile/pr111059-2.c
@@ -0,0 +1,5 @@
+void
+f ()
+{
+  (_Bool) (0 / 0);
+}
diff --git a/gcc/testsuite/gcc.c-torture/compile/pr111059-3.c b/gcc/testsuite/gcc.c-torture/compile/pr111059-3.c
new file mode 100644
index 000000000000..21099bc09fb6
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/compile/pr111059-3.c
@@ -0,0 +1,5 @@
+_Bool
+f ()
+{
+  return 1 << -1;
+}
diff --git a/gcc/testsuite/gcc.c-torture/compile/pr111059-4.c b/gcc/testsuite/gcc.c-torture/compile/pr111059-4.c
new file mode 100644
index 000000000000..95c68e55ce0d
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/compile/pr111059-4.c
@@ -0,0 +1,5 @@
+_Bool
+f ()
+{
+  return 0 / 0;
+}
diff --git a/gcc/testsuite/gcc.c-torture/compile/pr111059-5.c b/gcc/testsuite/gcc.c-torture/compile/pr111059-5.c
new file mode 100644
index 000000000000..10473550d26e
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/compile/pr111059-5.c
@@ -0,0 +1,5 @@
+void
+f ()
+{
+  (_Bool) (__INT_MAX__ + 1);
+}
diff --git a/gcc/testsuite/gcc.c-torture/compile/pr111059-6.c b/gcc/testsuite/gcc.c-torture/compile/pr111059-6.c
new file mode 100644
index 000000000000..213f79bfe721
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/compile/pr111059-6.c
@@ -0,0 +1,5 @@
+_Bool
+f ()
+{
+  return __INT_MAX__ + 1;
+}
diff --git a/gcc/testsuite/gcc.c-torture/compile/pr111059-7.c b/gcc/testsuite/gcc.c-torture/compile/pr111059-7.c
new file mode 100644
index 000000000000..466f45f12702
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/compile/pr111059-7.c
@@ -0,0 +1,9 @@
+/* { dg-options "-std=gnu23 -Wno-shift-count-negative" } */
+
+enum e : bool { X };
+
+void
+f ()
+{
+  (enum e) (1 << -1);
+}
diff --git a/gcc/testsuite/gcc.c-torture/compile/pr111059-8.c b/gcc/testsuite/gcc.c-torture/compile/pr111059-8.c
new file mode 100644
index 000000000000..67f339534e32
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/compile/pr111059-8.c
@@ -0,0 +1,9 @@
+/* { dg-options "-std=gnu23 -Wno-div-by-zero" } */
+
+enum e : bool { X };
+
+void
+f ()
+{
+  (enum e) (0 / 0);
+}
diff --git a/gcc/testsuite/gcc.c-torture/compile/pr111059-9.c b/gcc/testsuite/gcc.c-torture/compile/pr111059-9.c
new file mode 100644
index 000000000000..f88096d4b474
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/compile/pr111059-9.c
@@ -0,0 +1,9 @@
+/* { dg-options "-std=gnu23 -Wno-shift-count-negative" } */
+
+enum e : bool { X };
+
+enum e
+f ()
+{
+  return 1 << -1;
+}
diff --git a/gcc/testsuite/gcc.c-torture/compile/pr111911-1.c b/gcc/testsuite/gcc.c-torture/compile/pr111911-1.c
new file mode 100644
index 000000000000..350f4ccd1fdb
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/compile/pr111911-1.c
@@ -0,0 +1,7 @@
+int
+main (void)
+{
+  if (!(_Bool)(__INT_MAX__ + 1) / !(_Bool)(__INT_MAX__ + 1))
+    ;
+  return 1;
+}
diff --git a/gcc/testsuite/gcc.c-torture/compile/pr111911-2.c b/gcc/testsuite/gcc.c-torture/compile/pr111911-2.c
new file mode 100644
index 000000000000..28b69c46bdb7
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/compile/pr111911-2.c
@@ -0,0 +1,11 @@
+/* { dg-options "-std=gnu23 -Wno-overflow -Wno-div-by-zero" } */
+
+enum e : bool { X };
+
+int
+main (void)
+{
+  if (!(enum e)(__INT_MAX__ + 1) / !(enum e)(__INT_MAX__ + 1))
+    ;
+  return 1;
+}


More information about the Gcc-cvs mailing list