Hi! 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).
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 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. * gcc.dg/bitint-144.c: New test. --- a/gcc/c/c-typeck.cc 2026-09-14 18:09:11.662013474 +0200 +++ b/gcc/c/c-typeck.cc 2026-09-15 12:48:07.213853939 +0200 @@ -2907,7 +2907,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; } @@ -7673,7 +7683,12 @@ build_c_cast (location_t loc, tree type, } /* 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 --- a/gcc/testsuite/gcc.dg/torture/bitint-108.c 2026-09-15 12:12:22.187326991 +0200 +++ b/gcc/testsuite/gcc.dg/torture/bitint-108.c 2026-09-15 12:12:22.187326991 +0200 @@ -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 +} --- a/gcc/testsuite/gcc.dg/bitint-143.c 2026-09-15 12:57:57.790919968 +0200 +++ b/gcc/testsuite/gcc.dg/bitint-143.c 2026-09-15 13:03:42.837299132 +0200 @@ -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))); --- a/gcc/testsuite/gcc.dg/bitint-144.c 2026-09-15 12:58:08.192780255 +0200 +++ b/gcc/testsuite/gcc.dg/bitint-144.c 2026-09-15 13:03:49.732206830 +0200 @@ -0,0 +1,47 @@ +/* PR c/127383 */ +/* { dg-do compile { target bitint } } */ +/* { dg-options "-std=c2y" } */ + +#define expr_has_type(e, t) _Generic (e, default : 0, t : 1) +enum E : _BitInt(195) { E0 = 0, E1 = 1, EM1 = -1 }; +enum F : unsigned _BitInt(195) { F0 = 0, F1 = 1 }; + +struct S { enum E a : 193; const enum F b : 193; volatile enum E c : 193; const volatile enum F 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))); Jakub
