Hi Abdulmalek,

I need to be humble because this is a complex area that has been debated for decades, but I'll try to answer.

On 24/09/2026 03:21, Abdulmalek Almkainzi wrote:
Hello.

gcc treats bit-field expressions as having different type from its declared integer type.

for example:

```
struct S { unsigned i : 1; };

struct S s;
int i = _Generic(s.i, unsigned: 1, default: 2);
```

It would be problematic if "unsigned: 1" were a type name, wouldn't it?

Unfortunately, that would also be the most natural way of writing such a type, analogous to how one could write 'int (double)' to name the type of a function that has a parameter of type double and a return value of type int.


`i` is 2 in gcc, but 1 in clang and msvc.
I think clang/msvc follow the standard more correctly here:

6.7.3.2 paragraph 5

> A bit-field shall have a type that is a qualified or unqualified bool, signed int, unsigned int, a > bit-precise integer type, or other implementation-defined type. It is implementation-defined whether
 > atomic types are permitted.

My understanding is that GCC uses the "other implementation-defined type" escape hatch to justify its behaviour.

I did a simple fix in c_parser_generic_selection where I convert the type of a bit-field expression to its declared integer type.

This does make a different test fail (c23-auto-1.c), specifically this part:
```
   struct b { int a : 2; unsigned b : 3; } bv = { };
   auto bfa = bv.a;
   auto bfb = bv.b;
   static_assert (_Generic (bv.a, typeof (bfa) : 1, default : 2) == 1);
   static_assert (_Generic (bv.b, typeof (bfb) : 1, default : 2) == 1);
```
if the rhs of a auto type inference assignment is a bit-field, the variable will have a bit-field type. and that type matches a controlling operand of a bit-field expression.

Yes; GCC's interpretation of the C standard implies the existence of a vast number of integer types. At first glance, there seems to be no way of naming such bit-field types outside the specific context of a member declaration.

One might therefore expect _Generic to be useless for matching with such unnamable types, except in contrived tautological (and near-tautological) cases like these:

int main(int argc, char *argv[])
{
    struct bf {
        unsigned int i:31;
    } b = {};
    auto z = b.i;
    _Static_assert(_Generic(z, typeof(z):1, default: 0), "");
    _Static_assert(_Generic(b.i, typeof(z):1, default: 0), "");
    return z;
}

See https://godbolt.org/z/aM71x14TT

Surprisingly, one can work around this apparent inability to name specific-width integer types by using compound literals:

#include <stdio.h>

#define BF_TYPE(N) typeof(0, (struct {int j:(N);}){}.j)

#define NAME(T) \
  _Generic((T), BF_TYPE(1):1, default: 0) ? "one" : \
  _Generic((T), BF_TYPE(2):1, default: 0) ? "two" : \
  _Generic((T), BF_TYPE(3):1, default: 0) ? "three" : \
  _Generic((T), BF_TYPE(4):1, default: 0) ? "four" : \
  "many"

int main(void)
{
    struct bf {
        int i:3, j:4, k:1, l:2;
    } b;
    printf("%s %s %s %s", NAME(b.i), NAME(b.j), NAME(b.k), NAME(b.l));
    return 0;
}

See https://godbolt.org/z/z64Gz56x5

So, the extra integer types supported by GCC can actually be named -- just not without use of typeof.

The comma operator is used to work around what appears to be an arbitrary constraint. Without the comma operator, both Clang and GCC report a variant of "error: 'typeof' applied to a bit-field"; with it, the program compiled by GCC prints "three four one two" (as expected), whereas the program compiled by Clang prints "one one one one".

Interestingly, I cannot find any prohibition in C23 against using an assignment expression of bitfield type in a declaration that does type inference using 'auto'. Nevertheless, Clang (but not GCC) reports "error: cannot pass bit-field as __auto_type initializer in C". This appears to be a bug in Clang -- especially given that it can seemingly be bypassed by using the comma operator just as I did when specifying an expression of bitfield type as the operand of typeof.

int main(int argc, char *argv[])
{
    struct bf {
        int i:3, j:4, k:1, l:2;
    } b = {};
    auto x = (argc++, b.i); // no error
    auto y = b.i; // error only from Clang
    return 0;
}

See https://godbolt.org/z/7GfMK7P5G

HTH,
--
Christopher Bazley
Staff Software Engineer, GNU Tools Team.
Arm Ltd, 110 Fulbourn Road, Cambridge, CB1 9NJ, UK.
http://www.arm.com/

Reply via email to