diff --git a/stl/inc/__msvc_int128.hpp b/stl/inc/__msvc_int128.hpp index e26d7ead234..bcb3b6e0cab 100644 --- a/stl/inc/__msvc_int128.hpp +++ b/stl/inc/__msvc_int128.hpp @@ -9,12 +9,26 @@ #include #if _STL_COMPILER_PREPROCESSOR -#ifdef __cpp_lib_concepts +#include +#include // TRANSITION, GH-2520 +#include +#include + +#if _HAS_CXX20 #include #include +#define _ZERO_OR_NO_INIT +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv +#define _ZERO_OR_NO_INIT \ + {} // Trivial default initialization is not allowed in constexpr functions before C++20. +#endif // ^^^ !_HAS_CXX20 ^^^ + +#ifdef __cpp_lib_concepts #include -#include -#include // TRANSITION, GH-2520 +#define _TEMPLATE_CLASS_INTEGRAL(type) template +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv +#define _TEMPLATE_CLASS_INTEGRAL(type) template , int> = 0> +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) @@ -25,17 +39,18 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN -#if defined(_M_X64) && !defined(_M_ARM64EC) +#if defined(_M_X64) && !defined(_M_ARM64EC) && !defined(_M_CEE_PURE) && !defined(__CUDACC__) \ + && !defined(__INTEL_COMPILER) #define _STL_128_INTRINSICS 1 #ifdef __clang__ // clang doesn't have _udiv128 / _div128 #define _STL_128_DIV_INTRINSICS 0 #else // ^^^ Clang / other vvv #define _STL_128_DIV_INTRINSICS 1 #endif // ^^^ detect _udiv128 / _div128 ^^^ -#else // ^^^ x64 / other vvv +#else // ^^^ intrinsics available / intrinsics unavailable vvv #define _STL_128_INTRINSICS 0 #define _STL_128_DIV_INTRINSICS 0 -#endif // defined(_M_X64) && !defined(_M_ARM64EC) +#endif // ^^^ intrinsics unavailable ^^^ struct #ifndef _M_ARM @@ -57,7 +72,7 @@ struct } #if _STL_128_INTRINSICS - if (!_STD is_constant_evaluated()) { + if (!_Is_constant_evaluated()) { _Word[1] = __shiftleft128(_Word[0], _Word[1], _Count); } else #endif // _STL_128_INTRINSICS @@ -81,7 +96,7 @@ struct } #if _STL_128_INTRINSICS - if (!_STD is_constant_evaluated()) { + if (!_Is_constant_evaluated()) { _Word[0] = __shiftright128(_Word[0], _Word[1], _Count); } else #endif // _STL_128_INTRINSICS @@ -96,7 +111,7 @@ struct unsigned char _Carry, uint64_t _Left, uint64_t _Right, uint64_t& _Result) noexcept { // _STL_INTERNAL_CHECK(_Carry < 2); #if _STL_128_INTRINSICS - if (!_STD is_constant_evaluated()) { + if (!_Is_constant_evaluated()) { return _addcarry_u64(_Carry, _Left, _Right, &_Result); } #endif // _STL_128_INTRINSICS @@ -110,7 +125,7 @@ struct unsigned char _Carry, uint64_t _Left, uint64_t _Right, uint64_t& _Result) noexcept { // _STL_INTERNAL_CHECK(_Carry < 2); #if _STL_128_INTRINSICS - if (!_STD is_constant_evaluated()) { + if (!_Is_constant_evaluated()) { return _subborrow_u64(_Carry, _Left, _Right, &_Result); } #endif // _STL_128_INTRINSICS @@ -148,7 +163,7 @@ struct _NODISCARD static constexpr uint64_t _UMul128( const uint64_t _Left, const uint64_t _Right, uint64_t& _High_result) noexcept { #if _STL_128_INTRINSICS - if (!_STD is_constant_evaluated()) { + if (!_Is_constant_evaluated()) { return _umul128(_Left, _Right, &_High_result); } #endif // _STL_128_INTRINSICS @@ -161,7 +176,7 @@ struct static_cast(_Right), static_cast(_Right >> 32), }; - uint32_t __w[4]; + uint32_t __w[4] _ZERO_OR_NO_INIT; // multiply 2-digit numbers with 4-digit result in base 2^32 _Knuth_4_3_1_M(__u, __v, __w); @@ -197,7 +212,7 @@ struct } int64_t __k = 0; - int64_t __t; + int64_t __t _ZERO_OR_NO_INIT; for (int __i = 0; __i < static_cast(__n); ++__i) { const auto _Prod = static_cast(__qhat) * static_cast(__v[__i]); __t = __u[__i + __j] - __k - static_cast(_Prod); @@ -228,12 +243,16 @@ struct // _STL_INTERNAL_CHECK(_High < _Div); #if _STL_128_DIV_INTRINSICS - if (!_STD is_constant_evaluated()) { + if (!_Is_constant_evaluated()) { return _udiv128(_High, _Low, _Div, &_Remainder); } #endif // _STL_128_DIV_INTRINSICS +#if _HAS_CXX20 const auto __d = _STD countl_zero(static_cast(_Div >> 32)); +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv + const auto __d = _Countl_zero_fallback(static_cast(_Div >> 32)); +#endif // ^^^ !_HAS_CXX20 ^^^ if (__d >= 32) { // _Div < 2^32 auto _Rem = (_High << 32) | (_Low >> 32); auto _Result = _Rem / static_cast(_Div); @@ -259,7 +278,7 @@ struct static_cast(_Div << __d), static_cast(_Div >> (32 - __d)), }; - uint32_t __q[3]; + uint32_t __q[3] _ZERO_OR_NO_INIT; _Knuth_4_3_1_D(__u, 5, __v, 2, __q); // _STL_INTERNAL_CHECK(__u[4] == 0); @@ -273,9 +292,14 @@ struct constexpr _Base128() noexcept : _Word{} {} - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) constexpr _Base128(const _Ty _Val) noexcept : _Word{static_cast(_Val)} { - if constexpr (signed_integral<_Ty>) { +#ifdef __cpp_lib_concepts + if constexpr (signed_integral<_Ty>) +#else + if constexpr (is_signed_v<_Ty>) +#endif + { if (_Val < 0) { _Word[1] = ~0ull; } @@ -284,7 +308,7 @@ struct constexpr explicit _Base128(const uint64_t _Low, const uint64_t _High) noexcept : _Word{_Low, _High} {} - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) _NODISCARD constexpr explicit operator _Ty() const noexcept { return static_cast<_Ty>(_Word[0]); } @@ -293,7 +317,17 @@ struct return (_Word[0] | _Word[1]) != 0; } +#if _HAS_CXX20 _NODISCARD_FRIEND constexpr bool operator==(const _Base128&, const _Base128&) noexcept = default; +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv + _NODISCARD_FRIEND constexpr bool operator==(const _Base128& _Left, const _Base128& _Right) noexcept { + return _Left._Word[0] == _Right._Word[0] && _Left._Word[1] == _Right._Word[1]; + } + + _NODISCARD_FRIEND constexpr bool operator!=(const _Base128& _Left, const _Base128& _Right) noexcept { + return !(_Left == _Right); + } +#endif // ^^^ !_HAS_CXX20 ^^^ _NODISCARD_FRIEND constexpr bool operator<(const _Base128& _Left, const _Base128& _Right) noexcept { if (_Left._Word[1] < _Right._Word[1]) { @@ -315,35 +349,35 @@ struct return !(_Left < _Right); } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) _NODISCARD_FRIEND constexpr _Ty operator<<(const _Ty _Left, const _Base128& _Right) noexcept { return _Left << _Right._Word[0]; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) _NODISCARD_FRIEND constexpr _Ty operator>>(const _Ty _Left, const _Base128& _Right) noexcept { return _Left >> _Right._Word[0]; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) constexpr _Base128& operator<<=(const _Ty _Count) noexcept { _Left_shift(static_cast(_Count)); return *this; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) friend constexpr _Ty& operator<<=(_Ty& _Left, const _Base128& _Right) noexcept { _Left <<= _Right._Word[0]; return _Left; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) constexpr _Base128& operator>>=(const _Ty _Count) noexcept { _Unsigned_right_shift(static_cast(_Count)); return *this; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) friend constexpr _Ty& operator>>=(_Ty& _Left, const _Base128& _Right) noexcept { _Left >>= _Right._Word[0]; return _Left; @@ -426,14 +460,18 @@ struct // _STL_INTERNAL_CHECK(_Den._Word[1] != 0); // _STL_INTERNAL_CHECK(_Num._Word[1] > _Den._Word[1]); // Normalize by shifting both left until _Den's high bit is set (So _Den's high digit is >= b / 2) +#if _HAS_CXX20 const auto __d = _STD countl_zero(_Den._Word[1]); +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv + const auto __d = _Countl_zero_fallback(_Den._Word[1]); +#endif // ^^^ !_HAS_CXX20 ^^^ _Den <<= __d; auto _High_digit = __d == 0 ? 0 : _Num._Word[1] >> (64 - __d); // This creates a third digit for _Num _Num <<= __d; _Base128 __qhat; __qhat._Word[1] = _High_digit >= _Den._Word[1]; - uint64_t __rhat; + uint64_t __rhat _ZERO_OR_NO_INIT; __qhat._Word[0] = _UDiv128(_High_digit >= _Den._Word[1] ? _High_digit - _Den._Word[1] : _High_digit, _Num._Word[1], _Den._Word[1], __rhat); @@ -458,10 +496,10 @@ struct // _STL_INTERNAL_CHECK(__qhat._Word[1] == 0); // [_High_digit | _Num] -= __qhat * _Den [Since __qhat < b, this is 3-digit - 1-digit * 2-digit] - uint64_t _Prod0_hi; + uint64_t _Prod0_hi _ZERO_OR_NO_INIT; uint64_t _Prod_lo = _UMul128(__qhat._Word[0], _Den._Word[0], _Prod0_hi); auto _Borrow = _SubBorrow64(0, _Num._Word[0], _Prod_lo, _Num._Word[0]); - uint64_t _Prod1_hi; + uint64_t _Prod1_hi _ZERO_OR_NO_INIT; _Prod_lo = _UMul128(__qhat._Word[0], _Den._Word[1], _Prod1_hi); _Prod1_hi += _AddCarry64(0, _Prod_lo, _Prod0_hi, _Prod_lo); _Borrow = _SubBorrow64(_Borrow, _Num._Word[1], _Prod_lo, _Num._Word[1]); @@ -471,7 +509,11 @@ struct } return __qhat; #else // ^^^ 128-bit intrinsics / no such intrinsics vvv - auto __d = _STD countl_zero(_Den._Word[1]); +#if _HAS_CXX20 + auto __d = _STD countl_zero(_Den._Word[1]); +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv + auto __d = _Countl_zero_fallback(_Den._Word[1]); +#endif // ^^^ !_HAS_CXX20 ^^^ const bool _Three_word_den = __d >= 32; __d &= 31; uint32_t __u[5]{ @@ -493,7 +535,7 @@ struct __v[2] |= _Den._Word[0] >> (64 - __d); } - uint32_t __q[2]; + uint32_t __q[2] _ZERO_OR_NO_INIT; if (_Three_word_den) { // 4-digit by 3-digit base 2^32 division _Knuth_4_3_1_D(__u, 5, __v, 3, __q); @@ -516,7 +558,7 @@ struct } #endif // !_STL_128_DIV_INTRINSICS _NODISCARD static constexpr _Base128 _Modulo(const _Base128& _Num, const uint64_t _Den) noexcept { - uint64_t _Rem; + uint64_t _Rem _ZERO_OR_NO_INIT; (void) _UDiv128(_Num._Word[1] % _Den, _Num._Word[0], _Den, _Rem); return _Rem; } @@ -551,13 +593,17 @@ struct // _STL_INTERNAL_CHECK(_Den._Word[1] != 0); // _STL_INTERNAL_CHECK(_Num._Word[1] > _Den._Word[1]); // Normalize by shifting both left until _Den's high bit is set (So _Den's high digit is >= b / 2) +#if _HAS_CXX20 const auto __d = _STD countl_zero(_Den._Word[1]); +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv + const auto __d = _Countl_zero_fallback(_Den._Word[1]); +#endif // ^^^ !_HAS_CXX20 ^^^ _Den <<= __d; auto _High_digit = __d == 0 ? 0 : _Num._Word[1] >> (64 - __d); // This creates a third digit for _Num _Num <<= __d; uint64_t __qhat_high = _High_digit >= _Den._Word[1]; - uint64_t __rhat; + uint64_t __rhat _ZERO_OR_NO_INIT; uint64_t __qhat = _UDiv128(_High_digit >= _Den._Word[1] ? _High_digit - _Den._Word[1] : _High_digit, _Num._Word[1], _Den._Word[1], __rhat); @@ -585,10 +631,10 @@ struct // _STL_INTERNAL_CHECK(__qhat_high == 0); // [_High_digit | _Num] -= __qhat * _Den [3-digit - 1-digit * 2-digit] - uint64_t _Prod0_hi; + uint64_t _Prod0_hi _ZERO_OR_NO_INIT; uint64_t _Prod_lo = _UMul128(__qhat, _Den._Word[0], _Prod0_hi); auto _Borrow = _SubBorrow64(0, _Num._Word[0], _Prod_lo, _Num._Word[0]); - uint64_t _Prod1_hi; + uint64_t _Prod1_hi _ZERO_OR_NO_INIT; _Prod_lo = _UMul128(__qhat, _Den._Word[1], _Prod1_hi); _Prod1_hi += _AddCarry64(0, _Prod_lo, _Prod0_hi, _Prod_lo); _Borrow = _SubBorrow64(_Borrow, _Num._Word[1], _Prod_lo, _Num._Word[1]); @@ -598,7 +644,11 @@ struct (void) _AddCarry64(_Carry, _Num._Word[1], _Den._Word[1], _Num._Word[1]); } #else // ^^^ 128-bit intrinsics / no such intrinsics vvv - auto __d = _STD countl_zero(_Den._Word[1]); +#if _HAS_CXX20 + auto __d = _STD countl_zero(_Den._Word[1]); +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv + auto __d = _Countl_zero_fallback(_Den._Word[1]); +#endif // ^^^ !_HAS_CXX20 ^^^ const bool _Three_word_den = __d >= 32; __d &= 31; uint32_t __u[5]{ @@ -620,7 +670,7 @@ struct __v[2] |= _Den._Word[0] >> (64 - __d); } - uint32_t __q[2]; + uint32_t __q[2] _ZERO_OR_NO_INIT; if (_Three_word_den) { // 4-digit by 3-digit base 2^32 division _Knuth_4_3_1_D(__u, 5, __v, 3, __q); @@ -638,19 +688,19 @@ struct return _Num; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) friend constexpr _Ty& operator&=(_Ty& _Left, const _Base128& _Right) noexcept { _Left &= _Right._Word[0]; return _Left; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) friend constexpr _Ty& operator^=(_Ty& _Left, const _Base128& _Right) noexcept { _Left ^= _Right._Word[0]; return _Left; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) friend constexpr _Ty& operator|=(_Ty& _Left, const _Base128& _Right) noexcept { _Left |= _Right._Word[0]; return _Left; @@ -663,6 +713,10 @@ struct _Unsigned128 : _Base128 { using _Signed_type = _Signed128; using _Unsigned_type = _Unsigned128; +#if !_HAS_CXX17 + constexpr _Unsigned128() noexcept : _Base128{} {} +#endif // !_HAS_CXX17 + using _Base128::_Base128; constexpr explicit _Unsigned128(const _Base128& _That) noexcept : _Base128{_That} {} @@ -671,6 +725,7 @@ struct _Unsigned128 : _Base128 { return *this; } +#if _HAS_CXX20 _NODISCARD_FRIEND constexpr strong_ordering operator<=>( const _Unsigned128& _Left, const _Unsigned128& _Right) noexcept { strong_ordering _Ord = _Left._Word[1] <=> _Right._Word[1]; @@ -679,6 +734,31 @@ struct _Unsigned128 : _Base128 { } return _Ord; } +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv + _NODISCARD_FRIEND constexpr bool operator<(const _Unsigned128& _Left, const _Unsigned128& _Right) noexcept { + if (_Left._Word[1] < _Right._Word[1]) { + return true; + } + + if (_Right._Word[1] < _Left._Word[1]) { + return false; + } + + return _Left._Word[0] < _Right._Word[0]; + } + + _NODISCARD_FRIEND constexpr bool operator>(const _Unsigned128& _Left, const _Unsigned128& _Right) noexcept { + return _Right < _Left; + } + + _NODISCARD_FRIEND constexpr bool operator<=(const _Unsigned128& _Left, const _Unsigned128& _Right) noexcept { + return !(_Right < _Left); + } + + _NODISCARD_FRIEND constexpr bool operator>=(const _Unsigned128& _Left, const _Unsigned128& _Right) noexcept { + return !(_Left < _Right); + } +#endif // ^^^ !_HAS_CXX20 ^^^ _NODISCARD_FRIEND constexpr _Unsigned128 operator<<(const _Unsigned128& _Left, const _Base128& _Right) noexcept { auto _Tmp{_Left}; @@ -686,7 +766,7 @@ struct _Unsigned128 : _Base128 { return _Tmp; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) constexpr _Unsigned128& operator<<=(const _Ty _Count) noexcept { _Left_shift(static_cast(_Count)); return *this; @@ -702,7 +782,7 @@ struct _Unsigned128 : _Base128 { return _Tmp; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) constexpr _Unsigned128& operator>>=(const _Ty _Count) noexcept { _Unsigned_right_shift(static_cast(_Count)); return *this; @@ -760,7 +840,7 @@ struct _Unsigned128 : _Base128 { _AddCarry64(_Carry, _Word[1], _That._Word[1], _Word[1]); return *this; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) friend constexpr _Ty& operator+=(_Ty& _Left, const _Unsigned128& _Right) noexcept { _Left += _Right._Word[0]; return _Left; @@ -778,7 +858,7 @@ struct _Unsigned128 : _Base128 { _SubBorrow64(_Borrow, _Word[1], _That._Word[1], _Word[1]); return *this; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) friend constexpr _Ty& operator-=(_Ty& _Left, const _Unsigned128& _Right) noexcept { _Left -= _Right._Word[0]; return _Left; @@ -792,13 +872,13 @@ struct _Unsigned128 : _Base128 { *this = *this * _That; return *this; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) friend constexpr _Ty& operator*=(_Ty& _Left, const _Unsigned128& _Right) noexcept { _Left *= _Right._Word[0]; return _Left; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) _NODISCARD_FRIEND constexpr _Unsigned128 operator/(const _Unsigned128& _Num, const _Ty _Den) noexcept { #if !_STL_128_DIV_INTRINSICS if constexpr (sizeof(_Ty) <= 4) { @@ -813,7 +893,7 @@ struct _Unsigned128 : _Base128 { return _Unsigned128{_Base128::_Divide(_Num, _Den)}; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) constexpr _Unsigned128& operator/=(const _Ty _That) noexcept { #if !_STL_128_DIV_INTRINSICS if constexpr (sizeof(_Ty) <= 4) { @@ -829,7 +909,7 @@ struct _Unsigned128 : _Base128 { *this = _Unsigned128{_Base128::_Divide(*this, _That)}; return *this; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) friend constexpr _Ty& operator/=(_Ty& _Left, const _Unsigned128& _Right) noexcept { if (_Right._Word[1] != 0) { _Left = 0; @@ -839,7 +919,7 @@ struct _Unsigned128 : _Base128 { return _Left; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) _NODISCARD_FRIEND constexpr _Unsigned128 operator%(const _Base128& _Num, const _Ty _Den) noexcept { #if !_STL_128_DIV_INTRINSICS if constexpr (sizeof(_Ty) <= 4) { @@ -854,7 +934,7 @@ struct _Unsigned128 : _Base128 { return _Unsigned128{_Base128::_Modulo(_Num, _Den)}; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) constexpr _Unsigned128& operator%=(const _Ty _Den) noexcept { *this = *this % _Den; return *this; @@ -863,7 +943,7 @@ struct _Unsigned128 : _Base128 { *this = *this % _Den; return *this; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) friend constexpr _Ty& operator%=(_Ty& _Left, const _Unsigned128& _Right) noexcept { if (_Right._Word[1] == 0) { _Left %= _Right._Word[0]; @@ -946,6 +1026,7 @@ class numeric_limits<_Unsigned128> : public _Num_int_base { static constexpr int digits10 = 38; }; +#ifdef __cpp_lib_concepts template struct common_type<_Ty, _Unsigned128> { using type = _Unsigned128; @@ -954,11 +1035,21 @@ template struct common_type<_Unsigned128, _Ty> { using type = _Unsigned128; }; +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv +template +struct common_type<_Ty, _Unsigned128> : enable_if, _Unsigned128> {}; +template +struct common_type<_Unsigned128, _Ty> : enable_if, _Unsigned128> {}; +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ struct _Signed128 : _Base128 { using _Signed_type = _Signed128; using _Unsigned_type = _Unsigned128; +#if !_HAS_CXX17 + constexpr _Signed128() noexcept : _Base128{} {} +#endif // !_HAS_CXX17 + using _Base128::_Base128; constexpr explicit _Signed128(const _Base128& _That) noexcept : _Base128{_That} {} @@ -967,6 +1058,7 @@ struct _Signed128 : _Base128 { return *this; } +#if _HAS_CXX20 _NODISCARD_FRIEND constexpr strong_ordering operator<=>( const _Signed128& _Left, const _Signed128& _Right) noexcept { strong_ordering _Ord = static_cast(_Left._Word[1]) <=> static_cast(_Right._Word[1]); @@ -975,6 +1067,31 @@ struct _Signed128 : _Base128 { } return _Ord; } +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv + _NODISCARD_FRIEND constexpr bool operator<(const _Signed128& _Left, const _Signed128& _Right) noexcept { + if (static_cast(_Left._Word[1]) < static_cast(_Right._Word[1])) { + return true; + } + + if (static_cast(_Right._Word[1]) < static_cast(_Left._Word[1])) { + return false; + } + + return _Left._Word[0] < _Right._Word[0]; + } + + _NODISCARD_FRIEND constexpr bool operator>(const _Signed128& _Left, const _Signed128& _Right) noexcept { + return _Right < _Left; + } + + _NODISCARD_FRIEND constexpr bool operator<=(const _Signed128& _Left, const _Signed128& _Right) noexcept { + return !(_Right < _Left); + } + + _NODISCARD_FRIEND constexpr bool operator>=(const _Signed128& _Left, const _Signed128& _Right) noexcept { + return !(_Left < _Right); + } +#endif // ^^^ !_HAS_CXX20 ^^^ _NODISCARD_FRIEND constexpr _Signed128 operator<<(const _Signed128& _Left, const _Base128& _Right) noexcept { auto _Tmp{_Left}; @@ -982,7 +1099,7 @@ struct _Signed128 : _Base128 { return _Tmp; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) constexpr _Signed128& operator<<=(const _Ty _Count) noexcept { _Left_shift(static_cast(_Count)); return *this; @@ -1004,7 +1121,7 @@ struct _Signed128 : _Base128 { } #if _STL_128_INTRINSICS - if (!_STD is_constant_evaluated()) { + if (!_Is_constant_evaluated()) { _Word[0] = __shiftright128(_Word[0], _Word[1], _Count); } else #endif // _STL_128_INTRINSICS @@ -1021,7 +1138,7 @@ struct _Signed128 : _Base128 { return _Tmp; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) constexpr _Signed128& operator>>=(const _Ty _Count) noexcept { _Signed_right_shift(static_cast(_Count)); return *this; @@ -1079,7 +1196,7 @@ struct _Signed128 : _Base128 { _AddCarry64(_Carry, _Word[1], _That._Word[1], _Word[1]); return *this; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) friend constexpr _Ty& operator+=(_Ty& _Left, const _Signed128& _Right) noexcept { _Left = static_cast<_Ty>(_Signed128{_Left} + _Right); return _Left; @@ -1097,7 +1214,7 @@ struct _Signed128 : _Base128 { _SubBorrow64(_Borrow, _Word[1], _That._Word[1], _Word[1]); return *this; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) friend constexpr _Ty& operator-=(_Ty& _Left, const _Signed128& _Right) noexcept { _Left = static_cast<_Ty>(_Signed128{_Left} - _Right); return _Left; @@ -1121,7 +1238,7 @@ struct _Signed128 : _Base128 { return _Result; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) constexpr _Signed128& operator*=(const _Ty _That) noexcept { *this = *this * _That; return *this; @@ -1134,13 +1251,13 @@ struct _Signed128 : _Base128 { *this = _Signed128{static_cast(*this) * _That}; return *this; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) friend constexpr _Ty& operator*=(_Ty& _Left, const _Signed128& _Right) noexcept { _Left = static_cast<_Ty>(_Signed128{_Left} * _Right); return _Left; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) _NODISCARD_FRIEND constexpr _Signed128 operator/(_Signed128 _Num, _Ty _Den) noexcept { bool _Negative = false; _Num._Strip_negative(_Negative); @@ -1177,7 +1294,7 @@ struct _Signed128 : _Base128 { return _Result; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) constexpr _Signed128& operator/=(const _Ty _That) noexcept { *this = *this / _That; return *this; @@ -1190,7 +1307,7 @@ struct _Signed128 : _Base128 { *this = _Signed128{static_cast<_Base128&>(*this) / _That}; return *this; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) friend constexpr _Ty& operator/=(_Ty& _Left, const _Signed128& _Right) noexcept { _Left = static_cast<_Ty>(_Signed128{_Left} / _Right); return _Left; @@ -1212,7 +1329,12 @@ struct _Signed128 : _Base128 { return _Signed128{_Result}; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) + _NODISCARD_FRIEND constexpr _Signed128 operator%(_Signed128 _Left, const _Ty _Right) noexcept { + return _Left % _Signed128{_Right}; + } + + _TEMPLATE_CLASS_INTEGRAL(_Ty) constexpr _Signed128& operator%=(const _Ty _That) noexcept { *this = *this % _That; return *this; @@ -1225,7 +1347,7 @@ struct _Signed128 : _Base128 { *this = static_cast(*this) % _That; return *this; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) friend constexpr _Ty& operator%=(_Ty& _Left, const _Signed128& _Right) noexcept { _Left = static_cast<_Ty>(_Signed128{_Left} % _Right); return _Left; @@ -1305,6 +1427,7 @@ class numeric_limits<_Signed128> : public _Num_int_base { static constexpr int digits10 = 38; }; +#ifdef __cpp_lib_concepts template struct common_type<_Ty, _Signed128> { using type = _Signed128; @@ -1313,6 +1436,12 @@ template struct common_type<_Signed128, _Ty> { using type = _Signed128; }; +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv +template +struct common_type<_Ty, _Signed128> : enable_if, _Signed128> {}; +template +struct common_type<_Signed128, _Ty> : enable_if, _Signed128> {}; +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ template <> struct common_type<_Signed128, _Unsigned128> { @@ -1323,15 +1452,120 @@ struct common_type<_Unsigned128, _Signed128> { using type = _Unsigned128; }; +inline namespace literals { + inline namespace _Int128_literals { + namespace _Int128_detail { + enum class _U128_parse_status : unsigned char { + _Valid, + _Overflow, + _Invalid, + }; + + struct _U128_parse_result { + _U128_parse_status _Status_code; + _Unsigned128 _Value; + }; + + _NODISCARD _CONSTEVAL unsigned int _Char_to_digit(const char _Ch) noexcept { + if (_Ch >= '0' && _Ch <= '9') { + return static_cast(_Ch - '0'); + } + + if (_Ch >= 'A' && _Ch <= 'F') { + return static_cast(_Ch - 'A' + 10); + } + + if (_Ch >= 'a' && _Ch <= 'f') { + return static_cast(_Ch - 'a' + 10); + } + + return static_cast(-1); + } + + template + struct _Parse_u128_impl { + _NODISCARD static _CONSTEVAL _U128_parse_result _Parse() noexcept { + if constexpr (sizeof...(_Chars) == 0) { + return {_U128_parse_status::_Valid, 0}; + } else { + constexpr char _Char_seq[]{_Chars...}; + constexpr auto _U128_max = (numeric_limits<_Unsigned128>::max)(); + + _Unsigned128 _Val{}; + for (const char _Ch : _Char_seq) { + if (_Ch == '\'') { + continue; + } + + const unsigned int _Digit = _Char_to_digit(_Ch); + if (_Digit == static_cast(-1)) { + return {_U128_parse_status::_Invalid, _Unsigned128{}}; + } + + if (_Val > _U128_max / _Base || _Base * _Val > _U128_max - _Digit) { + return {_U128_parse_status::_Overflow, _Unsigned128{}}; + } + + _Val = _Base * _Val + _Digit; + } + return {_U128_parse_status::_Valid, _Val}; + } + } + }; + + template + struct _Parse_u128 : _Parse_u128_impl<10, _Chars...> {}; + + template + struct _Parse_u128<'0', 'X', _Chars...> : _Parse_u128_impl<16, _Chars...> {}; + + template + struct _Parse_u128<'0', 'x', _Chars...> : _Parse_u128_impl<16, _Chars...> {}; + + template + struct _Parse_u128<'0', 'B', _Chars...> : _Parse_u128_impl<2, _Chars...> {}; + + template + struct _Parse_u128<'0', 'b', _Chars...> : _Parse_u128_impl<2, _Chars...> {}; + + template + struct _Parse_u128<'0', _Chars...> : _Parse_u128_impl<8, _Chars...> {}; + } // namespace _Int128_detail + + template + _NODISCARD _CONSTEVAL _Unsigned128 operator"" __u128() noexcept { + constexpr auto _Parsed_result = _Int128_detail::_Parse_u128<_Chars...>::_Parse(); + static_assert(_Parsed_result._Status_code != _Int128_detail::_U128_parse_status::_Invalid, + "Invalid characters in the integer literal"); + static_assert(_Parsed_result._Status_code != _Int128_detail::_U128_parse_status::_Overflow, + "The integer literal is too large for an unsigned 128-bit number"); + return _Parsed_result._Value; + } + + template + _NODISCARD _CONSTEVAL _Signed128 operator"" __i128() noexcept { + constexpr auto _Parsed_result = _Int128_detail::_Parse_u128<_Chars...>::_Parse(); + static_assert(_Parsed_result._Status_code != _Int128_detail::_U128_parse_status::_Invalid, + "Invalid characters in the integer literal"); + static_assert(_Parsed_result._Status_code != _Int128_detail::_U128_parse_status::_Overflow + && _Parsed_result._Value._Word[1] < (static_cast(1) << 63), + "The integer literal is too large for a signed 128-bit number"); + return static_cast<_Signed128>(_Parsed_result._Value); + } + } // namespace _Int128_literals +} // namespace literals + #undef _STL_128_INTRINSICS #undef _STL_128_DIV_INTRINSICS _STD_END +#undef _TEMPLATE_CLASS_INTEGRAL +#undef _ZERO_OR_NO_INIT + #pragma pop_macro("new") _STL_RESTORE_CLANG_WARNINGS #pragma warning(pop) #pragma pack(pop) -#endif // __cpp_lib_concepts #endif // _STL_COMPILER_PREPROCESSOR #endif // __MSVC_INT128_HPP diff --git a/tests/std/tests/P1522R1_difference_type/env.lst b/tests/std/tests/P1522R1_difference_type/env.lst index 7b6bcff4830..19f025bd0e6 100644 --- a/tests/std/tests/P1522R1_difference_type/env.lst +++ b/tests/std/tests/P1522R1_difference_type/env.lst @@ -1,4 +1,4 @@ # Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -RUNALL_INCLUDE ..\strict_concepts_20_matrix.lst +RUNALL_INCLUDE ..\usual_matrix.lst diff --git a/tests/std/tests/P1522R1_difference_type/test.cpp b/tests/std/tests/P1522R1_difference_type/test.cpp index 0a2cc61c943..3a0cb740e2e 100644 --- a/tests/std/tests/P1522R1_difference_type/test.cpp +++ b/tests/std/tests/P1522R1_difference_type/test.cpp @@ -3,110 +3,149 @@ #include <__msvc_int128.hpp> #include -#include -#include #include #include #include #include +#if _HAS_CXX20 +#include + +namespace ordtest { + using std::strong_ordering; +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv +namespace ordtest { + enum class strong_ordering : signed char { + less = -1, + equal = 0, + equivalent = 0, + greater = 1, + }; +} +#endif // ^^^ !_HAS_CXX20 ^^^ + +#ifdef __cpp_lib_concepts // TRANSITION, GH-395 +#include + +#define SAME_AS std::same_as +#else // ^^^ has concepts / has no concepts vvv +#define SAME_AS std::is_same_v +#endif // ^^^ has no concepts ^^^ + +#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) + using std::_Signed128; using std::_Unsigned128; +using namespace std::literals::_Int128_literals; -constexpr void check_equal(const auto& x) { +template +constexpr void check_equal(const I& x) { assert(x == x); assert(!(x != x)); assert(!(x < x)); assert(!(x > x)); assert(x <= x); assert(x >= x); +#if _HAS_CXX20 assert(x <=> x == 0); +#endif // _HAS_CXX20 } -constexpr void check_order(const auto& x, const auto& y, const std::strong_ordering ord) { - assert((x == y) == (ord == std::strong_ordering::equal)); - assert((x != y) == (ord != std::strong_ordering::equal)); - assert((x < y) == (ord == std::strong_ordering::less)); - assert((x > y) == (ord == std::strong_ordering::greater)); - assert((x <= y) == (ord != std::strong_ordering::greater)); - assert((x >= y) == (ord != std::strong_ordering::less)); - - assert((y == x) == (ord == std::strong_ordering::equal)); - assert((y != x) == (ord != std::strong_ordering::equal)); - assert((y < x) == (ord == std::strong_ordering::greater)); - assert((y > x) == (ord == std::strong_ordering::less)); - assert((y <= x) == (ord != std::strong_ordering::less)); - assert((y >= x) == (ord != std::strong_ordering::greater)); - +template +constexpr void check_order(const I1& x, const I2& y, const ordtest::strong_ordering ord) { + assert((x == y) == (ord == ordtest::strong_ordering::equal)); + assert((x != y) == (ord != ordtest::strong_ordering::equal)); + assert((x < y) == (ord == ordtest::strong_ordering::less)); + assert((x > y) == (ord == ordtest::strong_ordering::greater)); + assert((x <= y) == (ord != ordtest::strong_ordering::greater)); + assert((x >= y) == (ord != ordtest::strong_ordering::less)); + + assert((y == x) == (ord == ordtest::strong_ordering::equal)); + assert((y != x) == (ord != ordtest::strong_ordering::equal)); + assert((y < x) == (ord == ordtest::strong_ordering::greater)); + assert((y > x) == (ord == ordtest::strong_ordering::less)); + assert((y <= x) == (ord != ordtest::strong_ordering::less)); + assert((y >= x) == (ord != ordtest::strong_ordering::greater)); + +#if _HAS_CXX20 assert((x <=> y) == (ord <=> 0)); assert((y <=> x) == (0 <=> ord)); +#endif // _HAS_CXX20 } constexpr bool test_unsigned() { - static_assert(std::regular<_Unsigned128>); - static_assert(std::three_way_comparable<_Unsigned128, std::strong_ordering>); - - static_assert(std::numeric_limits<_Unsigned128>::min() == 0); - static_assert(std::numeric_limits<_Unsigned128>::max() == ~_Unsigned128{}); - static_assert(std::numeric_limits<_Unsigned128>::digits == 128); - static_assert(std::numeric_limits<_Unsigned128>::is_modulo); - - static_assert(std::same_as, _Unsigned128>); - static_assert(std::same_as, _Unsigned128>); - static_assert(std::same_as, _Unsigned128>); - static_assert(std::same_as, _Unsigned128>); - static_assert(std::same_as, _Unsigned128>); +#ifdef __cpp_lib_concepts // TRANSITION, GH-395 + STATIC_ASSERT(std::regular<_Unsigned128>); + STATIC_ASSERT(std::three_way_comparable<_Unsigned128, ordtest::strong_ordering>); +#endif // __cpp_lib_concepts + + STATIC_ASSERT(std::numeric_limits<_Unsigned128>::min() == 0); + STATIC_ASSERT(std::numeric_limits<_Unsigned128>::max() == ~_Unsigned128{}); + STATIC_ASSERT(std::numeric_limits<_Unsigned128>::digits == 128); + STATIC_ASSERT(std::numeric_limits<_Unsigned128>::is_modulo); + + STATIC_ASSERT(SAME_AS, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); #ifdef __cpp_char8_t - static_assert(std::same_as, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); #endif // __cpp_char8_t - static_assert(std::same_as, _Unsigned128>); - static_assert(std::same_as, _Unsigned128>); - static_assert(std::same_as, _Unsigned128>); - static_assert(std::same_as, _Unsigned128>); - static_assert(std::same_as, _Unsigned128>); - static_assert(std::same_as, _Unsigned128>); - static_assert(std::same_as, _Unsigned128>); - static_assert(std::same_as, _Unsigned128>); - static_assert(std::same_as, _Unsigned128>); - static_assert(std::same_as, _Unsigned128>); - static_assert(std::same_as, _Unsigned128>); - - static_assert(std::same_as, _Unsigned128>); - static_assert(std::same_as, _Unsigned128>); - static_assert(std::same_as, _Unsigned128>); - static_assert(std::same_as, _Unsigned128>); - static_assert(std::same_as, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); + + STATIC_ASSERT(SAME_AS, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); #ifdef __cpp_char8_t - static_assert(std::same_as, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); #endif // __cpp_char8_t - static_assert(std::same_as, _Unsigned128>); - static_assert(std::same_as, _Unsigned128>); - static_assert(std::same_as, _Unsigned128>); - static_assert(std::same_as, _Unsigned128>); - static_assert(std::same_as, _Unsigned128>); - static_assert(std::same_as, _Unsigned128>); - static_assert(std::same_as, _Unsigned128>); - static_assert(std::same_as, _Unsigned128>); - static_assert(std::same_as, _Unsigned128>); - static_assert(std::same_as, _Unsigned128>); - static_assert(std::same_as, _Unsigned128>); - - static_assert(std::_Integer_class<_Unsigned128>); - static_assert(std::_Integer_like<_Unsigned128>); - static_assert(!std::_Signed_integer_like<_Unsigned128>); - static_assert(std::same_as, _Unsigned128>); - static_assert(std::same_as, _Signed128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); + +#ifdef __cpp_lib_concepts // TRANSITION, GH-395 + STATIC_ASSERT(std::_Integer_class<_Unsigned128>); + STATIC_ASSERT(std::_Integer_like<_Unsigned128>); + STATIC_ASSERT(!std::_Signed_integer_like<_Unsigned128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Signed128>); +#endif // __cpp_lib_concepts check_equal(_Unsigned128{}); check_equal(_Unsigned128{42}); check_equal(_Unsigned128{-42}); check_equal(_Unsigned128{0x11111111'11111111, 0x22222222'22222222}); + check_equal(0x22222222'22222222'11111111'11111111__u128); - check_order(_Unsigned128{42}, _Unsigned128{-42}, std::strong_ordering::less); - check_order(_Unsigned128{0, 42}, _Unsigned128{0, 52}, std::strong_ordering::less); // ordered only by MSW - check_order(_Unsigned128{42}, _Unsigned128{52}, std::strong_ordering::less); // ordered only by LSW + check_order(_Unsigned128{42}, _Unsigned128{-42}, ordtest::strong_ordering::less); + check_order(_Unsigned128{0, 42}, _Unsigned128{0, 52}, ordtest::strong_ordering::less); // ordered only by MSW + check_order(_Unsigned128{42}, _Unsigned128{52}, ordtest::strong_ordering::less); // ordered only by LSW check_order(_Unsigned128{0x11111111'11111111, 0x22222222'22222222}, - _Unsigned128{0x01010101'01010101, 0x01010101'01010101}, std::strong_ordering::greater); + _Unsigned128{0x01010101'01010101, 0x01010101'01010101}, ordtest::strong_ordering::greater); + check_order(0x22222222'22222222'11111111'11111111__u128, 0x01010101'01010101'01010101'01010101__u128, + ordtest::strong_ordering::greater); { _Unsigned128 u{42}; @@ -169,6 +208,7 @@ constexpr bool test_unsigned() { product = _Unsigned128{0x01010101'01010101, 0x01010101'01010101} * 5; assert(product._Word[0] == 0x05050505'05050505); assert(product._Word[1] == 0x05050505'05050505); + assert(product == 0x05050505'05050505'05050505'05050505__u128); product = 5 * _Unsigned128{0x01010101'01010101, 0x01010101'01010101}; assert(product._Word[0] == 0x05050505'05050505); @@ -178,6 +218,7 @@ constexpr bool test_unsigned() { product *= product; assert(product._Word[0] == 0x08070605'04030201); assert(product._Word[1] == 0x100f0e0d'0c0b0a09); + assert(product == 0x100f0e0d'0c0b0a09'08070605'04030201__u128); assert(+product == product); assert(-product + product == 0); @@ -185,18 +226,21 @@ constexpr bool test_unsigned() { * _Unsigned128{0x000f0e0d'0c0b0a09, 0x08070605'04030201}; assert(product._Word[0] == 0x6dc18e55'16d48f48); assert(product._Word[1] == 0xdc1b6bce'43cd6c21); + assert(product == 0xdc1b6bce'43cd6c21'6dc18e55'16d48f48__u128); assert(+product == product); assert(-product + product == 0); product <<= 11; assert(product._Word[0] == 0x0c72a8b6'a47a4000); assert(product._Word[1] == 0xdb5e721e'6b610b6e); + assert(product == 0xdb5e721e'6b610b6e'0c72a8b6'a47a4000__u128); assert(+product == product); assert(-product + product == 0); product >>= 17; assert(product._Word[0] == 0x85b70639'545b523d); assert(product._Word[1] == 0x00006daf'390f35b0); + assert(product == 0x00006daf'390f35b0'85b70639'545b523d__u128); assert(+product == product); assert(-product + product == 0); } @@ -213,6 +257,7 @@ constexpr bool test_unsigned() { q = _Unsigned128{0x01010101'01010101, 0x01010101'01010101} / _Unsigned128{13}; assert(q._Word[0] == 0xc50013c5'0013c500); assert(q._Word[1] == 0x0013c500'13c50013); + assert(q == 0x0013c500'13c50013'c50013c5'0013c500__u128); q = _Unsigned128{0x22222222'22222222, 0x22222222'22222222} / _Unsigned128{0x11111111'11111111, 0x11111111'11111111}; @@ -262,16 +307,19 @@ constexpr bool test_unsigned() { const _Unsigned128 x{0x01020304'02030405, 0x03040506'04050607}; const _Unsigned128 y{0x07060504'06050403, 0x05040302'04030101}; assert(((x & y) == _Unsigned128{0x01020104'02010401, 0x01040102'04010001})); + assert(((x & y) == 0x01040102'04010001'01020104'02010401__u128)); auto tmp = x; tmp &= y; assert(tmp == (x & y)); assert(((x | y) == _Unsigned128{0x07060704'06070407, 0x07040706'04070707})); + assert(((x | y) == 0x07040706'04070707'07060704'06070407__u128)); tmp = x; tmp |= y; assert(tmp == (x | y)); assert(((x ^ y) == _Unsigned128{0x06040600'04060006, 0x06000604'00060706})); + assert(((x ^ y) == 0x06000604'00060706'06040600'04060006__u128)); tmp = x; tmp ^= y; assert(tmp == (x ^ y)); @@ -308,90 +356,134 @@ constexpr bool test_unsigned() { assert(x._Word[0] == ~0ull); assert(x._Word[1] == static_cast(std::numeric_limits::max())); } + { + STATIC_ASSERT(noexcept(0__u128)); + STATIC_ASSERT(noexcept(42__u128)); + STATIC_ASSERT(noexcept(4'2__u128)); + STATIC_ASSERT(noexcept(052__u128)); + STATIC_ASSERT(noexcept(05'2__u128)); + STATIC_ASSERT(noexcept(0x2a__u128)); + STATIC_ASSERT(noexcept(0X2a__u128)); + STATIC_ASSERT(noexcept(0x2A__u128)); + STATIC_ASSERT(noexcept(0X2A__u128)); + STATIC_ASSERT(noexcept(0x2'A__u128)); + STATIC_ASSERT(noexcept(0b101010__u128)); + STATIC_ASSERT(noexcept(0b1'0101'0__u128)); + STATIC_ASSERT(noexcept(0B101010__u128)); + STATIC_ASSERT(noexcept(0B1'0101'0__u128)); + STATIC_ASSERT(noexcept(0xABCDEF__u128)); + STATIC_ASSERT(noexcept(340282366920938463463374607431768211455__u128)); + STATIC_ASSERT(noexcept(0xffffffff'FFFFFFFF'ffffFFFF'FFFFffff__u128)); + + STATIC_ASSERT(42__u128 == 42); + STATIC_ASSERT(4'2__u128 == 42); + STATIC_ASSERT(42__u128 == 052__u128); + STATIC_ASSERT(4'2__u128 == 052__u128); + STATIC_ASSERT(42__u128 == 0x2a__u128); + STATIC_ASSERT(4'2__u128 == 0X2a__u128); + STATIC_ASSERT(42__u128 == 0b101010__u128); + STATIC_ASSERT(4'2__u128 == 0b101010__u128); + STATIC_ASSERT(42__u128 == 0B101010__u128); + STATIC_ASSERT(4'2__u128 == 0B101010__u128); + STATIC_ASSERT(11259375__u128 == 0xABCDEF__u128); + STATIC_ASSERT( + 340'2823'6692'0938'4634'6337'4607'4317'6821'1455__u128 == 0xffffffff'FFFFFFFF'ffffFFFF'FFFFffff__u128); + + STATIC_ASSERT( + 340'282'366'920'938'463'463'374'607'431'768'211'455__u128 == std::numeric_limits<_Unsigned128>::max()); + STATIC_ASSERT(0xffffffff'ffffffff'ffffffff'ffffffff__u128 == std::numeric_limits<_Unsigned128>::max()); + } return true; } constexpr bool test_signed() { - static_assert(std::regular<_Signed128>); - static_assert(std::three_way_comparable<_Signed128, std::strong_ordering>); - - static_assert(std::numeric_limits<_Signed128>::min() == _Signed128{0, 1ull << 63}); - static_assert(std::numeric_limits<_Signed128>::max() == _Signed128{~0ull, ~0ull >> 1}); - static_assert(std::numeric_limits<_Signed128>::digits == 127); - static_assert(!std::numeric_limits<_Signed128>::is_modulo); - - static_assert(std::same_as, _Signed128>); - static_assert(std::same_as, _Signed128>); - static_assert(std::same_as, _Signed128>); - static_assert(std::same_as, _Signed128>); - static_assert(std::same_as, _Signed128>); +#ifdef __cpp_lib_concepts // TRANSITION, GH-395 + STATIC_ASSERT(std::regular<_Signed128>); + STATIC_ASSERT(std::three_way_comparable<_Signed128, ordtest::strong_ordering>); +#endif // __cpp_lib_concepts + + STATIC_ASSERT(std::numeric_limits<_Signed128>::min() == _Signed128{0, 1ull << 63}); + STATIC_ASSERT(std::numeric_limits<_Signed128>::max() == _Signed128{~0ull, ~0ull >> 1}); + STATIC_ASSERT(std::numeric_limits<_Signed128>::digits == 127); + STATIC_ASSERT(!std::numeric_limits<_Signed128>::is_modulo); + + STATIC_ASSERT(SAME_AS, _Signed128>); + STATIC_ASSERT(SAME_AS, _Signed128>); + STATIC_ASSERT(SAME_AS, _Signed128>); + STATIC_ASSERT(SAME_AS, _Signed128>); + STATIC_ASSERT(SAME_AS, _Signed128>); #ifdef __cpp_char8_t - static_assert(std::same_as, _Signed128>); + STATIC_ASSERT(SAME_AS, _Signed128>); #endif // __cpp_char8_t - static_assert(std::same_as, _Signed128>); - static_assert(std::same_as, _Signed128>); - static_assert(std::same_as, _Signed128>); - static_assert(std::same_as, _Signed128>); - static_assert(std::same_as, _Signed128>); - static_assert(std::same_as, _Signed128>); - static_assert(std::same_as, _Signed128>); - static_assert(std::same_as, _Signed128>); - static_assert(std::same_as, _Signed128>); - static_assert(std::same_as, _Signed128>); - - static_assert(std::same_as, _Signed128>); - static_assert(std::same_as, _Signed128>); - static_assert(std::same_as, _Signed128>); - static_assert(std::same_as, _Signed128>); - static_assert(std::same_as, _Signed128>); + STATIC_ASSERT(SAME_AS, _Signed128>); + STATIC_ASSERT(SAME_AS, _Signed128>); + STATIC_ASSERT(SAME_AS, _Signed128>); + STATIC_ASSERT(SAME_AS, _Signed128>); + STATIC_ASSERT(SAME_AS, _Signed128>); + STATIC_ASSERT(SAME_AS, _Signed128>); + STATIC_ASSERT(SAME_AS, _Signed128>); + STATIC_ASSERT(SAME_AS, _Signed128>); + STATIC_ASSERT(SAME_AS, _Signed128>); + STATIC_ASSERT(SAME_AS, _Signed128>); + + STATIC_ASSERT(SAME_AS, _Signed128>); + STATIC_ASSERT(SAME_AS, _Signed128>); + STATIC_ASSERT(SAME_AS, _Signed128>); + STATIC_ASSERT(SAME_AS, _Signed128>); + STATIC_ASSERT(SAME_AS, _Signed128>); #ifdef __cpp_char8_t - static_assert(std::same_as, _Signed128>); + STATIC_ASSERT(SAME_AS, _Signed128>); #endif // __cpp_char8_t - static_assert(std::same_as, _Signed128>); - static_assert(std::same_as, _Signed128>); - static_assert(std::same_as, _Signed128>); - static_assert(std::same_as, _Signed128>); - static_assert(std::same_as, _Signed128>); - static_assert(std::same_as, _Signed128>); - static_assert(std::same_as, _Signed128>); - static_assert(std::same_as, _Signed128>); - static_assert(std::same_as, _Signed128>); - static_assert(std::same_as, _Signed128>); - - static_assert(std::_Integer_class<_Signed128>); - static_assert(std::_Integer_like<_Signed128>); - static_assert(std::_Signed_integer_like<_Signed128>); - static_assert(std::same_as, _Unsigned128>); - static_assert(std::same_as, _Signed128>); + STATIC_ASSERT(SAME_AS, _Signed128>); + STATIC_ASSERT(SAME_AS, _Signed128>); + STATIC_ASSERT(SAME_AS, _Signed128>); + STATIC_ASSERT(SAME_AS, _Signed128>); + STATIC_ASSERT(SAME_AS, _Signed128>); + STATIC_ASSERT(SAME_AS, _Signed128>); + STATIC_ASSERT(SAME_AS, _Signed128>); + STATIC_ASSERT(SAME_AS, _Signed128>); + STATIC_ASSERT(SAME_AS, _Signed128>); + STATIC_ASSERT(SAME_AS, _Signed128>); + +#ifdef __cpp_lib_concepts // TRANSITION, GH-395 + STATIC_ASSERT(std::_Integer_class<_Signed128>); + STATIC_ASSERT(std::_Integer_like<_Signed128>); + STATIC_ASSERT(std::_Signed_integer_like<_Signed128>); + STATIC_ASSERT(SAME_AS, _Unsigned128>); + STATIC_ASSERT(SAME_AS, _Signed128>); +#endif // __cpp_lib_concepts check_equal(_Signed128{}); check_equal(_Signed128{42}); check_equal(_Signed128{-42}); check_equal(_Signed128{0x11111111'11111111, 0x22222222'22222222}); + check_equal(0x22222222'22222222'11111111'11111111__i128); - check_order(_Signed128{42}, _Signed128{-42}, std::strong_ordering::greater); + check_order(_Signed128{42}, _Signed128{-42}, ordtest::strong_ordering::greater); check_order(_Signed128{0x11111111'11111111, 0x22222222'22222222}, - _Signed128{0x01010101'01010101, 0x01010101'01010101}, std::strong_ordering::greater); - check_order(_Signed128{~0ull, ~0ull}, _Signed128{-1}, std::strong_ordering::equal); + _Signed128{0x01010101'01010101, 0x01010101'01010101}, ordtest::strong_ordering::greater); + check_order(0x22222222'22222222'11111111'11111111__i128, 0x01010101'01010101'01010101'01010101__i128, + ordtest::strong_ordering::greater); + check_order(_Signed128{~0ull, ~0ull}, _Signed128{-1}, ordtest::strong_ordering::equal); - check_order(_Signed128{-2}, _Signed128{-1}, std::strong_ordering::less); - check_order(_Signed128{-2}, _Signed128{1}, std::strong_ordering::less); - check_order(_Signed128{2}, _Signed128{-1}, std::strong_ordering::greater); - check_order(_Signed128{2}, _Signed128{1}, std::strong_ordering::greater); + check_order(_Signed128{-2}, _Signed128{-1}, ordtest::strong_ordering::less); + check_order(_Signed128{-2}, _Signed128{1}, ordtest::strong_ordering::less); + check_order(_Signed128{2}, _Signed128{-1}, ordtest::strong_ordering::greater); + check_order(_Signed128{2}, _Signed128{1}, ordtest::strong_ordering::greater); check_equal(_Signed128{0, 0}); - check_order(_Signed128{0, (1ull << 63)}, _Signed128{0, 0}, std::strong_ordering::less); - check_order(_Signed128{0, (1ull << 63)}, _Signed128{0, (1ull << 63)}, std::strong_ordering::equal); - check_order(_Signed128{0, 0}, _Signed128{1, 0}, std::strong_ordering::less); - check_order(_Signed128{0, (1ull << 63)}, _Signed128{1, 0}, std::strong_ordering::less); - check_order(_Signed128{0, (1ull << 63)}, _Signed128{1, (1ull << 63)}, std::strong_ordering::less); - check_order(_Signed128{0, 0}, _Signed128{0, 1}, std::strong_ordering::less); - check_order(_Signed128{0, (1ull << 63)}, _Signed128{0, 1}, std::strong_ordering::less); - check_order(_Signed128{0, (1ull << 63)}, _Signed128{0, 1 | (1ull << 63)}, std::strong_ordering::less); - check_order(_Signed128{0, 0}, _Signed128{1, 1}, std::strong_ordering::less); - check_order(_Signed128{0, (1ull << 63)}, _Signed128{1, 1}, std::strong_ordering::less); - check_order(_Signed128{0, (1ull << 63)}, _Signed128{1, 1 | (1ull << 63)}, std::strong_ordering::less); + check_order(_Signed128{0, (1ull << 63)}, _Signed128{0, 0}, ordtest::strong_ordering::less); + check_order(_Signed128{0, (1ull << 63)}, _Signed128{0, (1ull << 63)}, ordtest::strong_ordering::equal); + check_order(_Signed128{0, 0}, _Signed128{1, 0}, ordtest::strong_ordering::less); + check_order(_Signed128{0, (1ull << 63)}, _Signed128{1, 0}, ordtest::strong_ordering::less); + check_order(_Signed128{0, (1ull << 63)}, _Signed128{1, (1ull << 63)}, ordtest::strong_ordering::less); + check_order(_Signed128{0, 0}, _Signed128{0, 1}, ordtest::strong_ordering::less); + check_order(_Signed128{0, (1ull << 63)}, _Signed128{0, 1}, ordtest::strong_ordering::less); + check_order(_Signed128{0, (1ull << 63)}, _Signed128{0, 1 | (1ull << 63)}, ordtest::strong_ordering::less); + check_order(_Signed128{0, 0}, _Signed128{1, 1}, ordtest::strong_ordering::less); + check_order(_Signed128{0, (1ull << 63)}, _Signed128{1, 1}, ordtest::strong_ordering::less); + check_order(_Signed128{0, (1ull << 63)}, _Signed128{1, 1 | (1ull << 63)}, ordtest::strong_ordering::less); assert((_Signed128{-2} == _Unsigned128{0ull - 2, ~0ull})); assert((_Unsigned128{_Signed128{-2}} == _Unsigned128{0ull - 2, ~0ull})); @@ -497,16 +589,19 @@ constexpr bool test_signed() { } { auto product = _Signed128{0x01010101'01010101, 0x01010101'01010101} * 5; + assert(product == 0x05050505'05050505'05050505'05050505__i128); assert(product._Word[0] == 0x05050505'05050505); assert(product._Word[1] == 0x05050505'05050505); product = 5 * _Signed128{0x01010101'01010101, 0x01010101'01010101}; + assert(product == 0x05050505'05050505'05050505'05050505__i128); assert(product._Word[0] == 0x05050505'05050505); assert(product._Word[1] == 0x05050505'05050505); } { auto product = _Signed128{0x01010101'01010101, 0x01010101'01010101}; product *= product; + assert(product == 0x100f0e0d'0c0b0a09'08070605'04030201__i128); assert(product._Word[0] == 0x08070605'04030201); assert(product._Word[1] == 0x100f0e0d'0c0b0a09); assert(+product == product); @@ -515,18 +610,21 @@ constexpr bool test_signed() { { auto product = _Signed128{0x01020304'05060708, 0x090a0b0c'0d0e0f00} * _Signed128{0x000f0e0d'0c0b0a09, 0x08070605'04030201}; + assert(product == -0x23e49431'bc3293de'923e71aa'e92b70b8__i128); assert(product._Word[0] == 0x6dc18e55'16d48f48); assert(product._Word[1] == 0xdc1b6bce'43cd6c21); assert(+product == product); assert(-product + product == 0); product <<= 11; + assert(product == -0x24a18de1'949ef491'f38d5749'5b85c000__i128); assert(product._Word[0] == 0x0c72a8b6'a47a4000); assert(product._Word[1] == 0xdb5e721e'6b610b6e); assert(+product == product); assert(-product + product == 0); product >>= 17; + assert(product == -0x00001250'c6f0ca4f'7a48f9c6'aba4adc3__i128); assert(product._Word[0] == 0x85b70639'545b523d); assert(product._Word[1] == 0xffffedaf'390f35b0); assert(+product == product); @@ -566,6 +664,7 @@ constexpr bool test_signed() { assert(q._Word[1] == 0); q = _Signed128{0x01010101'01010101, 0x01010101'01010101} / _Signed128{13}; + assert(q == 0x0013c500'13c50013'c50013c5'0013c500__i128); assert(q._Word[0] == 0xc50013c5'0013c500); assert(q._Word[1] == 0x0013c500'13c50013); @@ -658,16 +757,19 @@ constexpr bool test_signed() { const _Signed128 x{0x01020304'02030405, 0x03040506'04050607}; const _Signed128 y{0x07060504'06050403, 0x05040302'04030101}; assert(((x & y) == _Signed128{0x01020104'02010401, 0x01040102'04010001})); + assert(((x & y) == 0x01040102'04010001'01020104'02010401__i128)); auto tmp = x; tmp &= y; assert(tmp == (x & y)); assert(((x | y) == _Signed128{0x07060704'06070407, 0x07040706'04070707})); + assert(((x | y) == 0x07040706'04070707'07060704'06070407__i128)); tmp = x; tmp |= y; assert(tmp == (x | y)); assert(((x ^ y) == _Signed128{0x06040600'04060006, 0x06000604'00060706})); + assert(((x ^ y) == 0x06000604'00060706'06040600'04060006__i128)); tmp = x; tmp ^= y; assert(tmp == (x ^ y)); @@ -704,6 +806,47 @@ constexpr bool test_signed() { assert(x._Word[1] == static_cast(std::numeric_limits::max())); assert(x == std::numeric_limits<_Signed128>::max()); } + { + STATIC_ASSERT(noexcept(0__i128)); + STATIC_ASSERT(noexcept(42__i128)); + STATIC_ASSERT(noexcept(4'2__i128)); + STATIC_ASSERT(noexcept(052__i128)); + STATIC_ASSERT(noexcept(05'2__i128)); + STATIC_ASSERT(noexcept(0x2a__i128)); + STATIC_ASSERT(noexcept(0X2a__i128)); + STATIC_ASSERT(noexcept(0x2A__i128)); + STATIC_ASSERT(noexcept(0X2A__i128)); + STATIC_ASSERT(noexcept(0x2'A__i128)); + STATIC_ASSERT(noexcept(0b101010__i128)); + STATIC_ASSERT(noexcept(0b1'0101'0__i128)); + STATIC_ASSERT(noexcept(0B101010__i128)); + STATIC_ASSERT(noexcept(0B1'0101'0__i128)); + STATIC_ASSERT(noexcept(0xABCDEF__i128)); + STATIC_ASSERT(noexcept(170141183460469231731687303715884105727__i128)); + STATIC_ASSERT(noexcept(0x7fffffff'FFFFFFFF'ffffFFFF'FFFFffff__i128)); + + STATIC_ASSERT(42__i128 == 42); + STATIC_ASSERT(4'2__i128 == 42); + STATIC_ASSERT(42__i128 == 052__i128); + STATIC_ASSERT(4'2__i128 == 052__i128); + STATIC_ASSERT(42__i128 == 0x2a__i128); + STATIC_ASSERT(4'2__i128 == 0X2a__i128); + STATIC_ASSERT(42__i128 == 0b101010__i128); + STATIC_ASSERT(4'2__i128 == 0b101010__i128); + STATIC_ASSERT(42__i128 == 0B101010__i128); + STATIC_ASSERT(4'2__i128 == 0B101010__i128); + STATIC_ASSERT(11259375__i128 == 0xABCDEF__i128); + STATIC_ASSERT( + 170'1411'8346'0469'2317'3168'7303'7158'8410'5727__i128 == 0x7fffffff'FFFFFFFF'ffffFFFF'FFFFffff__i128); + + STATIC_ASSERT( + 170'141'183'460'469'231'731'687'303'715'884'105'727__i128 == std::numeric_limits<_Signed128>::max()); + STATIC_ASSERT(0x7fffffff'ffffffff'ffffffff'ffffffff__i128 == std::numeric_limits<_Signed128>::max()); + + STATIC_ASSERT( + -170'141'183'460'469'231'731'687'303'715'884'105'727__i128 - 1 == std::numeric_limits<_Signed128>::min()); + STATIC_ASSERT(-0x7fffffff'ffffffff'ffffffff'ffffffff__i128 - 1 == std::numeric_limits<_Signed128>::min()); + } return true; } @@ -711,18 +854,26 @@ constexpr bool test_signed() { template T val() noexcept; +#ifdef __cpp_lib_concepts // TRANSITION, GH-395 template concept CanConditional = requires { true ? val() : val(); }; +#else // ^^^ has concepts / has no concepts vvv +template +constexpr bool CanConditional = false; + +template +constexpr bool CanConditional() : val())>> = true; +#endif // ^^^ has no concepts ^^^ constexpr bool test_cross() { // Test the behavior of cross-type operations. -#define TEST(expr, result) \ - do { \ - static_assert(std::same_as>); \ - assert((expr) == (result)); \ +#define TEST(expr, result) \ + do { \ + STATIC_ASSERT(SAME_AS>); \ + assert((expr) == (result)); \ } while (0) //////// Mixed integer-class operands @@ -764,14 +915,16 @@ constexpr bool test_cross() { // convertible only to wider types, or types of the same width and // signedness. Consequently, the conditional operator should reject integer- // class operands with the same width but differing signedness. - static_assert(!CanConditional<_Unsigned128, _Signed128>); - static_assert(!CanConditional<_Signed128, _Unsigned128>); + STATIC_ASSERT(!CanConditional<_Unsigned128, _Signed128>); + STATIC_ASSERT(!CanConditional<_Signed128, _Unsigned128>); +#ifdef __cpp_lib_concepts // TRANSITION, GH-395 // Conversions between integer-class types with the same width and differing // signedness are narrowing, so the three-way comparison operator should // reject mixed operands of such types. - static_assert(!std::three_way_comparable_with<_Unsigned128, _Signed128>); - static_assert(!std::three_way_comparable_with<_Signed128, _Unsigned128>); + STATIC_ASSERT(!std::three_way_comparable_with<_Unsigned128, _Signed128>); + STATIC_ASSERT(!std::three_way_comparable_with<_Signed128, _Unsigned128>); +#endif // __cpp_lib_concepts // Other comparison operators behave as they do for operands of mixed // integral types; when the operands have the same width, the signed operand @@ -917,6 +1070,7 @@ constexpr bool test_cross() { TEST(true ? 42 : _Signed128{13}, _Signed128{42}); TEST(true ? 42 : _Unsigned128{13}, _Unsigned128{42}); +#if _HAS_CXX20 // (meow <=> 0) here is a hack to get prvalues TEST(4 <=> _Unsigned128{3}, (std::strong_ordering::greater <=> 0)); TEST(4 <=> _Signed128{3}, (std::strong_ordering::greater <=> 0)); @@ -930,6 +1084,7 @@ constexpr bool test_cross() { TEST(-3 <=> _Signed128{3}, (std::strong_ordering::less <=> 0)); TEST(_Signed128{-3} <=> 3, (std::strong_ordering::less <=> 0)); TEST(_Unsigned128{-3} <=> 3, (std::strong_ordering::greater <=> 0)); +#endif // _HAS_CXX20 // Other comparison operators behave as they do for operands of mixed // integral types; when the operands have the same width, the signed operand @@ -997,7 +1152,11 @@ constexpr bool test_cross() { x = -26; TEST(u *= 2, x); y = 12; +#ifdef _M_CEE // TRANSITION, silent bad codegen, not yet reported + i = 12; +#else // ^^^ workaround / no workaround vvv TEST(i *= -2, y); +#endif // ^^^ no workaround ^^^ x = _Unsigned128{0x55555555'5555554c, 0x55555555'55555555}; TEST(u /= 3, x); // Yes, u is still unsigned =) @@ -1041,9 +1200,9 @@ constexpr bool test_cross() { int main() { test_unsigned(); - static_assert(test_unsigned()); + STATIC_ASSERT(test_unsigned()); test_signed(); - static_assert(test_signed()); + STATIC_ASSERT(test_signed()); test_cross(); - static_assert(test_cross()); + STATIC_ASSERT(test_cross()); }