https://gcc.gnu.org/g:1baba8d792105c8f36cf2345d778492e045d853c

commit r14-12927-g1baba8d792105c8f36cf2345d778492e045d853c
Author: Jakub Jelinek <[email protected]>
Date:   Wed Sep 16 22:22:20 2026 +0200

    c: Fix up integer promotion of bit-precise bit-fields [PR127383]
    
    The following testcases are miscompiled when a bit-precise integer
    bit-field is explicitly cast to a non-cv-qualified bit-precise integer
    (of the same signedness) with precision of the bit-field width.
    We can then incorrectly apply an integer promotion of the bit-field
    to the corresponding bit-precise integer, even when it shouldn't be
    applied (because it is a cast rather than the bit-field itself).
    Regardless of the cv-qualifications, the TREE_TYPE of the FIELD_DECLs
    (and COMPONENT_REFs) is a cv-unqualified BITINT_TYPE.  We first call
    convert to that type in convert_lvalue_to_rvalue and that doesn't change
    anything.
    If there is an explicit cast to the same type as the TREE_TYPE of
    the COMPONENT_REF has, we usually wrap it into a NON_LVALUE_EXPR, because
    the COMPONENT_REF is usually an lvalue (but as can be seen in the testcases,
    not always).  So the second hunk makes sure to add NON_LVALUE_EXPR around
    the COMPONENT_REF even in the (cast) foo ().a cases to distinguish it
    from foo ().a.
    The second change is in default_conversion, for the promotion of bit-fields
    of bit-precise integer type we don't promote them if they are wrapped in
    some cast or NON_LVALUE_EXPR (the function normally uses STRIP_TYPE_NOPS).
    
    2026-09-16  Jakub Jelinek  <[email protected]>
    
            PR c/127383
            * c-typeck.cc (default_conversion): If orig_expr is different
            from exp and exp is a bit-field of bit-precise integer type, call
            perform_integral_promotions on orig_exp rather than exp.
            (build_c_cast): Wrap in NON_LVALUE_EXPR not just lvalues, but
            also non-lvalue bit-fields of bit-precise integer type.
    
            * gcc.dg/torture/bitint-108.c: New test.
            * gcc.dg/bitint-143.c: New test.
    
    Reviewed-by: Joseph Myers <[email protected]>
    (cherry picked from commit d7fa48a8500bcfaa1cf4ae3f917c22aa6ec7c75f)

Diff:
---
 gcc/c/c-typeck.cc                         | 19 +++++++++++--
 gcc/testsuite/gcc.dg/bitint-143.c         | 45 +++++++++++++++++++++++++++++++
 gcc/testsuite/gcc.dg/torture/bitint-108.c | 16 +++++++++++
 3 files changed, 78 insertions(+), 2 deletions(-)

diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index e1c35cdcda12..f9366f42fedd 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -2603,7 +2603,17 @@ default_conversion (tree exp)
     return convert (promoted_type, exp);
 
   if (INTEGRAL_TYPE_P (type))
-    return perform_integral_promotions (exp);
+    {
+      /* Avoid performing integral promotion of bit-precise integer
+        type bit-field to its corresponding bit-precise integer if
+        orig_exp is a cast to it to its underlying type.  */
+      if (orig_exp != exp
+         && TREE_CODE (exp) == COMPONENT_REF
+         && DECL_C_BIT_FIELD (TREE_OPERAND (exp, 1))
+         && BITINT_TYPE_P (DECL_BIT_FIELD_TYPE (TREE_OPERAND (exp, 1))))
+       return perform_integral_promotions (orig_exp);
+      return perform_integral_promotions (exp);
+    }
 
   return exp;
 }
@@ -6647,7 +6657,12 @@ build_c_cast (location_t loc, tree type, tree expr)
     }
 
   /* Don't let a cast be an lvalue.  */
