diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index a8ff87fb600..4dc771a540c 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -912,6 +912,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 859cbee53d6..0e3d09b3a75 100644 --- a/libstdc++-v3/include/Makefile.in +++ b/libstdc++-v3/include/Makefile.in @@ -1249,6 +1249,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..1de2d18dc1a --- /dev/null +++ b/libstdc++-v3/include/c_compatibility/stdckdint.h @@ -0,0 +1,113 @@ +// C compatibility header -*- 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 +// . + +/** @file include/stdckdint.h + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_STDCKDINT_H +#define _GLIBCXX_STDCKDINT_H + +#if __cplusplus > 202302L +#include +#include + +#define __STDC_VERSION_STDCKDINT_H__ 202311L + +#ifndef _GLIBCXX_DOXYGEN +// We define these in our own namespace, but let Doxygen think otherwise. +namespace __gnu_cxx _GLIBCXX_VISIBILITY(default) +{ +#endif +/// @cond undocumented +namespace __detail +{ + template + concept __cv_unqual_signed_or_unsigned_integer_type + = std::same_as<_Tp, std::remove_cv_t<_Tp>> + && std::__is_standard_integer<_Tp>::value; +} +/// @endcond + +/** 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 + 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 + 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 + 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); + } +/// @} +#ifndef _GLIBCXX_DOXYGEN +} + +using __gnu_cxx::ckd_add; +using __gnu_cxx::ckd_sub; +using __gnu_cxx::ckd_mul; +#endif + +#endif // C++26 + +#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 3c25295850f..ba7ed0312ab 100644 --- a/libstdc++-v3/src/c++23/std.compat.cc.in +++ b/libstdc++-v3/src/c++23/std.compat.cc.in @@ -53,6 +53,14 @@ _GLIBCXX_STDBIT_FUNC(stdc_bit_ceil); #undef _GLIBCXX_STDBIT_FUNC } +// +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 + +#if __STDC_VERSION_STDCKDINT_H__ != 202311L +# error "__STDC_VERSION_STDCKDINT_H__ not defined correctly in " +#endif + +#include +#include + +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 + +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'" }