diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a613999..b88a048 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,8 +38,8 @@ jobs: uses: boostorg/boost-ci/.github/workflows/reusable.yml@master with: exclude_compiler: 'clang-3.5,clang-3.6,clang-3.7,clang-3.8,clang-3.9,clang-4.0,clang-5.0,clang-6.0,clang-7, - clang-8,clang-9,clang-10,clang-11,clang-12,clang-13,clang-14,clang-15,clang-16, - gcc-4.7,gcc-4.8,gcc-4.9,gcc-5,gcc-6,gcc-7,gcc-8,gcc-9,gcc-10,gcc-11,gcc-12' + clang-8,clang-9,clang-10, + gcc-4.7,gcc-4.8,gcc-4.9,gcc-5,gcc-6,gcc-7,gcc-8' # Example of customization: # with: # enable_reflection: true diff --git a/README.md b/README.md index e098089..b7b0a66 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ A safe and fully featured replacement for C++ builtin numeric types. This library requires C++20 and is tested on the following platforms: -* GCC 13 and later -* Clang 17 and later +* GCC 9 and later +* Clang 11 and later * Visual Studio 2022 and later * Intel OneAPI DPC++ diff --git a/doc/modules/ROOT/pages/overview.adoc b/doc/modules/ROOT/pages/overview.adoc index 7c882c6..b907daa 100644 --- a/doc/modules/ROOT/pages/overview.adoc +++ b/doc/modules/ROOT/pages/overview.adoc @@ -30,8 +30,8 @@ Safety critical applications Boost.safe_numbers is tested natively on Ubuntu (x86_64, x86_32, s390x, aarch64, ARM32v7), macOS (x86_64, and Apple Silicon), and Windows (x86_64, x86_32, and ARM64); as well as emulated PPC64LE using QEMU with the following compilers: -* GCC 13 and later -* Clang 17 and later +* GCC 9 and later +* Clang 11 and later * Visual Studio 2022 (14.3) and later * Intel OneAPI DPC++ 2024.2 and later diff --git a/include/boost/safe_numbers/detail/config.hpp b/include/boost/safe_numbers/detail/config.hpp index 1c352d6..63fab69 100644 --- a/include/boost/safe_numbers/detail/config.hpp +++ b/include/boost/safe_numbers/detail/config.hpp @@ -56,4 +56,15 @@ # define BOOST_SAFE_NUMBERS_UNREACHABLE std::abort() #endif +namespace boost::safe_numbers::detail { + +// Workaround for static_assert(false, ...) in if constexpr branches. +// Before C++23 (P2593R1), static_assert(false) is ill-formed even in +// discarded branches. Making the condition depend on a template parameter +// defers evaluation until instantiation. +template +inline constexpr auto dependent_false {false}; + +} // namespace boost::safe_numbers::detail + #endif // BOOST_SAFENUMBERS_CONFIG_HPP diff --git a/include/boost/safe_numbers/detail/type_traits.hpp b/include/boost/safe_numbers/detail/type_traits.hpp index 1ac880f..25af6f3 100644 --- a/include/boost/safe_numbers/detail/type_traits.hpp +++ b/include/boost/safe_numbers/detail/type_traits.hpp @@ -53,7 +53,7 @@ struct underlying> } // namespace impl template -using underlying_type_t = impl::underlying::type; +using underlying_type_t = typename impl::underlying::type; } // namespace boost::safe_numbers::detail diff --git a/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp b/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp index 0572fc4..42a876a 100644 --- a/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp +++ b/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp @@ -47,7 +47,7 @@ class unsigned_integer_basis requires std::is_same_v explicit constexpr unsigned_integer_basis(T) noexcept { - static_assert(false, "Construction from bool is not allowed"); + static_assert(dependent_false, "Construction from bool is not allowed"); } template @@ -86,7 +86,7 @@ constexpr unsigned_integer_basis::operator OtherBasis() const noexcep { if constexpr (sizeof(OtherBasis) < sizeof(BasisType)) { - static_assert(false, "Narrowing conversions are not allowed"); + static_assert(dependent_false, "Narrowing conversions are not allowed"); } return static_cast(basis_); @@ -356,95 +356,95 @@ template } // namespace boost::safe_numbers::detail -#define BOOST_SAFE_NUMBERS_DEFINE_MIXED_UNSIGNED_INTEGER_OP(OP_NAME, OP_SYMBOL) \ -template \ - requires (!std::is_same_v) \ -constexpr auto OP_SYMBOL(const boost::safe_numbers::detail::unsigned_integer_basis, \ - const boost::safe_numbers::detail::unsigned_integer_basis) \ -{ \ - if constexpr (std::is_same_v) \ - { \ - if constexpr (std::is_same_v) \ - { \ - static_assert(false, "Can not perform " OP_NAME " between u8 and u16"); \ - } \ - else if constexpr (std::is_same_v) \ - { \ - static_assert(false, "Can not perform " OP_NAME " between u8 and u32"); \ - } \ - else if constexpr (std::is_same_v) \ - { \ - static_assert(false, "Can not perform " OP_NAME " between u8 and u64"); \ - } \ - else \ - { \ - static_assert(false, "Can not perform " OP_NAME " between u8 and unknown type"); \ - } \ - } \ - else if constexpr (std::is_same_v) \ - { \ - if constexpr (std::is_same_v) \ - { \ - static_assert(false, "Can not perform " OP_NAME " between u16 and u8"); \ - } \ - else if constexpr (std::is_same_v) \ - { \ - static_assert(false, "Can not perform " OP_NAME " between u16 and u32"); \ - } \ - else if constexpr (std::is_same_v) \ - { \ - static_assert(false, "Can not perform " OP_NAME " between u16 and u64"); \ - } \ - else \ - { \ - static_assert(false, "Can not perform " OP_NAME " between u16 and unknown type"); \ - } \ - } \ - else if constexpr (std::is_same_v) \ - { \ - if constexpr (std::is_same_v) \ - { \ - static_assert(false, "Can not perform " OP_NAME " between u32 and u8"); \ - } \ - else if constexpr (std::is_same_v) \ - { \ - static_assert(false, "Can not perform " OP_NAME " between u32 and u16"); \ - } \ - else if constexpr (std::is_same_v) \ - { \ - static_assert(false, "Can not perform " OP_NAME " between u32 and u64"); \ - } \ - else \ - { \ - static_assert(false, "Can not perform " OP_NAME " between u32 and unknown type"); \ - } \ - } \ - else if constexpr (std::is_same_v) \ - { \ - if constexpr (std::is_same_v) \ - { \ - static_assert(false, "Can not perform " OP_NAME " between u64 and u8"); \ - } \ - else if constexpr (std::is_same_v) \ - { \ - static_assert(false, "Can not perform " OP_NAME " between u64 and u16"); \ - } \ - else if constexpr (std::is_same_v) \ - { \ - static_assert(false, "Can not perform " OP_NAME " between u64 and u32"); \ - } \ - else \ - { \ - static_assert(false, "Can not perform " OP_NAME " between u64 and unknown type"); \ - } \ - } \ - else \ - { \ - static_assert(false, "Can not perform " OP_NAME " on mixed width unsigned integer types"); \ - } \ - \ - return boost::safe_numbers::detail::unsigned_integer_basis(0); \ +#define BOOST_SAFE_NUMBERS_DEFINE_MIXED_UNSIGNED_INTEGER_OP(OP_NAME, OP_SYMBOL) \ +template \ + requires (!std::is_same_v) \ +constexpr auto OP_SYMBOL(const boost::safe_numbers::detail::unsigned_integer_basis, \ + const boost::safe_numbers::detail::unsigned_integer_basis) \ +{ \ + if constexpr (std::is_same_v) \ + { \ + if constexpr (std::is_same_v) \ + { \ + static_assert(boost::safe_numbers::detail::dependent_false, "Can not perform " OP_NAME " between u8 and u16"); \ + } \ + else if constexpr (std::is_same_v) \ + { \ + static_assert(boost::safe_numbers::detail::dependent_false, "Can not perform " OP_NAME " between u8 and u32"); \ + } \ + else if constexpr (std::is_same_v) \ + { \ + static_assert(boost::safe_numbers::detail::dependent_false, "Can not perform " OP_NAME " between u8 and u64"); \ + } \ + else \ + { \ + static_assert(boost::safe_numbers::detail::dependent_false, "Can not perform " OP_NAME " between u8 and unknown type"); \ + } \ + } \ + else if constexpr (std::is_same_v) \ + { \ + if constexpr (std::is_same_v) \ + { \ + static_assert(boost::safe_numbers::detail::dependent_false, "Can not perform " OP_NAME " between u16 and u8"); \ + } \ + else if constexpr (std::is_same_v) \ + { \ + static_assert(boost::safe_numbers::detail::dependent_false, "Can not perform " OP_NAME " between u16 and u32"); \ + } \ + else if constexpr (std::is_same_v) \ + { \ + static_assert(boost::safe_numbers::detail::dependent_false, "Can not perform " OP_NAME " between u16 and u64"); \ + } \ + else \ + { \ + static_assert(boost::safe_numbers::detail::dependent_false, "Can not perform " OP_NAME " between u16 and unknown type"); \ + } \ + } \ + else if constexpr (std::is_same_v) \ + { \ + if constexpr (std::is_same_v) \ + { \ + static_assert(boost::safe_numbers::detail::dependent_false, "Can not perform " OP_NAME " between u32 and u8"); \ + } \ + else if constexpr (std::is_same_v) \ + { \ + static_assert(boost::safe_numbers::detail::dependent_false, "Can not perform " OP_NAME " between u32 and u16"); \ + } \ + else if constexpr (std::is_same_v) \ + { \ + static_assert(boost::safe_numbers::detail::dependent_false, "Can not perform " OP_NAME " between u32 and u64"); \ + } \ + else \ + { \ + static_assert(boost::safe_numbers::detail::dependent_false, "Can not perform " OP_NAME " between u32 and unknown type"); \ + } \ + } \ + else if constexpr (std::is_same_v) \ + { \ + if constexpr (std::is_same_v) \ + { \ + static_assert(boost::safe_numbers::detail::dependent_false, "Can not perform " OP_NAME " between u64 and u8"); \ + } \ + else if constexpr (std::is_same_v) \ + { \ + static_assert(boost::safe_numbers::detail::dependent_false, "Can not perform " OP_NAME " between u64 and u16"); \ + } \ + else if constexpr (std::is_same_v) \ + { \ + static_assert(boost::safe_numbers::detail::dependent_false, "Can not perform " OP_NAME " between u64 and u32"); \ + } \ + else \ + { \ + static_assert(boost::safe_numbers::detail::dependent_false, "Can not perform " OP_NAME " between u64 and unknown type"); \ + } \ + } \ + else \ + { \ + static_assert(boost::safe_numbers::detail::dependent_false, "Can not perform " OP_NAME " on mixed width unsigned integer types"); \ + } \ + \ + return boost::safe_numbers::detail::unsigned_integer_basis(0); \ } namespace boost::safe_numbers::detail { diff --git a/include/boost/safe_numbers/format.hpp b/include/boost/safe_numbers/format.hpp index d5216cd..33204a5 100644 --- a/include/boost/safe_numbers/format.hpp +++ b/include/boost/safe_numbers/format.hpp @@ -9,6 +9,8 @@ #include #include +#ifdef BOOST_SAFE_NUMBERS_DETAIL_INT128_HAS_FORMAT + #ifndef BOOST_SAFE_NUMBERS_BUILD_MODULE #include @@ -27,4 +29,6 @@ struct std::formatterclang:on gcc:on - [ requires cxx17_hdr_charconv cxx20_hdr_bit cxx20_hdr_compare cxx20_hdr_concepts cxx20_hdr_format ] + [ requires cxx20_hdr_bit cxx20_hdr_compare cxx20_hdr_concepts ] ; run quick.cpp ; diff --git a/test/test_unsigned_std_format.cpp b/test/test_unsigned_std_format.cpp index 984f5d9..dd274f9 100644 --- a/test/test_unsigned_std_format.cpp +++ b/test/test_unsigned_std_format.cpp @@ -9,10 +9,17 @@ import boost.safe_numbers; #else #include + +#ifdef BOOST_SAFE_NUMBERS_DETAIL_INT128_HAS_FORMAT + #include #endif +#endif + +#ifdef BOOST_SAFE_NUMBERS_DETAIL_INT128_HAS_FORMAT + #include using namespace boost::safe_numbers; @@ -37,3 +44,12 @@ int main() return boost::report_errors(); } + +#else + +int main() +{ + return 0; +} + +#endif