]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c, middle-end: Implement C2Y N3747 paper - Integer Sets, v5
authorJakub Jelinek <jakub@redhat.com>
Tue, 28 Apr 2026 06:54:42 +0000 (08:54 +0200)
committerJakub Jelinek <jakub@gcc.gnu.org>
Tue, 28 Apr 2026 06:54:42 +0000 (08:54 +0200)
C23 disallowed signed _BitInt(1), it only allowed unsigned _BitInt(1)
and signed _BitInt(2) and larger precisions.
The https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3747.pdf paper
changes this for C2Y and allows signed _BitInt(1) and (backwards
incompatibly) changes the type of 0wb from _BitInt(2) to _BitInt(1),
all other literals keep their earlier types.  The paper contains large
redesign of the C types hierarchy, but my understanding is that only
those two changes are changing something for users.

2026-04-28  Jakub Jelinek  <jakub@redhat.com>

gcc/
* tree.cc (build_bitint_type): Allow build_bitint_type (1, 0).
(signed_or_unsigned_type_for): Call that for !unsignedp case
of BITINT_TYPE with bits 1.
gcc/c-family/
* c-common.cc (c_common_signed_or_unsigned_type): Use
build_bitint_type for TREE_CODE (type) == BITINT_TYPE whenever
flag_isoc2y even when precision is 1.
(c_common_get_alias_set): Don't special case BITINT_TYPE
with precision 1 for flag_isoc2y.
* c-lex.cc (interpret_integer): Use _BitInt(1) type for 0wb
if flag_isoc2y, rather than _BitInt(2).
gcc/c/
* c-decl.cc (finish_declspecs) <case cts_bitint>: Implement
C2Y N3747 - Integer Sets, v5.  Allow signed _BitInt(1) for
flag_isoc2y.
gcc/testsuite/
* gcc.dg/torture/bitint-96.c: New test.
* gcc.dg/torture/bitint-97.c: New test.
* gcc.dg/torture/bitint-98.c: New test.
* gcc.dg/bitint-130.c: New test.
* gcc.dg/bitint-131.c: New test.
* gcc.dg/bitint-132.c: New test.

Reviewed-by: Joseph Myers <josmyers@redhat.com>
gcc/c-family/c-common.cc
gcc/c-family/c-lex.cc
gcc/c/c-decl.cc
gcc/testsuite/gcc.dg/bitint-130.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/bitint-131.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/bitint-132.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/torture/bitint-96.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/torture/bitint-97.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/torture/bitint-98.c [new file with mode: 0644]
gcc/tree.cc

index 5acb221d31f35f6b822ea1256949748270eaa67c..dcbf5192fc1604de3718be1ec4ee8bc5637c33f3 100644 (file)
@@ -2819,8 +2819,8 @@ c_common_signed_or_unsigned_type (int unsignedp, tree type)
     return type;
 
   if (TREE_CODE (type) == BITINT_TYPE
-      /* signed _BitInt(1) is invalid, avoid creating that.  */
-      && (unsignedp || TYPE_PRECISION (type) > 1))
+      /* signed _BitInt(1) is invalid before C2Y, avoid creating that.  */
+      && (unsignedp || flag_isoc2y || TYPE_PRECISION (type) > 1))
     return build_bitint_type (TYPE_PRECISION (type), unsignedp);
 
 #define TYPE_OK(node)                                                      \
