[2/2] libstdc++: Implement <stdckdint.h> for C++26 (P3370R1)

Message ID 20241203122054.281326-2-jwakely@redhat.com
State New
Headers
Series [1/2] libstdc++: Implement <stdbit.h> for C++26 (P3370R1) |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_gcc_check--master-aarch64 success Test passed
linaro-tcwg-bot/tcwg_gcc_check--master-arm success Test passed

Commit Message

Jonathan Wakely Dec. 3, 2024, 12:17 p.m. UTC
  This is the second part of the P3370R1 proposal just approved by the
committee in Wrocław. This adds C++ equivalents of the functions added
to C23 by WG14 N2683.

These functions are in the global namespace, but to avoid collisions
with the same functions defined by other standard library
implementations, this change defines them in namespace __gnu_cxx and
then adds them to the global namespace.

libstdc++-v3/ChangeLog:

	* include/Makefile.am: Add stdckdint.h.
	* include/Makefile.in: Regenerate.
	* src/c++23/std.compat.cc.in: Export <stdckdint.h> functions.
	* include/c_compatibility/stdckdint.h: New file.
	* testsuite/26_numerics/stdckdint/1.cc: New test.
	* testsuite/26_numerics/stdckdint/2_neg.cc: New test.
---

Tested x86_64-linux.

Also available at https://forge.sourceware.org/gcc/gcc-TEST/pulls/28

 libstdc++-v3/include/Makefile.am              |   1 +
 libstdc++-v3/include/Makefile.in              |   1 +
 .../include/c_compatibility/stdckdint.h       | 111 ++++++++++++++++++
 libstdc++-v3/src/c++23/std.compat.cc.in       |   8 ++
 .../testsuite/26_numerics/stdckdint/1.cc      |  63 ++++++++++
 .../testsuite/26_numerics/stdckdint/2_neg.cc  |  39 ++++++
 6 files changed, 223 insertions(+)
 create mode 100644 libstdc++-v3/include/c_compatibility/stdckdint.h
 create mode 100644 libstdc++-v3/testsuite/26_numerics/stdckdint/1.cc
 create mode 100644 libstdc++-v3/testsuite/26_numerics/stdckdint/2_neg.cc
  

Patch

diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index d7e487a1354..a41e4985245 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -909,6 +909,7 @@  c_compatibility_headers = \
 	${c_compatibility_srcdir}/math.h \
 	${c_compatibility_srcdir}/stdatomic.h \
 	${c_compatibility_srcdir}/stdbit.h \
+	${c_compatibility_srcdir}/stdckdint.h \
 	${c_compatibility_srcdir}/stdlib.h
 endif
 
diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in
index 2bdb2f7384d..203baad5929 100644
--- a/libstdc++-v3/include/Makefile.in
+++ b/libstdc++-v3/include/Makefile.in
@@ -1246,6 +1246,7 @@  c_compatibility_builddir = .
 @GLIBCXX_C_HEADERS_C_GLOBAL_TRUE@	${c_compatibility_srcdir}/math.h \
 @GLIBCXX_C_HEADERS_C_GLOBAL_TRUE@	${c_compatibility_srcdir}/stdatomic.h \
 @GLIBCXX_C_HEADERS_C_GLOBAL_TRUE@	${c_compatibility_srcdir}/stdbit.h \
+@GLIBCXX_C_HEADERS_C_GLOBAL_TRUE@	${c_compatibility_srcdir}/stdckdint.h \
 @GLIBCXX_C_HEADERS_C_GLOBAL_TRUE@	${c_compatibility_srcdir}/stdlib.h
 
 @GLIBCXX_C_HEADERS_C_STD_TRUE@c_compatibility_headers = 
