[Bug c++/84684] inserting random code / flags produces wrong code

jakub at gcc dot gnu.org gcc-bugzilla@gcc.gnu.org
Mon Mar 5 14:58:00 GMT 2018


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');


More information about the Gcc-bugs mailing list