@@ -3961,8 +3961,10 @@ c_common_get_alias_set (tree t)
         TYPE_ALIAS_SET_KNOWN_P.  */
       if (TYPE_UNSIGNED (t))
        {
-         /* There is no signed _BitInt(1).  */
-         if (TREE_CODE (t) == BITINT_TYPE && TYPE_PRECISION (t) == 1)
+         /* There is no signed _BitInt(1) before C2Y.  */
+         if (TREE_CODE (t) == BITINT_TYPE
+             && !flag_isoc2y
+             && TYPE_PRECISION (t) == 1)
            return -1;
          tree t1 = c_common_signed_type (t);
          gcc_checking_assert (t != t1);
index 1750fbc3d062448244b605ed3bbfa95bb7fb93a4..334ab3bf4e4cb641afaffb50b8c556b42a4d3f3f 100644 (file)
@@ -1076,7 +1076,7 @@ interpret_integer (const cpp_token *token, unsigned int flags,
        }
 
       prec = wi::min_precision (wval, UNSIGNED);
-      if (prec == 0)
+      if (prec == 0 && ((flags & CPP_N_UNSIGNED) != 0 || !flag_isoc2y))
        prec = 1;
       if ((flags & CPP_N_UNSIGNED) == 0)
        ++prec;
index 37bd01ab7b01e918ade88d82294d323f2f13633f..5e5193ed8809ab22a5a56c4d9faae8b5fa691d4c 100644 (file)
@@ -13651,10 +13651,11 @@ finish_declspecs (struct c_declspecs *specs)
     case cts_bitint:
       gcc_assert (!specs->long_p && !specs->short_p
                  && !specs->complex_p);
-      if (!specs->unsigned_p && specs->u.bitint_prec == 1)
+      if (!specs->unsigned_p && specs->u.bitint_prec == 1 && !flag_isoc2y)
        {
          error_at (specs->locations[cdw_typespec],
-                   "%<signed _BitInt%> argument must be at least 2");
+                   "%<signed _BitInt%> argument must be at least 2 "
+                   "before C2Y");
          specs->type = integer_type_node;
          break;
        }
