https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126483
Patrick Palka <ppalka at gcc dot gnu.org> changed:
What |Removed |Added
----------------------------------------------------------------------------
Target Milestone|--- |13.5
Known to fail| |13.4.0
CC| |ppalka at gcc dot gnu.org
Last reconfirmed| |2026-08-04
Known to work| |12.5.0
Summary|Wrong active union member |[13/14/15/16/17 Regression]
|in constant evaluation of |Wrong active union member
|variable template |in constant evaluation of
|initializers |variable template
| |initializers
Ever confirmed|0 |1
Status|UNCONFIRMED |NEW
--- Comment #1 from Patrick Palka <ppalka at gcc dot gnu.org> ---
Started with r13-6422 "c++: more mce_false folding from cp_fully_fold_init
[PR108243]".
Initial reduction:
namespace boost {
namespace multiprecision {
enum expression_template_option { et_off };
template <class Backend, expression_template_option> struct number {
using self_type = number;
template <class V> constexpr number(V) : m_backend(0) {}
constexpr void operator+=(self_type val) { do_add(val, int()); }
template <class Exp> constexpr void do_add(Exp, int) {
Backend __trans_tmp_2 = 0;
eval_add(m_backend, __trans_tmp_2);
}
Backend m_backend;
};
enum cpp_integer_type { unsigned_magnitude };
enum cpp_int_check_type { unchecked };
namespace backends {
template <unsigned long, unsigned long, cpp_integer_type, cpp_int_check_type,
class = int>
struct cpp_int_backend;
}
using backends::cpp_int_backend;
template <class Backend, expression_template_option ExpressionTemplates,
class Arithmetic>
constexpr bool operator==(number<Backend, ExpressionTemplates>, Arithmetic) {
Backend __trans_tmp_1 = 0;
return eval_eq(__trans_tmp_1);
}
namespace backends {
template <unsigned long, unsigned long, bool> struct cpp_int_base;
template <class> struct max_precision;
template <unsigned long MinBits, unsigned long MaxBits,
cpp_integer_type SignType, cpp_int_check_type Checked,
class Allocator>
struct max_precision<
cpp_int_backend<MinBits, MaxBits, SignType, Checked, Allocator>> {
static constexpr long value = MaxBits;
};
template <class> struct min_precision;
template <unsigned long MinBits, unsigned long MaxBits,
cpp_integer_type SignType, cpp_int_check_type Checked,
class Allocator>
struct min_precision<
cpp_int_backend<MinBits, MaxBits, SignType, Checked, Allocator>> {
static constexpr long value = MinBits;
};
constexpr bool value = false;
constexpr long limb_bits = sizeof(long);
template <unsigned long MinBits> struct cpp_int_base<MinBits, MinBits, false> {
union data_type {
unsigned long long m_data[limb_bits];
__int128 m_double_first_limb;
constexpr data_type(long, long) : m_data{} {}
constexpr data_type(__int128) : m_double_first_limb() {
if (__builtin_is_constant_evaluated()) {
data_type t(0, limb_bits);
*this = t;
}
}
} m_wrapper;
long m_limbs;
constexpr cpp_int_base(__int128 i) : m_wrapper(i), m_limbs() {}
constexpr unsigned long long *limbs() { return m_wrapper.m_data; }
};
template <unsigned long MinBits, unsigned long MaxBits,
cpp_integer_type SignType, cpp_int_check_type Checked, class>
struct cpp_int_backend
: cpp_int_base<
min_precision<cpp_int_backend<MinBits, 1, SignType, Checked>>::value,
max_precision<cpp_int_backend<1, MaxBits, SignType, Checked>>::value,
value> {
using self_type = cpp_int_backend;
template <class Arg>
constexpr cpp_int_backend(Arg i)
: cpp_int_base<min_precision<self_type>::value,
max_precision<self_type>::value, value>(i) {}
};
template <unsigned long MinBits, unsigned long MaxBits,
cpp_int_check_type Checked, class Allocator>
constexpr bool eval_eq(
cpp_int_backend<MinBits, MaxBits, unsigned_magnitude, Checked, Allocator>
a) {
return *a.limbs() == 0;
}
template <class CppInt1, class CppInt2, class CppInt3>
constexpr void add_unsigned_constexpr(CppInt1, CppInt2 a, CppInt3 b) {
*a.limbs() > *b.limbs();
}
template <class CppInt1, class CppInt2, class CppInt3>
constexpr void add_unsigned(CppInt1 result, CppInt2 a, CppInt3 b) {
add_unsigned_constexpr(result, a, b);
}
template <unsigned long MinBits1, unsigned long MaxBits1,
cpp_integer_type SignType1, cpp_int_check_type Checked1,
class Allocator1, unsigned long MinBits2, unsigned long MaxBits2,
cpp_integer_type SignType2, cpp_int_check_type Checked2,
class Allocator2>
constexpr void eval_add(
cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>
result,
cpp_int_backend<MinBits2, MaxBits2, SignType2, Checked2, Allocator2> o) {
eval_add(result, result, o);
}
template <unsigned long MinBits1, unsigned long MaxBits1,
cpp_integer_type SignType1, cpp_int_check_type Checked1,
class Allocator1, unsigned long MinBits2, unsigned long MaxBits2,
cpp_integer_type SignType2, cpp_int_check_type Checked2,
class Allocator2, unsigned long MinBits3, unsigned long MaxBits3,
cpp_integer_type SignType3, cpp_int_check_type Checked3,
class Allocator3>
constexpr void eval_add(
cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>
result,
cpp_int_backend<MinBits2, MaxBits2, SignType2, Checked2, Allocator2> a,
cpp_int_backend<MinBits3, MaxBits3, SignType3, Checked3, Allocator3> b) {
add_unsigned(result, a, b);
}
} // namespace backends
} // namespace multiprecision
} // namespace boost
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;
}();
static_assert(vt<0> == 0);