https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84684
Jakub Jelinek <jakub at gcc dot gnu.org> changed: What |Removed |Added ---------------------------------------------------------------------------- Keywords| |wrong-code CC| |jason at gcc dot gnu.org Target Milestone|--- |8.0 --- Comment #12 from Jakub Jelinek <jakub at gcc dot gnu.org> --- It seems to be a CONSTRUCTOR sharing issue somewhere. If I put a breakpoint on cxx_eval_constant_expression when t is: union_composition<0, dna4>::value_to_char_helper<4, dna4> (TARGET_EXPR <D.34562, {}>) and then add breakpoint on constexpr.c:1601, the first hit on that breakpoint is that value_to_char_helper call (let's just ignore it) and following 3 calls are with t to_char<dna4> (TARGET_EXPR <D.34725, *(const struct dna4 &) dna4::assign_rank (&alphabet, (int) (uint8_t) i)>) On the first of these 3 I see: (gdb) p debug_generic_stmt (new_call.bindings) alph {._value=0} $217 = void (gdb) p new_call.bindings->list.value $218 = <constructor 0x7fffee727210> and obviously nothing is found in the cache. On the second call I see: (gdb) p debug_generic_stmt (new_call.bindings) alph {._value=1} $219 = void (gdb) p new_call.bindings->list.value $220 = <constructor 0x7fffee7272b8> (gdb) p debug_generic_stmt ($218) {._value=1} and the last case is already a problem, but we are lucky at this iteration, the hashes are different and with luck we don't find the entry. And in the third call I see: (gdb) p debug_generic_stmt (new_call.bindings) alph {._value=2} $222 = void (gdb) p new_call.bindings->list.value $223 = <constructor 0x7fffee727378> (gdb) p debug_generic_stmt ($218) {._value=2} $224 = void (gdb) p debug_generic_stmt ($220) {._value=2} and this time we are unlucky and find the call from the previous iteration. To get this problem far more unlikely, we could in constexpr_call_hasher::equal add: if (lhs == rhs) return 1; + if (lhs->hash != rhs->hash) + return false; if (!constexpr_fundef_hasher::equal (lhs->fundef, rhs->fundef)) return 0; (this function is returning bool, so shouldn't return 0/1 instead of true/false btw), which I think generally is a good thing for performance reasons. But it will not really address the underlying bug. Either whatever modified the $218 and $220 ctors shouldn't really modify them. To reproduce this bug reliably, do: --- gcc/cp/constexpr.c.jj 2018-03-05 13:58:05.475928800 +0100 +++ gcc/cp/constexpr.c 2018-03-05 15:54:06.529016570 +0100 @@ -1598,8 +1598,12 @@ cxx_eval_call_expression (const constexp constexpr_call *entry = NULL; if (depth_ok && !non_constant_args && ctx->strict) { +#if 0 new_call.hash = iterative_hash_template_arg (new_call.bindings, constexpr_fundef_hasher::hash (new_call.fundef)); +#else + new_call.hash = 0; +#endif /* If we have seen this call before, we are done. */ maybe_initialize_constexpr_call_table (); and then it reproduces even on the simplified: typedef decltype (sizeof (0)) size_t; namespace std { template<class _E> struct initializer_list { typedef _E value_type; typedef const _E& reference; typedef const _E& const_reference; typedef size_t size_type; typedef const _E* iterator; typedef const _E* const_iterator; iterator _M_array; size_type _M_len; constexpr initializer_list(const_iterator __a, size_type __l) : _M_array(__a), _M_len(__l) { } constexpr initializer_list() noexcept : _M_array(0), _M_len(0) { } constexpr size_type size() const noexcept { return _M_len; } constexpr const_iterator begin() const noexcept { return _M_array; } constexpr const_iterator end() const noexcept { return begin() + size(); } }; } template <typename E, size_t N> struct array { constexpr E &operator[](size_t n) noexcept { return elems[n]; } constexpr const E &operator[](size_t n) const noexcept { return elems[n]; } constexpr size_t size() const { return N; } E elems[N]; }; template<typename T> constexpr inline T max (std::initializer_list<T> i) { const T *b = i.begin (); const T *e = i.end (); if (b == e) return *b; const T *r = b; while (++b != e) if (*r < *b) r = b; return *r; } template <typename alphabet_type> constexpr char to_char(alphabet_type const alph) { return alph.to_char(); } template <typename ...alphabet_types> struct union_composition { static constexpr size_t value_size = (alphabet_types::value_size + ... ); unsigned char _value; template <size_t fixed_size, typename alphabet_t> static constexpr auto value_to_char_helper(alphabet_t alphabet) { array<char, fixed_size> value_to_char{}; for (size_t i = 0u; i < alphabet_t::value_size; ++i) value_to_char[i] = to_char(alphabet.assign_rank(i)); return value_to_char; } static constexpr auto make_value_to_char() { constexpr auto N = sizeof...(alphabet_types); constexpr array<size_t, N> alphabet_sizes { alphabet_types::value_size... }; constexpr size_t fixed_size = max({alphabet_types::value_size...}); array value_to_char_tables = array<array<char, fixed_size>, N> { value_to_char_helper<fixed_size>(alphabet_types{})... }; array<char, value_size> value_to_char{}; for (size_t i = 0u, value = 0u; i < N; ++i) for (size_t k = 0u; k < alphabet_sizes[i]; ++k, ++value) value_to_char[value] = value_to_char_tables[i][k]; return value_to_char; } }; struct gap { constexpr char to_char() const noexcept { return '-'; } constexpr gap & assign_rank([[maybe_unused]] bool const i) noexcept { return *this; } static constexpr size_t value_size{1}; }; struct dna4 { constexpr char to_char() const noexcept { return value_to_char[_value]; } constexpr dna4 & assign_rank(unsigned char const c) { _value = c; return *this; } static constexpr size_t value_size{4}; static constexpr char value_to_char[value_size] { 'A', 'C', 'G', 'T' }; unsigned char _value; }; struct dna5 { constexpr char to_char() const noexcept { return value_to_char[_value]; } constexpr dna5 & assign_rank(unsigned char const c) { _value = c; return *this; } static constexpr size_t value_size{5}; static constexpr char value_to_char[value_size] { 'A', 'C', 'G', 'T', 'N' }; unsigned char _value; }; constexpr array value_to_char1 = union_composition<dna4>::make_value_to_char(); static_assert(value_to_char1.size() == 4u); static_assert(value_to_char1[0] == 'A'); static_assert(value_to_char1[1] == 'C'); static_assert(value_to_char1[2] == 'G'); static_assert(value_to_char1[3] == 'T'); constexpr array value_to_char2 = union_composition<dna4, gap>::make_value_to_char(); static_assert(value_to_char2.size() == 5u); static_assert(value_to_char2[0] == 'A'); static_assert(value_to_char2[1] == 'C'); static_assert(value_to_char2[2] == 'G'); static_assert(value_to_char2[3] == 'T'); static_assert(value_to_char2[4] == '-'); constexpr array value_to_char3 = union_composition<dna4, gap, dna5>::make_value_to_char(); static_assert(value_to_char3.size() == 10u); static_assert(value_to_char3[0] == 'A'); static_assert(value_to_char3[1] == 'C'); static_assert(value_to_char3[2] == 'G'); static_assert(value_to_char3[3] == 'T'); static_assert(value_to_char3[4] == '-'); static_assert(value_to_char3[5] == 'A'); static_assert(value_to_char3[6] == 'C'); static_assert(value_to_char3[7] == 'G'); static_assert(value_to_char3[8] == 'T'); static_assert(value_to_char3[9] == 'N'); constexpr array value_to_char4 = union_composition<dna5, gap, dna4>::make_value_to_char(); static_assert(value_to_char4.size() == 10u); static_assert(value_to_char4[0] == 'A'); static_assert(value_to_char4[1] == 'C'); static_assert(value_to_char4[2] == 'G'); static_assert(value_to_char4[3] == 'T'); static_assert(value_to_char4[4] == 'N'); static_assert(value_to_char4[5] == '-'); static_assert(value_to_char4[6] == 'A'); static_assert(value_to_char4[7] == 'C'); static_assert(value_to_char4[8] == 'G'); static_assert(value_to_char4[9] == 'T'); constexpr array value_to_char5 = union_composition<gap, dna4, dna5>::make_value_to_char(); static_assert(value_to_char5.size() == 10u); static_assert(value_to_char5[0] == '-'); static_assert(value_to_char5[1] == 'A'); static_assert(value_to_char5[2] == 'C'); static_assert(value_to_char5[3] == 'G'); static_assert(value_to_char5[4] == 'T'); static_assert(value_to_char5[5] == 'A'); static_assert(value_to_char5[6] == 'C'); static_assert(value_to_char5[7] == 'G'); static_assert(value_to_char5[8] == 'T'); static_assert(value_to_char5[9] == 'N'); With that hack I get: pr84684-4.C:111:33: error: static assertion failed static_assert(value_to_char1[1] == 'C'); pr84684-4.C:112:33: error: static assertion failed static_assert(value_to_char1[2] == 'G'); pr84684-4.C:113:33: error: static assertion failed static_assert(value_to_char1[3] == 'T'); pr84684-4.C:118:33: error: static assertion failed static_assert(value_to_char2[1] == 'C'); pr84684-4.C:119:33: error: static assertion failed static_assert(value_to_char2[2] == 'G'); pr84684-4.C:120:33: error: static assertion failed static_assert(value_to_char2[3] == 'T'); pr84684-4.C:126:33: error: static assertion failed static_assert(value_to_char3[1] == 'C'); pr84684-4.C:127:33: error: static assertion failed static_assert(value_to_char3[2] == 'G'); pr84684-4.C:128:33: error: static assertion failed static_assert(value_to_char3[3] == 'T'); pr84684-4.C:131:33: error: static assertion failed static_assert(value_to_char3[6] == 'C'); pr84684-4.C:132:33: error: static assertion failed static_assert(value_to_char3[7] == 'G'); pr84684-4.C:133:33: error: static assertion failed static_assert(value_to_char3[8] == 'T'); pr84684-4.C:134:33: error: static assertion failed static_assert(value_to_char3[9] == 'N'); pr84684-4.C:139:33: error: static assertion failed static_assert(value_to_char4[1] == 'C'); pr84684-4.C:140:33: error: static assertion failed static_assert(value_to_char4[2] == 'G'); pr84684-4.C:141:33: error: static assertion failed static_assert(value_to_char4[3] == 'T'); pr84684-4.C:142:33: error: static assertion failed static_assert(value_to_char4[4] == 'N'); pr84684-4.C:145:33: error: static assertion failed static_assert(value_to_char4[7] == 'C'); pr84684-4.C:146:33: error: static assertion failed static_assert(value_to_char4[8] == 'G'); pr84684-4.C:147:33: error: static assertion failed static_assert(value_to_char4[9] == 'T'); pr84684-4.C:153:33: error: static assertion failed static_assert(value_to_char5[2] == 'C'); pr84684-4.C:154:33: error: static assertion failed static_assert(value_to_char5[3] == 'G'); pr84684-4.C:155:33: error: static assertion failed static_assert(value_to_char5[4] == 'T'); pr84684-4.C:157:33: error: static assertion failed static_assert(value_to_char5[6] == 'C'); pr84684-4.C:158:33: error: static assertion failed static_assert(value_to_char5[7] == 'G'); pr84684-4.C:159:33: error: static assertion failed static_assert(value_to_char5[8] == 'T'); pr84684-4.C:160:33: error: static assertion failed static_assert(value_to_char5[9] == 'N');