diff --git a/gcc/testsuite/gcc.dg/bitint-130.c b/gcc/testsuite/gcc.dg/bitint-130.c
new file mode 100644 (file)
index 0000000..bc0305f
--- /dev/null
@@ -0,0 +1,52 @@
+/* C2Y N3747 - Integer Sets, v5 */
+/* { dg-do compile { target bitint } } */
+/* { dg-options "-std=c2y -pedantic-errors -O2" } */
+/* { dg-add-options float32 } */
+/* { dg-add-options float64 } */
+/* { dg-add-options float32x } */
+/* { dg-require-effective-target float32 } */
+/* { dg-require-effective-target float32x } */
+/* { dg-require-effective-target float64 } */
+
+[[gnu::noipa]] void
+foo (_BitInt(1) p[3], _Float32 q, _Float64 r, _Float32x s)
+{
+  p[0] = q;
+  p[1] = r;
+  p[2] = s;
+}
+
+[[gnu::noipa]] void
+bar (_BitInt(1) p[2], _Float32 q[2], _Float64 r[2], _Float32x s[2])
+{
+  q[0] = p[0];
+  q[1] = p[1];
+  r[0] = p[0];
+  r[1] = p[1];
+  s[0] = p[0];
+  s[1] = p[1];
+}
+
+int
+main ()
+{
+  _BitInt(1) e[15];
+  foo (e, 0.5f32, 0.25f64, 0.75f32x);
+  foo (e + 3, -0.5f32, -0.25f64, -0.75f32x);
+  foo (e + 6, 0.f32, 0.f64, 0.f32x);
+  foo (e + 9, -1.f32, -1.f64, -1.f32x);
+  foo (e + 12, -1.5f32, -1.25f64, -1.75f32x);
+  for (int i = 0; i < 15; ++i)
+    if (e[i] != (i < 9 ? 0 : -1))
+      __builtin_abort ();
+  e[0] = 0wb;
+  e[1] = ~0wb;
+  _Float32 j[2];
+  _Float64 k[2];
+  _Float32x l[2];
+  bar (e, j, k, l);
+  if (j[0] != 0.0f32 || j[1] != -1.0f32
+      || k[0] != 0.0f64 || k[1] != -1.0f64
+      || l[0] != 0.0f32x || l[1] != -1.0f32x)
+    __builtin_abort ();
+}
diff --git a/gcc/testsuite/gcc.dg/bitint-131.c b/gcc/testsuite/gcc.dg/bitint-131.c
new file mode 100644 (file)
index 0000000..6d785e4
--- /dev/null
@@ -0,0 +1,45 @@
+/* C2Y N3747 - Integer Sets, v5 */
+/* { dg-do compile { target bitint } } */
+/* { dg-options "-std=c2y -pedantic-errors -O2" } */
+/* { dg-add-options float128 } */
+/* { dg-add-options float64x } */
+/* { dg-require-effective-target float128 } */
+/* { dg-require-effective-target float64x } */
+
+[[gnu::noipa]] void
+foo (_BitInt(1) p[2], _Float128 q, _Float64x r)
+{
+  p[0] = q;
+  p[1] = r;
+}
+
+[[gnu::noipa]] void
+bar (_BitInt(1) p[2], _Float128 q[2], _Float64x r[2])
+{
+  q[0] = p[0];
+  q[1] = p[1];
+  r[0] = p[0];
+  r[1] = p[1];
+}
+
+int
+main ()
+{
+  _BitInt(1) e[10];
+  foo (e, 0.5f128, 0.25f64x);
+  foo (e + 2, -0.5f128, -0.25f64x);
+  foo (e + 4, 0.f128, 0.f64x);
+  foo (e + 6, -1.f128, -1.f64x);
+  foo (e + 8, -1.5f128, -1.25f64x);
+  for (int i = 0; i < 10; ++i)
+    if (e[i] != (i < 6 ? 0 : -1))
+      __builtin_abort ();
+  e[0] = 0wb;
+  e[1] = ~0wb;
+  _Float128 j[2];
+  _Float64x k[2];
+  bar (e, j, k);
+  if (j[0] != 0.0f128 || j[1] != -1.0f128
+      || k[0] != 0.0f64x || k[1] != -1.0f64x)
+    __builtin_abort ();
+}
diff --git a/gcc/testsuite/gcc.dg/bitint-132.c b/gcc/testsuite/gcc.dg/bitint-132.c
new file mode 100644 (file)
index 0000000..762ff49
--- /dev/null
@@ -0,0 +1,30 @@
+/* C2Y N3747 - Integer Sets, v5 */
+/* { dg-do compile { target bitint } } */
+/* { dg-options "-std=c2y -pedantic-errors -O2" } */
+/* { dg-add-options float16 } */
+/* { dg-require-effective-target float16_runtime } */
+
+[[gnu::noipa]] _BitInt(1)
+foo (_Float16 x)
+{
+  return x;
+}
+
+[[gnu::noipa]] _Float16
+bar (_BitInt(1) x)
+{
+  return x;
+}
+
+int
+main ()
+{
+  if (foo (0.5f16) != 0wb || foo (0.25f16) != 0wb
+      || foo (-0.5f16) != 0wb || foo (-0.25f16) != 0wb
+      || foo (0.f16) != 0wb || foo (-0.75f16) != 0wb
+      || foo (-1.f16) != ~0wb || foo (-1.75f16) != ~0wb
+      || foo (-1.5f16) != ~0wb || foo (-1.25f16) != ~0wb)
+    __builtin_abort ();
+  if (bar (0wb) != 0.f16 || bar (~0wb) != -1.f16)
+    __builtin_abort ();
+}
diff --git a/gcc/testsuite/gcc.dg/torture/bitint-96.c b/gcc/testsuite/gcc.dg/torture/bitint-96.c
new file mode 100644 (file)
index 0000000..6589d13
--- /dev/null
@@ -0,0 +1,166 @@
+/* C2Y N3747 - Integer Sets, v5 */
+/* { dg-do compile { target bitint } } */
+/* { dg-options "-std=c2y -pedantic-errors" } */
+
+#define expr_has_type(e, t) _Generic (e, default : 0, t : 1)
+
+void
+foo ()
+{
+  _BitInt(1) a = (_BitInt(1)) -1;
+  static_assert (expr_has_type (a, signed _BitInt(1)), "");
+  static_assert (expr_has_type (0wb, _BitInt(1)), "");
+  static_assert (expr_has_type (~0wb, _BitInt(1)), "");
+  static_assert (expr_has_type (1wb, _BitInt(2)), "");
+  static_assert (expr_has_type (-1wb, _BitInt(2)), "");
+  static_assert (expr_has_type (2wb, _BitInt(3)), "");
+  static_assert (expr_has_type (0wb + 0wb, signed _BitInt(1)), "");
+  static_assert (expr_has_type (0wb + 0uwb, unsigned _BitInt(1)), "");
+  static_assert (expr_has_type (0wb + 1wb, _BitInt(2)), "");
+  static_assert (expr_has_type (0uwb + 0wb, unsigned _BitInt(1)), "");
+  static_assert (expr_has_type (1wb + 0wb, _BitInt(2)), "");
+  static_assert (0wb == 0, "");
+  static_assert (~0wb == -1, "");
+  static_assert (~~0wb == 0, "");
+  static_assert (-1wb == -1, "");
+}
+
+_BitInt(1) b = 0wb;
+_BitInt(1) c = ~0wb;
+_BitInt(1) d = 0wb;
+
+[[gnu::noipa]] void
+bar (_BitInt(1) a[21], signed _BitInt(1) b[21], const _BitInt(1) c[21])
+{
+  a[0] = b[0] + c[0];
+  a[1] = b[1] + c[1];
+  a[2] = b[2] + c[2];
+  a[3] = b[3] - c[3];
+  a[4] = b[4] - c[4];
+  a[5] = b[5] - c[5];
+  a[6] = b[6] * c[6];
+  a[7] = b[7] * c[8];
+  a[8] = b[8] * c[8];
+  a[9] = b[9] / c[9];
+  a[10] = b[10] % c[10];
+  a[11] = b[11] % c[11];
+  a[12] = b[12] & c[12];
+  a[13] = b[13] & c[13];
+  a[14] = b[14] & c[14];
+  a[15] = b[15] | c[15];
+  a[16] = b[16] | c[16];
+  a[17] = b[17] | c[17];
+  a[18] = b[18] ^ c[18];
+  a[19] = b[19] ^ c[19];
+  a[20] = b[20] ^ c[20];
+}
+
+#if __BITINT_MAXWIDTH__ >= 256
+[[gnu::noipa]] _BitInt(256)
+baz (_BitInt(256) x, _BitInt(1) y)
+{
+  return x * y;
+}
+#endif
+
+[[gnu::noipa]] void
+qux (_BitInt(1) p[3], float q, double r, long double s)
+{
+  p[0] = q;
+  p[1] = r;
+  p[2] = s;
+}
+
+[[gnu::noipa]] void
+corge (_BitInt(1) p[2], float q[2], double r[2], long double s[2])
+{
+  q[0] = p[0];
+  q[1] = p[1];
+  r[0] = p[0];
+  r[1] = p[1];
+  s[0] = p[0];
+  s[1] = p[1];
+}
+
+int
+main ()
+{
+  if (b != 0 || c != -1)
+    __builtin_abort ();
+  --b;
+  ++c;
+  if (b != -1 || c != 0)
+    __builtin_abort ();
+  b = ~b;
+  c = ~c;
+  if (b != 0 || c != -1)
+    __builtin_abort ();
+  c *= d;
+  if (d != 0)
+    __builtin_abort ();
+  _BitInt(1) e[21];
+  _BitInt(1) f[21] = {
+    0, -1, 0,
+    0, -1, -1,
+    0, -1, 0,
+    0,
+    0, -1,
+    0, -1, -1,
+    0, 0, -1,
+    0, -1, -1
+  };
+  signed _BitInt(1) g[21] = {
+    0, 0, -1,
+    0, 0, -1,
+    0, 0, -1,
+    -1,
+    -1, -1,
+    0, 0, -1,
+    0, -1, -1,
+    0, 0, -1
+  }; 
+  bar (e, f, g);
+  _BitInt(1) h[21] = {
+    0, -1, -1,
+    0, -1, 0,
+    0, 0, 0,
+    0,
+    0, 0,
+    0, 0, -1,
+    0, -1, -1,
+    0, -1, 0
+  };
+  for (int i = 0; i < 21; ++i)
+    if (e[i] != h[i])
+      __builtin_abort ();
+#if __BITINT_MAXWIDTH__ >= 256
+  if (baz (24807048826655379640613156875228202584070386120119745959437529938064756141700wb, ~0wb)
+      != -24807048826655379640613156875228202584070386120119745959437529938064756141700wb)
+    __builtin_abort ();
+  if (baz (24807048826655379640613156875228202584070386120119745959437529938064756141700wb, 0wb) != 0wb)
+    __builtin_abort ();
+  if (baz (-24807048826655379640613156875228202584070386120119745959437529938064756141700wb, 0wb)
+      != 24807048826655379640613156875228202584070386120119745959437529938064756141700wb)
+    __builtin_abort ();
+  if (baz (0wb, ~0wb) != 0wb)
+    __builtin_abort ();
+#endif
+  qux (e, 0.5f, 0.25, 0.75L);
+  qux (e + 3, -0.5f, -0.25, -0.75L);
+  qux (e + 6, 0.f, 0., 0.L);
+  qux (e + 9, -1.f, -1., -1.L);
+  qux (e + 12, -1.5f, -1.25, -1.75L);
+  for (int i = 0; i < 15; ++i)
+    if (e[i] != (i < 9 ? 0 : -1))
+      __builtin_abort ();
+  e[0] = 0wb;
+  e[1] = ~0wb;
+  float j[2];
+  double k[2];
+  long double l[2];
+  corge (e, j, k, l);
+  if (j[0] != 0.0f || j[1] != -1.0f
+      || k[0] != 0.0 || k[1] != -1.0
+      || l[0] != 0.0L || l[1] != -1.0L)
+    __builtin_abort ();
+}
diff --git a/gcc/testsuite/gcc.dg/torture/bitint-97.c b/gcc/testsuite/gcc.dg/torture/bitint-97.c
new file mode 100644 (file)
index 0000000..569b3aa
--- /dev/null
@@ -0,0 +1,35 @@
+/* C2Y N3747 - Integer Sets, v5 */
+/* { dg-do compile { target bitint } } */
+/* { dg-options "-std=c2y -pedantic-errors -fwrapv" } */
+
+[[gnu::noipa]] void
+bar (_BitInt(1) a[8], signed _BitInt(1) b[8], const _BitInt(1) c[8])
+{
+  a[0] = b[0] + c[0];
+  a[1] = b[1] - c[1];
+  a[2] = b[2] / c[2];
+  a[3] = ++b[3];
+  a[4] = b[4]++;
+  a[5] = --b[5];
+  a[6] = b[6]--;
+  a[7] = -b[7];
+}
+
+int
+main ()
+{
+  _BitInt(1) e[8];
+  _BitInt(1) f[8] = {
+    -1, 0, -1, 0, 0, -1, -1, -1,
+  };
+  signed _BitInt(1) g[8] = {
+    -1, -1, -1, -1, -1, 0, 0, -1,
+  }; 
+  bar (e, f, g);
+  _BitInt(1) h[8] = {
+    0, -1, -1, -1, 0, 0, -1, -1
+  };
+  for (int i = 0; i < 8; ++i)
+    if (e[i] != h[i] || (i > 2 && f[i] != g[i]))
+      __builtin_abort ();
+}
diff --git a/gcc/testsuite/gcc.dg/torture/bitint-98.c b/gcc/testsuite/gcc.dg/torture/bitint-98.c
new file mode 100644 (file)
index 0000000..30aeb13
--- /dev/null
@@ -0,0 +1,88 @@
+/* C2Y N3747 - Integer Sets, v5 */
+/* { dg-do compile { target bitint } } */
+/* { dg-options "-std=c2y -pedantic-errors" } */
+/* { dg-skip-if "" { ! run_expensive_tests }  { "*" } { "-O0" "-O2" } } */
+/* { dg-skip-if "" { ! run_expensive_tests } { "-flto" } { "" } } */
+
+#if __BITINT_MAXWIDTH__ >= 256
+[[gnu::noipa]] bool
+foo (_BitInt(256) x, _BitInt(256) y, _BitInt(1) *z)
+{
+  return __builtin_add_overflow (x, y, z);
+}
+
+[[gnu::noipa]] bool
+bar (_BitInt(256) x, _BitInt(256) y, _BitInt(1) *z)
+{
+  return __builtin_sub_overflow (x, y, z);
+}
+
+[[gnu::noipa]] bool
+baz (_BitInt(256) x, _BitInt(256) y, _BitInt(1) *z)
+{
+  return __builtin_mul_overflow (x, y, z);
+}
+
+#endif
+
+int
+main ()
+{
+#if __BITINT_MAXWIDTH__ >= 256
+  _BitInt(1) z;
+  if (foo (42733374111348455377695489439454828259303231857505551329091595050247393780228wb,
+          -42733374111348455377695489439454828259303231857505551329091595050247393780228wb, &z)
+      || z)
+    __builtin_abort ();
+  if (foo (-42733374111348455377695489439454828259303231857505551329091595050247393780228wb,
+          42733374111348455377695489439454828259303231857505551329091595050247393780227wb, &z)
+      || z != ~0wb)
+    __builtin_abort ();
+  if (!foo (42733374111348455377695489439454828259303231857505551329091595050247393780228wb,
+           -42733374111348455377695489439454828259303231857505551329091595050247393780227wb, &z)
+      || z != ~0wb)
+    __builtin_abort ();
+  if (!foo (16820456121763970532229687954276499920583743168031969051240987845040434685573wb,
+           23294062539967091892447346238664352846257951403931317361440421661377004317436wb, &z)
+      || z != ~0wb)
+    __builtin_abort ();
+  if (!foo (16820456121763970532229687954276499920583743168031969051240987845040434685574wb,
+           23294062539967091892447346238664352846257951403931317361440421661377004317436wb, &z)
+      || z != 0wb)
+    __builtin_abort ();
+  if (bar (42733374111348455377695489439454828259303231857505551329091595050247393780228wb,
+          42733374111348455377695489439454828259303231857505551329091595050247393780228wb, &z)
+      || z)
+    __builtin_abort ();
+  if (bar (-42733374111348455377695489439454828259303231857505551329091595050247393780228wb,
+          -42733374111348455377695489439454828259303231857505551329091595050247393780227wb, &z)
+      || z != ~0wb)
+    __builtin_abort ();
+  if (!bar (42733374111348455377695489439454828259303231857505551329091595050247393780228wb,
+           42733374111348455377695489439454828259303231857505551329091595050247393780227wb, &z)
+      || z != ~0wb)
+    __builtin_abort ();
+  if (!bar (16820456121763970532229687954276499920583743168031969051240987845040434685573wb,
+           -23294062539967091892447346238664352846257951403931317361440421661377004317436wb, &z)
+      || z != ~0wb)
+    __builtin_abort ();
+  if (!bar (16820456121763970532229687954276499920583743168031969051240987845040434685574wb,
+           -23294062539967091892447346238664352846257951403931317361440421661377004317436wb, &z)
+      || z != 0wb)
+    __builtin_abort ();
+  if (baz (0, 0, &z) || z != 0wb)
+    __builtin_abort ();
+  if (baz (-1, 0, &z) || z != 0wb)
+    __builtin_abort ();
+  if (baz (-1, 1, &z) || z != ~0wb)
+    __builtin_abort ();
+  if (!baz (-1, -1, &z) || z != ~0wb)
+    __builtin_abort ();
+  if (!baz (30448945616760427177055226753240458517982932603339745354716666930701331527103wb,
+           42368953827467495356095621726856482300912964572736555031468887238137186859246wb, &z) || z != 0wb)
+    __builtin_abort ();
+  if (!baz (30448945616760427177055226753240458517982932603339745354716666930701331527103wb,
+           -42368953827467495356095621726856482300912964572736555031468887238137186859247wb, &z) || z != ~0wb)
+    __builtin_abort ();
+#endif
+}
index 2868cb0867dd805fa267a4edc890ded63ab17909..8479ffab584fd22e4cc337f8b5f91dc829c75290 100644 (file)
@@ -7421,7 +7421,7 @@ build_bitint_type (unsigned HOST_WIDE_INT precision, int unsignedp)
 {
   tree itype, ret;
 
-  gcc_checking_assert (precision >= 1 + !unsignedp);
+  gcc_checking_assert (precision >= 1);
 
   if (unsignedp)
     unsignedp = MAX_INT_CACHED_PREC + 1;
@@ -11440,7 +11440,7 @@ signed_or_unsigned_type_for (int unsignedp, tree type)
   else
     return NULL_TREE;
 
-  if (TREE_CODE (type) == BITINT_TYPE && (unsignedp || bits > 1))
+  if (TREE_CODE (type) == BITINT_TYPE)
     return build_bitint_type (bits, unsignedp);
   return build_nonstandard_integer_type (bits, unsignedp);
 }