From 937471190197f43c4796ab70049da67dd7322b7b Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 23 Jul 2026 09:33:10 -0400 Subject: [PATCH 01/13] Add new parse literal function with compile error for out of range --- include/boost/int128/detail/mini_from_chars.hpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/include/boost/int128/detail/mini_from_chars.hpp b/include/boost/int128/detail/mini_from_chars.hpp index 0db8ac56..997581f4 100644 --- a/include/boost/int128/detail/mini_from_chars.hpp +++ b/include/boost/int128/detail/mini_from_chars.hpp @@ -265,6 +265,20 @@ BOOST_INT128_TEST_EXPORT BOOST_INT128_HOST_DEVICE constexpr int from_chars_liter return impl::from_chars_integer_impl(first, last, value, base); } +// Parse a user-defined literal, hard-failing on any malformed or out-of-range input. +template +BOOST_INT128_HOST_DEVICE constexpr Integer parse_literal(const char* first, const char* last) noexcept +{ + Integer value {}; + + if (from_chars_literal(first, last, value) != first - last) + { + BOOST_INT128_UNREACHABLE; + } + + return value; +} + } // namespace detail } // namespace int128 } // namespace boost From 4de182da1ff6905ca6b4dd9d439e8c27f5a300e9 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 23 Jul 2026 09:33:23 -0400 Subject: [PATCH 02/13] Update literals to use new function --- include/boost/int128/literals.hpp | 32 ++++++++----------------------- 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/include/boost/int128/literals.hpp b/include/boost/int128/literals.hpp index e170f7db..47b18d8a 100644 --- a/include/boost/int128/literals.hpp +++ b/include/boost/int128/literals.hpp @@ -17,58 +17,42 @@ namespace literals { BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr uint128_t operator ""_u128(const char* str) noexcept { - uint128_t result {}; - detail::from_chars_literal(str, str + detail::strlen(str), result); - return result; + return detail::parse_literal(str, str + detail::strlen(str)); } BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr uint128_t operator ""_U128(const char* str) noexcept { - uint128_t result {}; - detail::from_chars_literal(str, str + detail::strlen(str), result); - return result; + return detail::parse_literal(str, str + detail::strlen(str)); } BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr uint128_t operator ""_u128(const char* str, std::size_t len) noexcept { - uint128_t result {}; - detail::from_chars_literal(str, str + len, result); - return result; + return detail::parse_literal(str, str + len); } BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr uint128_t operator ""_U128(const char* str, std::size_t len) noexcept { - uint128_t result {}; - detail::from_chars_literal(str, str + len, result); - return result; + return detail::parse_literal(str, str + len); } BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr int128_t operator ""_i128(const char* str) noexcept { - int128_t result {}; - detail::from_chars_literal(str, str + detail::strlen(str), result); - return result; + return detail::parse_literal(str, str + detail::strlen(str)); } BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr int128_t operator ""_I128(const char* str) noexcept { - int128_t result {}; - detail::from_chars_literal(str, str + detail::strlen(str), result); - return result; + return detail::parse_literal(str, str + detail::strlen(str)); } BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr int128_t operator ""_i128(const char* str, std::size_t len) noexcept { - int128_t result {}; - detail::from_chars_literal(str, str + len, result); - return result; + return detail::parse_literal(str, str + len); } BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr int128_t operator ""_I128(const char* str, std::size_t len) noexcept { - int128_t result {}; - detail::from_chars_literal(str, str + len, result); - return result; + return detail::parse_literal(str, str + len); } } // namespace literals From 59e51ad6f32c84d386bce89bced111bc786421e8 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 23 Jul 2026 09:34:20 -0400 Subject: [PATCH 03/13] Add new compile failure test --- test/Jamfile | 2 ++ test/compile_tests/literals_overflow_fail.cpp | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 test/compile_tests/literals_overflow_fail.cpp diff --git a/test/Jamfile b/test/Jamfile index f38218ca..f9ab09a9 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -152,3 +152,5 @@ compile compile_tests/numeric_compile.cpp ; compile compile_tests/string_compile.cpp ; compile compile_tests/random_compile.cpp ; compile compile_tests/utilities_compile.cpp ; + +compile-fail compile_tests/literals_overflow_fail.cpp ; diff --git a/test/compile_tests/literals_overflow_fail.cpp b/test/compile_tests/literals_overflow_fail.cpp new file mode 100644 index 00000000..6679f106 --- /dev/null +++ b/test/compile_tests/literals_overflow_fail.cpp @@ -0,0 +1,18 @@ +// Copyright 2026 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt +// +// A user-defined literal whose value does not fit the target type must be rejected +// at compile time instead of being silently accepted. + +#include + +using namespace boost::int128::literals; + +int main() +{ + constexpr auto value = 340282366920938463463374607431768211456_u128; + static_cast(value); + + return 0; +} From 5cd995890d3e82f704adc75383b1e88e10347d48 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 23 Jul 2026 09:55:18 -0400 Subject: [PATCH 04/13] Add testing of literal prefixes --- test/test_literals.cpp | 56 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/test/test_literals.cpp b/test/test_literals.cpp index fe55ad83..b60f8b92 100644 --- a/test/test_literals.cpp +++ b/test/test_literals.cpp @@ -60,6 +60,35 @@ void test_u128_digit_separators() static_assert(100'000_u128 == boost::int128::uint128_t{100000}, "constexpr separator"); } +void test_u128_base_prefixes() +{ + // A C++ base prefix is recognized and the digits parsed in that base + + // Hexadecimal (0x / 0X) + BOOST_TEST(boost::int128::uint128_t{255} == 0xFF_u128); + BOOST_TEST(boost::int128::uint128_t{255} == 0XfF_U128); + BOOST_TEST(boost::int128::uint128_t{255} == "0xff"_u128); + BOOST_TEST(boost::int128::uint128_t{0xDEADBEEFULL} == 0xDEAD'BEEF_u128); + + // Binary (0b / 0B) + BOOST_TEST(boost::int128::uint128_t{10} == 0b1010_u128); + BOOST_TEST(boost::int128::uint128_t{240} == 0B1111'0000_U128); + + // Octal (leading 0) + BOOST_TEST(boost::int128::uint128_t{511} == 0777_u128); + BOOST_TEST(boost::int128::uint128_t{8} == 010_u128); + BOOST_TEST(boost::int128::uint128_t{0} == 00_u128); + + // A full-width value written in hexadecimal is the maximum + const boost::int128::uint128_t max_val {std::numeric_limits::max()}; + BOOST_TEST(max_val == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF_u128); + + // Prefixes must be usable in a constant expression + static_assert(0x10_u128 == boost::int128::uint128_t{16}, "constexpr hex"); + static_assert(0b100_u128 == boost::int128::uint128_t{4}, "constexpr binary"); + static_assert(010_u128 == boost::int128::uint128_t{8}, "constexpr octal"); +} + void test_i128_literals() { BOOST_TEST(boost::int128::int128_t{0} == 0_i128); @@ -101,6 +130,31 @@ void test_i128_digit_separators() static_assert(100'000_i128 == boost::int128::int128_t{100000}, "constexpr separator"); } +void test_i128_base_prefixes() +{ + // Hexadecimal, binary and octal prefixes are recognized for the signed type too + BOOST_TEST(boost::int128::int128_t{127} == 0x7F_i128); + BOOST_TEST(boost::int128::int128_t{255} == "0xFF"_I128); + BOOST_TEST(boost::int128::int128_t{10} == 0b1010_i128); + BOOST_TEST(boost::int128::int128_t{511} == 0777_i128); + + // A leading unary minus is applied after the (prefixed) literal is parsed + BOOST_TEST(boost::int128::int128_t{-255} == -0xFF_i128); + BOOST_TEST(boost::int128::int128_t{-511} == -0777_i128); + + // The string form may embed the sign ahead of the prefix + BOOST_TEST(boost::int128::int128_t{-255} == "-0xFF"_i128); + BOOST_TEST(boost::int128::int128_t{-10} == "-0b1010"_I128); + BOOST_TEST(boost::int128::int128_t{-511} == "-0777"_i128); + + // A full-width positive value written in hexadecimal is the maximum + const boost::int128::int128_t max_val {std::numeric_limits::max()}; + BOOST_TEST(max_val == 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF_i128); + + // Prefixes must be usable in a constant expression + static_assert(-0x10_i128 == boost::int128::int128_t{-16}, "constexpr signed hex"); +} + #ifdef _MSC_VER # pragma warning(pop) #endif @@ -109,8 +163,10 @@ int main() { test_u128_literals(); test_u128_digit_separators(); + test_u128_base_prefixes(); test_i128_literals(); test_i128_digit_separators(); + test_i128_base_prefixes(); return boost::report_errors(); } From 4a36d36f804260e42dd20b6df99a015578ebdf31 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 23 Jul 2026 09:55:29 -0400 Subject: [PATCH 05/13] Add handling of literal prefix --- .../boost/int128/detail/mini_from_chars.hpp | 62 ++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/include/boost/int128/detail/mini_from_chars.hpp b/include/boost/int128/detail/mini_from_chars.hpp index 997581f4..fa59f405 100644 --- a/include/boost/int128/detail/mini_from_chars.hpp +++ b/include/boost/int128/detail/mini_from_chars.hpp @@ -266,16 +266,76 @@ BOOST_INT128_TEST_EXPORT BOOST_INT128_HOST_DEVICE constexpr int from_chars_liter } // Parse a user-defined literal, hard-failing on any malformed or out-of-range input. +// A C++ base prefix (0x/0X hex, 0b/0B binary, or a leading 0 for octal) is stripped and +// the digits parsed in that base, otherwise handled as base 10 template BOOST_INT128_HOST_DEVICE constexpr Integer parse_literal(const char* first, const char* last) noexcept { Integer value {}; - if (from_chars_literal(first, last, value) != first - last) + // A leading sign stays with the digits; a base prefix, if present, follows it. + auto next = first; + const bool negative {next != last && *next == '-'}; + if (negative) + { + ++next; + } + + int base {10}; + bool prefixed {false}; + + if (last - next >= 2 && *next == '0') + { + const char marker {next[1]}; + if (marker == 'x' || marker == 'X') + { + base = 16; + next += 2; + prefixed = true; + } + else if (marker == 'b' || marker == 'B') + { + base = 2; + next += 2; + prefixed = true; + } + else + { + base = 8; + next += 1; + prefixed = true; + } + } + + // With no prefix, from_chars_literal handles the sign and the full decimal range + if (!prefixed) + { + if (from_chars_literal(first, last, value) != first - last) + { + BOOST_INT128_UNREACHABLE; + } + + return value; + } + + // Prefixed: parse the magnitude in the detected base, then reapply the sign. + if (from_chars_literal(next, last, value, base) != next - last) { BOOST_INT128_UNREACHABLE; } + if (negative) + { + BOOST_INT128_IF_CONSTEXPR (std::numeric_limits::is_signed) + { + value = static_cast(-value); + } + else + { + BOOST_INT128_UNREACHABLE; + } + } + return value; } From b529d17118460262499dd9a19612f829cd4f5da3 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 23 Jul 2026 09:55:38 -0400 Subject: [PATCH 06/13] Add new compile failure test for prefix --- test/Jamfile | 1 + .../literals_base_prefix_fail.cpp | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 test/compile_tests/literals_base_prefix_fail.cpp diff --git a/test/Jamfile b/test/Jamfile index f9ab09a9..75f04a58 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -154,3 +154,4 @@ compile compile_tests/random_compile.cpp ; compile compile_tests/utilities_compile.cpp ; compile-fail compile_tests/literals_overflow_fail.cpp ; +compile-fail compile_tests/literals_base_prefix_fail.cpp ; diff --git a/test/compile_tests/literals_base_prefix_fail.cpp b/test/compile_tests/literals_base_prefix_fail.cpp new file mode 100644 index 00000000..f7e10b0c --- /dev/null +++ b/test/compile_tests/literals_base_prefix_fail.cpp @@ -0,0 +1,19 @@ +// Copyright 2026 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt +// +// A prefixed user-defined literal whose value does not fit the target type must be +// rejected at compile time. 0x1 followed by 32 zeros is 2^128, one past the +// uint128_t maximum, so the base-16 parse overflows and this must not compile. + +#include + +using namespace boost::int128::literals; + +int main() +{ + constexpr auto value = 0x100000000000000000000000000000000_u128; + static_cast(value); + + return 0; +} From 6c6dcad526499b1d2085dab5befe2865ef6b669b Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 23 Jul 2026 09:55:54 -0400 Subject: [PATCH 07/13] Document the literal prefixes and error handling --- doc/modules/ROOT/pages/literals.adoc | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/doc/modules/ROOT/pages/literals.adoc b/doc/modules/ROOT/pages/literals.adoc index d3b72b2d..2670613c 100644 --- a/doc/modules/ROOT/pages/literals.adoc +++ b/doc/modules/ROOT/pages/literals.adoc @@ -45,7 +45,30 @@ BOOST_INT128_HOST_DEVICE constexpr int128_t operator ""_I128(const char* str, st The macros at the end allow you to write out a 128-bit number like you would with say `UINT64_C` without having to add quotes. -NOTE: The minimum `int128_t` value cannot be written as a negated `_i128` literal, because its positive magnitude (2^127) exceeds the maximum `int128_t` value and so fails to parse (yielding `0`). Use `BOOST_INT128_INT128_C(-170141183460469231731687303715884105728)` or `(std::numeric_limits::min)()` instead. +== Bases + +The literals accept the standard C++ integer base prefixes in addition to plain decimal. +A `0x` or `0X` prefix selects hexadecimal, `0b` or `0B` selects binary, and a leading `0` selects octal. +The prefix is stripped and the remaining digits are parsed in the selected base. +Digit separators (`'`) may be used in any base. + +[source, c++] +---- +0xDEAD'BEEF_u128 // hexadecimal, == 3735928559 +0b1111'0000_u128 // binary, == 240 +0777_u128 // octal, == 511 +255_u128 // decimal, == 255 +-0xFF_i128 // == -255 (the unary minus is applied after parsing) +"-0b1010"_i128 // == -10 (the string form may embed the sign) +---- + +== Error Handling + +An invalid literal is rejected at compile time rather than being silently accepted. +This includes a value that overflows the target type, a digit that is not valid for the selected base (for example `0b2` or `09`), and an empty magnitude such as `0x`. +When such a literal is used in a constant expression the parse reaches `BOOST_INT128_UNREACHABLE`, which is not a constant expression and therefore is a hard compile error. + +NOTE: The minimum `int128_t` value cannot be written as a negated `_i128` literal, because its positive magnitude (2^127) exceeds the maximum `int128_t` value and so fails to parse (a hard compile error in a constant expression). Use `BOOST_INT128_INT128_C(-170141183460469231731687303715884105728)`, the string form `"-170141183460469231731687303715884105728"_i128`, or `(std::numeric_limits::min)()` instead. See the xref:examples.adoc#examples_construction[construction examples] for usage demonstrations of both literals and macros. From d2a0bbac4873bf56547a14c5345b038cef474618 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 23 Jul 2026 11:20:05 -0400 Subject: [PATCH 08/13] Improve rejection --- .../boost/int128/detail/mini_from_chars.hpp | 45 ++++++++++++++++--- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/include/boost/int128/detail/mini_from_chars.hpp b/include/boost/int128/detail/mini_from_chars.hpp index fa59f405..229cf1b9 100644 --- a/include/boost/int128/detail/mini_from_chars.hpp +++ b/include/boost/int128/detail/mini_from_chars.hpp @@ -15,6 +15,10 @@ #include #include +#if !(defined(BOOST_INT128_HAS_GPU_SUPPORT) || defined(BOOST_INT128_DISABLE_EXCEPTIONS)) +#include +#endif + #endif namespace boost { @@ -265,6 +269,26 @@ BOOST_INT128_TEST_EXPORT BOOST_INT128_HOST_DEVICE constexpr int from_chars_liter return impl::from_chars_integer_impl(first, last, value, base); } +// Rejects an out of range literal +[[noreturn]] BOOST_INT128_HOST_DEVICE inline void parse_literal_out_of_range() +{ + #if defined(BOOST_INT128_HAS_GPU_SUPPORT) || defined(BOOST_INT128_DISABLE_EXCEPTIONS) + BOOST_INT128_UNREACHABLE; + #else + BOOST_INT128_THROW_EXCEPTION(std::out_of_range("Literal is out of range of the target type")); + #endif +} + +// Rejects an invlaid literal +[[noreturn]] BOOST_INT128_HOST_DEVICE inline void parse_invalid_literal() +{ + #if defined(BOOST_INT128_HAS_GPU_SUPPORT) || defined(BOOST_INT128_DISABLE_EXCEPTIONS) + BOOST_INT128_UNREACHABLE; + #else + BOOST_INT128_THROW_EXCEPTION(std::invalid_argument("Literal is not a valid integer")); + #endif +} + // Parse a user-defined literal, hard-failing on any malformed or out-of-range input. // A C++ base prefix (0x/0X hex, 0b/0B binary, or a leading 0 for octal) is stripped and // the digits parsed in that base, otherwise handled as base 10 @@ -307,21 +331,32 @@ BOOST_INT128_HOST_DEVICE constexpr Integer parse_literal(const char* first, cons } } - // With no prefix, from_chars_literal handles the sign and the full decimal range + // With no prefix, from_chars_literal handles the sign and the full decimal range. + // Overflow is reported as EDOM; anything else short of full consumption is malformed. if (!prefixed) { - if (from_chars_literal(first, last, value) != first - last) + const auto status = from_chars_literal(first, last, value); + if (status == EDOM) { - BOOST_INT128_UNREACHABLE; + parse_literal_out_of_range(); + } + else if (status != first - last) + { + parse_invalid_literal(); } return value; } // Prefixed: parse the magnitude in the detected base, then reapply the sign. - if (from_chars_literal(next, last, value, base) != next - last) + const auto status = from_chars_literal(next, last, value, base); + if (status == EDOM) + { + parse_literal_out_of_range(); + } + else if (status != next - last) { - BOOST_INT128_UNREACHABLE; + parse_invalid_literal(); } if (negative) From 93721b94372c6cd75938ad5640b04850cd192fe6 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 23 Jul 2026 11:20:23 -0400 Subject: [PATCH 09/13] Add test of invalid literal compilation failure --- test/Jamfile | 1 + test/compile_tests/literals_invalid_fail.cpp | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 test/compile_tests/literals_invalid_fail.cpp diff --git a/test/Jamfile b/test/Jamfile index 75f04a58..e1a2c71c 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -155,3 +155,4 @@ compile compile_tests/utilities_compile.cpp ; compile-fail compile_tests/literals_overflow_fail.cpp ; compile-fail compile_tests/literals_base_prefix_fail.cpp ; +compile-fail compile_tests/literals_invalid_fail.cpp ; diff --git a/test/compile_tests/literals_invalid_fail.cpp b/test/compile_tests/literals_invalid_fail.cpp new file mode 100644 index 00000000..5c12e9dc --- /dev/null +++ b/test/compile_tests/literals_invalid_fail.cpp @@ -0,0 +1,19 @@ +// Copyright 2026 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt +// +// A user-defined literal whose characters are not valid digits for the base must be +// rejected at compile time instead of being silently truncated. "12xyz" parses the +// leading 12 and then hits the invalid 'x', so the literal is malformed. + +#include + +using namespace boost::int128::literals; + +int main() +{ + constexpr auto value = "12xyz"_u128; + static_cast(value); + + return 0; +} From b432e6c041ed023f715b746303d8440281be134f Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 23 Jul 2026 12:04:05 -0400 Subject: [PATCH 10/13] Update example to not fail --- examples/to_string.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/to_string.cpp b/examples/to_string.cpp index 1da26c73..68bb4637 100644 --- a/examples/to_string.cpp +++ b/examples/to_string.cpp @@ -55,10 +55,10 @@ int main() std::cout << "Match: " << (s_large_str == s_large_std) << std::endl; // Values beyond 64-bit range. - // The positive magnitude 2^127 exceeds INT128_MAX, so INT128_MIN cannot be - // written as a negated _i128 literal; this line therefore yields 0. Use the - // INT128_C macro (below) or std::numeric_limits::min() instead. - const auto large_negative {-170141183460469231731687303715884105728_i128}; + // INT128_MIN via a negated numeric literal fails: the sign is not part of the + // literal, so the positive magnitude 2^127 is read first and rejected as out of + // range. A string literal keeps the sign with the digits, so it parses cleanly. + const auto large_negative {"-170141183460469231731687303715884105728"_i128}; std::cout << "\nint128_t min with string literal: " << to_string(large_negative) << std::endl; const auto large_negative_c {BOOST_INT128_INT128_C(-170141183460469231731687303715884105728)}; From 9ca17801ae40e4e20f5f5661ec70b9e15fd7afc7 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 23 Jul 2026 12:05:39 -0400 Subject: [PATCH 11/13] Use macro workaround for GCC 5 support --- .../boost/int128/detail/mini_from_chars.hpp | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/include/boost/int128/detail/mini_from_chars.hpp b/include/boost/int128/detail/mini_from_chars.hpp index 229cf1b9..94291e54 100644 --- a/include/boost/int128/detail/mini_from_chars.hpp +++ b/include/boost/int128/detail/mini_from_chars.hpp @@ -279,7 +279,7 @@ BOOST_INT128_TEST_EXPORT BOOST_INT128_HOST_DEVICE constexpr int from_chars_liter #endif } -// Rejects an invlaid literal +// Rejects an invalid literal [[noreturn]] BOOST_INT128_HOST_DEVICE inline void parse_invalid_literal() { #if defined(BOOST_INT128_HAS_GPU_SUPPORT) || defined(BOOST_INT128_DISABLE_EXCEPTIONS) @@ -289,6 +289,14 @@ BOOST_INT128_TEST_EXPORT BOOST_INT128_HOST_DEVICE constexpr int from_chars_liter #endif } +// GCC before 6 rejects a constexpr function that contains a throw-expression or a +// call to a non-constexpr function anywhere in its body so we need to use unreachable in that case +#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 6 +# define BOOST_INT128_REJECT_LITERAL(reporter) BOOST_INT128_UNREACHABLE +#else +# define BOOST_INT128_REJECT_LITERAL(reporter) reporter() +#endif + // Parse a user-defined literal, hard-failing on any malformed or out-of-range input. // A C++ base prefix (0x/0X hex, 0b/0B binary, or a leading 0 for octal) is stripped and // the digits parsed in that base, otherwise handled as base 10 @@ -338,11 +346,11 @@ BOOST_INT128_HOST_DEVICE constexpr Integer parse_literal(const char* first, cons const auto status = from_chars_literal(first, last, value); if (status == EDOM) { - parse_literal_out_of_range(); + BOOST_INT128_REJECT_LITERAL(parse_literal_out_of_range); } else if (status != first - last) { - parse_invalid_literal(); + BOOST_INT128_REJECT_LITERAL(parse_invalid_literal); } return value; @@ -352,11 +360,11 @@ BOOST_INT128_HOST_DEVICE constexpr Integer parse_literal(const char* first, cons const auto status = from_chars_literal(next, last, value, base); if (status == EDOM) { - parse_literal_out_of_range(); + BOOST_INT128_REJECT_LITERAL(parse_literal_out_of_range); } else if (status != next - last) { - parse_invalid_literal(); + BOOST_INT128_REJECT_LITERAL(parse_invalid_literal); } if (negative) @@ -378,4 +386,6 @@ BOOST_INT128_HOST_DEVICE constexpr Integer parse_literal(const char* first, cons } // namespace int128 } // namespace boost +#undef BOOST_INT128_REJECT_LITERAL + #endif //MINI_FROM_CHARS_HPP From f7b7123cfd931ec44c777a1651b531c496fb2ab2 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 23 Jul 2026 13:46:42 -0400 Subject: [PATCH 12/13] Remove test from MSVC 14.1 that yields ICE --- test/test_literals.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/test_literals.cpp b/test/test_literals.cpp index b60f8b92..2ac1e211 100644 --- a/test/test_literals.cpp +++ b/test/test_literals.cpp @@ -151,8 +151,10 @@ void test_i128_base_prefixes() const boost::int128::int128_t max_val {std::numeric_limits::max()}; BOOST_TEST(max_val == 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF_i128); - // Prefixes must be usable in a constant expression + // MSVC 14.1 ICE + #if !defined(_MSC_VER) || _MSC_VER >= 1920 static_assert(-0x10_i128 == boost::int128::int128_t{-16}, "constexpr signed hex"); + #endif } #ifdef _MSC_VER From 03c1e3d1e81a60064c61c623a384c113442131ae Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 23 Jul 2026 15:35:51 -0400 Subject: [PATCH 13/13] Remove more MSVC 14.1 ICE --- test/test_literals.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/test_literals.cpp b/test/test_literals.cpp index 2ac1e211..5fade8b7 100644 --- a/test/test_literals.cpp +++ b/test/test_literals.cpp @@ -56,8 +56,11 @@ void test_u128_digit_separators() const boost::int128::uint128_t max_val {std::numeric_limits::max()}; BOOST_TEST(max_val == 340'282'366'920'938'463'463'374'607'431'768'211'455_u128); + // MSVC 14.1 ICE + #if !defined(_MSC_VER) || _MSC_VER >= 1920 // Separators must be usable in a constant expression static_assert(100'000_u128 == boost::int128::uint128_t{100000}, "constexpr separator"); + #endif } void test_u128_base_prefixes() @@ -83,10 +86,13 @@ void test_u128_base_prefixes() const boost::int128::uint128_t max_val {std::numeric_limits::max()}; BOOST_TEST(max_val == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF_u128); + // MSVC 14.1 ICE + #if !defined(_MSC_VER) || _MSC_VER >= 1920 // Prefixes must be usable in a constant expression static_assert(0x10_u128 == boost::int128::uint128_t{16}, "constexpr hex"); static_assert(0b100_u128 == boost::int128::uint128_t{4}, "constexpr binary"); static_assert(010_u128 == boost::int128::uint128_t{8}, "constexpr octal"); + #endif } void test_i128_literals() @@ -126,8 +132,11 @@ void test_i128_digit_separators() const boost::int128::int128_t max_val {std::numeric_limits::max()}; BOOST_TEST(max_val == 170'141'183'460'469'231'731'687'303'715'884'105'727_i128); + // MSVC 14.1 ICE + #if !defined(_MSC_VER) || _MSC_VER >= 1920 // Separators must be usable in a constant expression static_assert(100'000_i128 == boost::int128::int128_t{100000}, "constexpr separator"); + #endif } void test_i128_base_prefixes()