diff --git a/libstdc++-v3/include/c_compatibility/stdckdint.h b/libstdc++-v3/include/c_compatibility/stdckdint.h
new file mode 100644
index 00000000000..ef3bc1ba458
--- /dev/null
+++ b/libstdc++-v3/include/c_compatibility/stdckdint.h
@@ -0,0 +1,111 @@ 
+// C compatibility header <stdckdint.h> -*- C++ -*-
+
+// Copyright The GNU Toolchain Authors.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+// <http://www.gnu.org/licenses/>.
+
+/** @file include/stdckdint.h
+ *  This is a Standard C++ Library header.
+ */
+
+#ifndef _GLIBCXX_STDCKDINT_H
+#define _GLIBCXX_STDCKDINT_H
+
+#if __cplusplus > 202302L
+#include <type_traits>
+#include <concepts>
+
+#define __STDC_VERSION_STDCKDINT_H__ 202311L
+
+namespace __gnu_cxx _GLIBCXX_VISIBILITY(default)
+{
+namespace __detail
+{
+  template<typename _Tp>
+    concept __signed_or_unsigned_integer_type
+      = std::__is_signed_integer<_Tp>::value
+	  || std::__is_unsigned_integer<_Tp>::value;
+
+  template<typename _Tp>
+    concept __cv_unqual_signed_or_unsigned_integer_type
+      = std::same_as<_Tp, std::remove_cvref_t<_Tp>>
+	  && __signed_or_unsigned_integer_type<_Tp>;
+}
+
+/** Checked integer arithmetic
+ *
+ * Performs arithmetic on `__a` and `__b` and stores the result in `*__result`,
+ * with overflow detection.
+ * The arithmetic is performed in infinite signed precision, without overflow,
+ * then converted to the result type, `_Tp1`. If the converted result is not
+ * equal to the infinite precision result, the stored result is wrapped to the
+ * width of `_Tp1` and `true` is returned. Otherwise, the stored result is
+ * correct and `false` is returned.
+ *
+ * @param __result A pointer to a signed or unsigned integer type.
+ * @param __a      A signed or unsigned integer type.
+ * @param __b      A signed or unsigned integer type.
+ * @return True if overflow occurred, false otherwise.
+ * @since C++26
+ * @{
+ */
+template<typename _Tp1, typename _Tp2, typename _Tp3>
+  inline bool
+  ckd_add(_Tp1* __result, _Tp2 __a, _Tp3 __b)
+  {
+    using __gnu_cxx::__detail::__cv_unqual_signed_or_unsigned_integer_type;
+    static_assert(__cv_unqual_signed_or_unsigned_integer_type<_Tp1>);
+    static_assert(__cv_unqual_signed_or_unsigned_integer_type<_Tp2>);
+    static_assert(__cv_unqual_signed_or_unsigned_integer_type<_Tp3>);
+    return __builtin_add_overflow(__a, __b, __result);
+  }
+
+template<typename _Tp1, typename _Tp2, typename _Tp3>
+  inline bool
+  ckd_sub(_Tp1* __result, _Tp2 __a, _Tp3 __b)
+  {
+    using __gnu_cxx::__detail::__cv_unqual_signed_or_unsigned_integer_type;
+    static_assert(__cv_unqual_signed_or_unsigned_integer_type<_Tp1>);
+    static_assert(__cv_unqual_signed_or_unsigned_integer_type<_Tp2>);
+    static_assert(__cv_unqual_signed_or_unsigned_integer_type<_Tp3>);
+    return __builtin_sub_overflow(__a, __b, __result);
+  }
+
+template<typename _Tp1, typename _Tp2, typename _Tp3>
+  inline bool
+  ckd_mul(_Tp1* __result, _Tp2 __a, _Tp3 __b)
+  {
+    using __gnu_cxx::__detail::__cv_unqual_signed_or_unsigned_integer_type;
+    static_assert(__cv_unqual_signed_or_unsigned_integer_type<_Tp1>);
+    static_assert(__cv_unqual_signed_or_unsigned_integer_type<_Tp2>);
+    static_assert(__cv_unqual_signed_or_unsigned_integer_type<_Tp3>);
+    return __builtin_mul_overflow(__a, __b, __result);
+  }
+/// @}
+}
+
+using __gnu_cxx::ckd_add;
+using __gnu_cxx::ckd_sub;
+using __gnu_cxx::ckd_mul;
+
+#endif // C++23
+
+#endif // _GLIBCXX_STDCKDINT_H
diff --git a/libstdc++-v3/src/c++23/std.compat.cc.in b/libstdc++-v3/src/c++23/std.compat.cc.in
index 0aa7e651df8..8b64ff4973b 100644
--- a/libstdc++-v3/src/c++23/std.compat.cc.in
+++ b/libstdc++-v3/src/c++23/std.compat.cc.in
@@ -127,6 +127,14 @@  export
   using __gnu_cxx::stdc_bit_ceil;
 }
 
+// <stdckdint.h>
+export
+{
+  using __gnu_cxx::ckd_add;
+  using __gnu_cxx::ckd_sub;
+  using __gnu_cxx::ckd_mul;
+}
+
 #define STD_COMPAT 1
 
 // C library exports are appended from std-clib.cc.in.
