Skip to content

GCC rejects constexpr operations on cpp_int (> double_limb_type) initialized from a double_limb_type integer #768

Description

@marcoffee

Currently, gcc is rejecting to compile constexpr operations on fixed cpp_ints wider than double_limb_type. It says that the current initialized field is m_double_first_limb, even after we initialize m_data:

Here

constexpr data_type(double_limb_type i) : m_double_first_limb(i)
{
#ifndef BOOST_MP_NO_CONSTEXPR_DETECTION
if (BOOST_MP_IS_CONST_EVALUATED(m_double_first_limb))
{
data_type t(static_cast<limb_type>(i & max_limb_value), static_cast<limb_type>(i >> limb_bits));

And here

constexpr data_type(double_limb_type i) : m_double_first_limb(i)
{
#ifndef BOOST_MP_NO_CONSTEXPR_DETECTION
if (BOOST_MP_IS_CONST_EVALUATED(m_double_first_limb))
{
data_type t(static_cast<limb_type>(i & max_limb_value), static_cast<limb_type>(i >> limb_bits));

There's also this one, but it will only be an issue with constexpr dynamic allocations, I can fix it here or I can fix it at my PR #654. I also think the BOOST_MP_ENDIAN_LITTLE_BYTE check will not be needed anymore as the split will be explicit as in the other cases

#if BOOST_MP_ENDIAN_LITTLE_BYTE
constexpr data_type(double_limb_type i) noexcept : double_first(i)
{}
constexpr data_type(signed_double_limb_type i) noexcept : double_first(static_cast<double_limb_type>(boost::multiprecision::detail::unsigned_abs(i))) {}
#endif

Below is a minimal reproducible example

#include <boost/multiprecision/cpp_int.hpp>


template <int Bits>
inline constexpr auto repro = [] constexpr noexcept {
  namespace mp = boost::multiprecision;

  using ubits_t = mp::number<
    mp::cpp_int_backend<Bits, Bits, mp::unsigned_magnitude, mp::unchecked, void>,
    // also happens for mp::et_on
    mp::et_off
  >;

  ubits_t x = static_cast<unsigned __int128>(0);
  // ubits_t x = 0U;  // does not fail

  // x *= x;  // multiplication also fails
  x += x;
  return x;
}();

// DOES NOT FAIL
// static_assert(repro<128> == 0);

// FAIL
static_assert(repro<129> == 0);
// static_assert(repro<192> == 0);
// static_assert(repro<512> == 0);

Here's a link to compiler explorer. You can see that this is GCC only as clang works fine.

The solution is a simple change on how the cpp_ints are constructed on the m_double_first_limb (initialize m_data instead of m_double_first_limb on all cases). I can open a PR (with regression tests) if needed (I have a working version here that I used to locate the issue).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions