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);
```

`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.

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.


I don't know what's the best course of action here:

1. the current behavior is ok.
2. apply the _Genreic fix and remove this test.
3. change auto type inference. If the rhs is a bit-field, use its declared integer type, not its bit-field type.

I've attached a patch that only applies a fix to _Generic (c23-auto-1.c test fails), and another patch that additionally makes the auto inferred type not a bit-field type (test passes).

Note this is my first message and patch in this mailing list, please let me know if I'm missing something.

Regards,
Abdulmalek Almkainzi
From f8f7e71f64f0ff855ff5f821e02b5ff14d019eb7 Mon Sep 17 00:00:00 2001
From: Abdulmalek Almkainzi <[email protected]>
Date: Thu, 24 Sep 2026 04:49:09 +0300
Subject: [RFC PATCH] c: _Generic bit-field fix

If the controlling expression of _Generic is a bit-field,
use the bit-field's underlying integer type for compatibility
checking.

Also, in auto type inference, if the rhs of the assignment is a
bit-field expression, the type of the variable is the bit-field's
declared integer type.

gcc/c/ChangeLog:

	* c-parser.cc (c_parser_declaration_or_fndef): If the rhs of
	the assignment is a bit-field, use its underlying integer type
	as the init_type.
	(c_parser_generic_selection): If the controlling operand is a
	bit-field expression, use its underlying integer type as the
	selector_type.

gcc/testsuite/ChangeLog:

	* gcc.dg/c11-generic-5.c: New test.

Signed-off-by: Abdulmalek Almkainzi <[email protected]>
---
 gcc/c/c-parser.cc                    | 14 ++++++++++++++
 gcc/testsuite/gcc.dg/c11-generic-5.c |  6 ++++++
 2 files changed, 20 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/c11-generic-5.c

diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index 927aff6d5b8..5ec77fc494f 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -2945,6 +2945,13 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
 		  init = convert_lvalue_to_rvalue (init_loc, init, true, true,
 						   true);
 		  tree init_type = TREE_TYPE (init.value);
+		  if (TREE_CODE (init.value) == COMPONENT_REF)
+		    {
+		      tree field = TREE_OPERAND (init.value, 1);
+		      if (TREE_CODE (field) == FIELD_DECL
+			  && DECL_C_BIT_FIELD (field))
+			init_type = DECL_BIT_FIELD_TYPE (field);
+		    }
 		  bool vm_type = c_type_variably_modified_p (init_type);
 		  if (vm_type)
 		    init.value = save_expr (init.value);
@@ -11416,6 +11423,13 @@ c_parser_generic_selection (c_parser *parser)
 	}
       mark_exp_read (selector.value);
       selector_type = TREE_TYPE (selector.value);
+      if (TREE_CODE (selector.value) == COMPONENT_REF)
+	{
+	  tree field = TREE_OPERAND (selector.value, 1);
+	  if (TREE_CODE (field) == FIELD_DECL && DECL_C_BIT_FIELD (field))
+	    selector_type = DECL_BIT_FIELD_TYPE (field);
+	}
+
       /* In ISO C terms, rvalues (including the controlling expression
 	 of _Generic) do not have qualified types.  */
       if (TREE_CODE (selector_type) != ARRAY_TYPE)
diff --git a/gcc/testsuite/gcc.dg/c11-generic-5.c b/gcc/testsuite/gcc.dg/c11-generic-5.c
new file mode 100644
index 00000000000..02c54779c1e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c11-generic-5.c
@@ -0,0 +1,6 @@
+/* { dg-do compile } */
+/* { dg-options "-std=c11" } */
+
+struct S { unsigned i : 1; };
+
+_Static_assert(_Generic((struct S){0}.i, unsigned: 1, default: 0), "bit-field should be compatible with its underlying integer type");
-- 
2.53.0

From 616847f2951dfd4107b7371b834a9270bd1c173b Mon Sep 17 00:00:00 2001
From: Abdulmalek Almkainzi <[email protected]>
Date: Thu, 24 Sep 2026 05:03:14 +0300
Subject: [RFC PATCH] c: _Generic and auto bit-field fix

If the controlling expression of _Generic is a bit-field,
use the bit-field's underlying integer type for compatibility
checking.

Also, if the rhs of an auto type inference assignment is a
bit-field expression, the type of the variable is the bit-field's
underlying integer type.

gcc/c/ChangeLog:

	* c-parser.cc (c_parser_declaration_or_fndef): if the rhs of an
	auto type inference assignment is a bit-field expression, the type
	of the variable is the bit-field's underlying integer type.
	(c_parser_generic_selection): If the controlling operand is a
	bit-field expression, use its underlying integer type as the
	selector_type.

gcc/testsuite/ChangeLog:

	* gcc.dg/c11-generic-5.c: New test.

Signed-off-by: Abdulmalek Almkainzi <[email protected]>
---
 gcc/c/c-parser.cc                    | 14 ++++++++++++++
 gcc/testsuite/gcc.dg/c11-generic-5.c |  6 ++++++
 2 files changed, 20 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/c11-generic-5.c

diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index 927aff6d5b8..5ec77fc494f 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -2945,6 +2945,13 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
 		  init = convert_lvalue_to_rvalue (init_loc, init, true, true,
 						   true);
 		  tree init_type = TREE_TYPE (init.value);
+		  if (TREE_CODE (init.value) == COMPONENT_REF)
+		    {
+		      tree field = TREE_OPERAND (init.value, 1);
+		      if (TREE_CODE (field) == FIELD_DECL
+			  && DECL_C_BIT_FIELD (field))
+			init_type = DECL_BIT_FIELD_TYPE (field);
+		    }
 		  bool vm_type = c_type_variably_modified_p (init_type);
 		  if (vm_type)
 		    init.value = save_expr (init.value);
@@ -11416,6 +11423,13 @@ c_parser_generic_selection (c_parser *parser)
 	}
       mark_exp_read (selector.value);
       selector_type = TREE_TYPE (selector.value);
+      if (TREE_CODE (selector.value) == COMPONENT_REF)
+	{
+	  tree field = TREE_OPERAND (selector.value, 1);
+	  if (TREE_CODE (field) == FIELD_DECL && DECL_C_BIT_FIELD (field))
+	    selector_type = DECL_BIT_FIELD_TYPE (field);
+	}
+
       /* In ISO C terms, rvalues (including the controlling expression
 	 of _Generic) do not have qualified types.  */
       if (TREE_CODE (selector_type) != ARRAY_TYPE)
diff --git a/gcc/testsuite/gcc.dg/c11-generic-5.c b/gcc/testsuite/gcc.dg/c11-generic-5.c
new file mode 100644
index 00000000000..02c54779c1e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c11-generic-5.c
@@ -0,0 +1,6 @@
+/* { dg-do compile } */
+/* { dg-options "-std=c11" } */
+
+struct S { unsigned i : 1; };
+
+_Static_assert(_Generic((struct S){0}.i, unsigned: 1, default: 0), "bit-field should be compatible with its underlying integer type");
-- 
2.53.0

Reply via email to