-  if (lvalue_p (value))
+  if (lvalue_p (value)
+      /* Also, don't let a cast be a bit-field of a bit-precise integer type,
+        otherwise it could be incorrectly subject of integer promotions.  */
+      || (TREE_CODE (value) == COMPONENT_REF
+         && DECL_C_BIT_FIELD (TREE_OPERAND (value, 1))
+         && BITINT_TYPE_P (DECL_BIT_FIELD_TYPE (TREE_OPERAND (value, 1)))))
     value = non_lvalue_loc (loc, value);
 
   /* Don't allow the results of casting to floating-point or complex
diff --git a/gcc/testsuite/gcc.dg/bitint-143.c 
b/gcc/testsuite/gcc.dg/bitint-143.c
new file mode 100644
index 000000000000..eedddce2b221
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/bitint-143.c
@@ -0,0 +1,45 @@
+/* PR c/127383 */
+/* { dg-do compile { target bitint } } */
+/* { dg-options "-std=c23" } */
+
+#define expr_has_type(e, t) _Generic (e, default : 0, t : 1)
+
+struct S { _BitInt(195) a : 193; const unsigned _BitInt(195) b : 193; volatile 
_BitInt(195) c : 193; const volatile unsigned _BitInt(195) d : 193; };
+struct S s;
+struct S foo ();
+static_assert (expr_has_type (+s.a, _BitInt(195)));
+static_assert (expr_has_type (+s.b, unsigned _BitInt(195)));
+static_assert (expr_has_type (+s.c, _BitInt(195)));
+static_assert (expr_has_type (+s.d, unsigned _BitInt(195)));
+static_assert (expr_has_type ((_BitInt(193)) s.a, _BitInt(193)));
+static_assert (expr_has_type ((unsigned _BitInt(193)) s.b, unsigned 
_BitInt(193)));
+static_assert (expr_has_type ((_BitInt(193)) s.c, _BitInt(193)));
+static_assert (expr_has_type ((unsigned _BitInt(193)) s.d, unsigned 
_BitInt(193)));
+static_assert (expr_has_type (+(_BitInt(193)) s.a, _BitInt(193)));
+static_assert (expr_has_type (+(unsigned _BitInt(193)) s.b, unsigned 
_BitInt(193)));
+static_assert (expr_has_type (+(_BitInt(193)) s.c, _BitInt(193)));
+static_assert (expr_has_type (+(unsigned _BitInt(193)) s.d, unsigned 
_BitInt(193)));
+static_assert (expr_has_type ((const _BitInt(193)) s.a, _BitInt(193)));
+static_assert (expr_has_type ((volatile unsigned _BitInt(193)) s.b, unsigned 
_BitInt(193)));
+static_assert (expr_has_type ((const volatile _BitInt(193)) s.c, 
_BitInt(193)));
+static_assert (expr_has_type ((const unsigned _BitInt(193)) s.d, unsigned 
_BitInt(193)));
+static_assert (expr_has_type (+(_BitInt(193)) s.a, _BitInt(193)));
+static_assert (expr_has_type (+(unsigned _BitInt(193)) s.b, unsigned 
_BitInt(193)));
+static_assert (expr_has_type (+(_BitInt(193)) s.c, _BitInt(193)));
+static_assert (expr_has_type (+(unsigned _BitInt(193)) s.d, unsigned 
_BitInt(193)));
+static_assert (expr_has_type (+foo ().a, _BitInt(195)));
+static_assert (expr_has_type (+foo ().b, unsigned _BitInt(195)));
+static_assert (expr_has_type (+foo ().c, _BitInt(195)));
+static_assert (expr_has_type (+foo ().d, unsigned _BitInt(195)));
+static_assert (expr_has_type ((_BitInt(193)) foo ().a, _BitInt(193)));
+static_assert (expr_has_type ((unsigned _BitInt(193)) foo ().b, unsigned 
_BitInt(193)));
+static_assert (expr_has_type ((_BitInt(193)) foo ().c, _BitInt(193)));
+static_assert (expr_has_type ((unsigned _BitInt(193)) foo ().d, unsigned 
_BitInt(193)));
+static_assert (expr_has_type (+(_BitInt(193)) foo ().a, _BitInt(193)));
+static_assert (expr_has_type (+(unsigned _BitInt(193)) foo ().b, unsigned 
_BitInt(193)));
+static_assert (expr_has_type (+(_BitInt(193)) foo ().c, _BitInt(193)));
+static_assert (expr_has_type (+(unsigned _BitInt(193)) foo ().d, unsigned 
_BitInt(193)));
+static_assert (expr_has_type ((const _BitInt(193)) foo ().a, _BitInt(193)));
+static_assert (expr_has_type ((volatile unsigned _BitInt(193)) foo ().b, 
unsigned _BitInt(193)));
+static_assert (expr_has_type ((const volatile _BitInt(193)) foo ().c, 
_BitInt(193)));
+static_assert (expr_has_type ((const unsigned _BitInt(193)) foo ().d, unsigned 
_BitInt(193)));
diff --git a/gcc/testsuite/gcc.dg/torture/bitint-108.c 
b/gcc/testsuite/gcc.dg/torture/bitint-108.c
new file mode 100644
index 000000000000..19c9b3e046fc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/bitint-108.c
@@ -0,0 +1,16 @@
+/* PR c/127383 */
+/* { dg-do run { target bitint } } */
+
+#if __BITINT_MAXWIDTH__ >= 65
+struct { unsigned _BitInt(65) f : 63; } s;
+#endif
+
+int
+main ()
+{
+#if __SIZEOF_LONG_LONG__ * __CHAR_BIT__ == 64 && __BITINT_MAXWIDTH__ >= 65
+  unsigned long long w = ~(unsigned _BitInt(63)) s.f;
+  if (w != (-1ULL >> 1))
+    __builtin_abort ();
+#endif
+}

Reply via email to