diff --git a/libstdc++-v3/testsuite/26_numerics/stdckdint/1.cc b/libstdc++-v3/testsuite/26_numerics/stdckdint/1.cc
new file mode 100644
index 00000000000..1402c834a7e
--- /dev/null
+++ b/libstdc++-v3/testsuite/26_numerics/stdckdint/1.cc
@@ -0,0 +1,63 @@ 
+// { dg-do run { target c++26 } }
+
+#include <stdckdint.h>
+
+#if __STDC_VERSION_STDCKDINT_H__ != 202311L
+# error "__STDC_VERSION_STDCKDINT_H__ not defined correctly in <stdckdint.h>"
+#endif
+
+#include <limits.h>
+#include <testsuite_hooks.h>
+
+void
+test_add()
+{
+  int result;
+  bool overflow;
+
+  overflow = ::ckd_add(&result, (unsigned)INT_MAX, 1LL);
+  VERIFY( overflow );
+  VERIFY( result == INT_MIN );
+
+  overflow = ::ckd_add(&result, (long long)INT_MIN, -1);
+  VERIFY( overflow );
+  VERIFY( result == INT_MAX );
+
+  overflow = ::ckd_add(&result, 99u, 100ll);
+  VERIFY( ! overflow );
+  VERIFY( result == 199 );
+}
+
+void
+test_sub()
+{
+  int result;
+  bool overflow;
+
+  overflow = ::ckd_sub(&result, -1, -5);
+  VERIFY( ! overflow );
+  VERIFY( result == 4 );
+}
+
+void
+test_mul()
+{
+  long long result;
+  bool overflow;
+
+  overflow = ::ckd_mul(&result, INT_MIN, -1);
+  VERIFY( ! overflow );
+  VERIFY( result == -(long long)INT_MIN );
+
+  unsigned uresult;
+  overflow = ::ckd_mul(&uresult, INT_MIN, -1);
+  VERIFY( ! overflow );
+  VERIFY( result == (unsigned)INT_MAX + 1u );
+}
+
+int main()
+{
+  test_add();
+  test_sub();
+  test_mul();
+}
diff --git a/libstdc++-v3/testsuite/26_numerics/stdckdint/2_neg.cc b/libstdc++-v3/testsuite/26_numerics/stdckdint/2_neg.cc
new file mode 100644
index 00000000000..7954da2d0d9
--- /dev/null
+++ b/libstdc++-v3/testsuite/26_numerics/stdckdint/2_neg.cc
@@ -0,0 +1,39 @@ 
+// { dg-do compile { target c++26 } }
+
+#include <stdckdint.h>
+
+void
+test_add(int i, char c, bool b)
+{
+  ::ckd_add(&i, c, 1); // { dg-error "here" }
+  ::ckd_add(&i, 1, c); // { dg-error "here" }
+  ::ckd_add(&i, b, 2); // { dg-error "here" }
+  ::ckd_add(&i, 2, b); // { dg-error "here" }
+  ::ckd_add(&c, 3, 3); // { dg-error "here" }
+  ::ckd_add((const int*)&i, 4, 4); // { dg-error "here" }
+}
+
+void
+test_sub(int i, char c, bool b)
+{
+  ::ckd_sub(&i, c, 1); // { dg-error "here" }
+  ::ckd_sub(&i, 1, c); // { dg-error "here" }
+  ::ckd_sub(&i, b, 2); // { dg-error "here" }
+  ::ckd_sub(&i, 2, b); // { dg-error "here" }
+  ::ckd_sub(&c, 3, 3); // { dg-error "here" }
+  ::ckd_sub((const int*)&i, 4, 4); // { dg-error "here" }
+}
+
+void
+test_mul(int i, char c, bool b)
+{
+  ::ckd_mul(&i, c, 1); // { dg-error "here" }
+  ::ckd_mul(&i, 1, c); // { dg-error "here" }
+  ::ckd_mul(&i, b, 2); // { dg-error "here" }
+  ::ckd_mul(&i, 2, b); // { dg-error "here" }
+  ::ckd_mul(&c, 3, 3); // { dg-error "here" }
+  ::ckd_mul((const int*)&i, 4, 4); // { dg-error "here" }
+}
+
+// { dg-prune-output "static assertion failed" }
+// { dg-prune-output "pointer to 'const'" }