https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126483
Bug ID: 126483
Summary: Wrong active union member in constant evaluation of
variable template initializers
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: c++
Assignee: unassigned at gcc dot gnu.org
Reporter: speaktomarco at gmail dot com
Target Milestone: ---
Created attachment 65168
--> https://gcc.gnu.org/bugzilla/attachment.cgi?id=65168&action=edit
preprocessed reproducer, Boost.Multiprecision 1.91
Command line: g++ -std=gnu++26 -fsyntax-only bug_repro.ii
(same result with -std=c++20)
gcc rejects constexpr operations on a Boost.Multiprecision fixed cpp_int
wider than 128 bits when the value was initialized from a 128-bit integer,
but only when the code runs inside the initializer of a variable template.
clang 19 and 22 accept the same translation unit. Happens on current trunk:
https://godbolt.org/z/fojP5KPMT (gcc trunk/16.1/14.4 and clang 19.1). Tested
locally with gcc 16.1.0 and clang 22.1.8 (more details below).
Source before preprocessing (same as from compiler explorer link):
#include <boost/multiprecision/cpp_int.hpp>
namespace mp = boost::multiprecision;
using u129 = mp::number<
mp::cpp_int_backend<129, 129, mp::unsigned_magnitude, mp::unchecked, void>,
mp::et_off
>;
// FAILS: variable template initializer
template <int>
inline constexpr auto vt = [] (void) constexpr noexcept {
u129 x = static_cast<unsigned __int128>(0);
x += x;
return x;
}();
// COMPILES: same body, plain constexpr variable
inline constexpr auto plain = [] (void) constexpr noexcept {
u129 x = static_cast<unsigned __int128>(0);
x += x;
return x;
}();
static_assert(plain == 0);
static_assert(vt<0> == 0);
It is rejected with the following error:
/usr/local/include/boost/multiprecision/cpp_int/add_unsigned.hpp:32:16: error:
accessing 'boost::multiprecision::backends::cpp_int_base<129, 129,
boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked,
void, false>::data_type::m_data' member instead of active
'boost::multiprecision::backends::cpp_int_base<129, 129,
boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked,
void, false>::data_type::m_double_first_limb' member in constant expression
32 | result = static_cast<double_limb_type>(*a.limbs()) +
static_cast<double_limb_type>(*b.limbs());
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The union constructor involved (boost/multiprecision/cpp_int.hpp)
mem-initializes m_double_first_limb and then, under
std::is_constant_evaluated() (aliased as BOOST_MP_IS_CONST_EVALUATED), switches
the active member
to m_data through whole-union copy assignment:
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));
*this = t;
}
#endif
}
The active member that gcc reports always matches the one the
non-constant-evaluated branch of the constructor would initialize, even
though that branch never runs. The exact same body compiles as a plain
constexpr variable or a constexpr function (comment out the vt
static_assert on the reproducer and it passes). The same error happens with
'*this = t' replaced by 'std::construct_at' (maybe related to PR 102286),
and with std::is_constant_evaluated() replaced by 'if consteval'.
This is possibly related to the mce_* call handling from PR 119150.
Closest existing PRs I found: PR 92399, PR 110245, PR 117679.
I also could not reproduce this without boost (standalone unions with similar
structure compile fine), so the
preprocessed file is attached (xz-compressed).
Found while investigating
https://github.com/boostorg/multiprecision/issues/768.
gcc -v of the local gcc (trunk/14.4 also fails, see the compiler explorer link
above):
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/local/libexec/gcc/x86_64-linux-gnu/16/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ./configure -v --build=x86_64-linux-gnu --disable-cet
--disable-multilib --disable-nls --disable-vtable-verify --disable-libatomic
--enable-checking=release --enable-clocale=gnu --enable-default-pie
--enable-gnu-unique-object --enable-languages=c,c++,fortran,lto
--enable-libstdcxx-debug --enable-libstdcxx-time=yes
--enable-libstdcxx-visibility --enable-link-serialization=2
--enable-linker-build-id --enable-lto --enable-plugin --enable-shared
--enable-symvers=gnu --enable-threads=posix --host=x86_64-linux-gnu
--prefix=/usr/local --program-suffix=-16 --target=x86_64-linux-gnu
--with-abi=m64 --with-arch-32=i686 --with-default-libstdcxx-abi=new
--with-gcc-major-version-only --with-tune=generic --without-cuda-driver
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 16.1.0 (GCC)