Bug 52985 - Postincrement not applied after indexing ternary array expression
Summary: Postincrement not applied after indexing ternary array expression
Status: NEW
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 4.6.1
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: wrong-code
Depends on:
Blocks:
 
Reported: 2012-04-14 11:00 UTC by Will Benfold
Modified: 2024-03-28 20:25 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail: 4.7.1, 5.2.1, 7.2.0
Last reconfirmed: 2024-03-14 00:00:00


Attachments
Preprocessed source (68.33 KB, application/x-gzip)
2012-04-14 11:00 UTC, Will Benfold
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Will Benfold 2012-04-14 11:00:41 UTC
Created attachment 27156 [details]
Preprocessed source

[Component=middle-end was a guess, apologies if it's wrong.]

The code below prints an infinite series of zeros, as if the iterator is never incremented.

The problem goes away if:
* the ternary expression is replaced by either tableA or tableB
* 'flag' is set to true
* the iterator increment is performed in a separate statement
* the vector and iterators are replaced with an array and (int *)
* tableA and tableB are replaced by vectors.

If the result of the array indexing is actually used, e.g. printed to cerr, then results are the same at -O1 and above, but I see a segfault with -O0.

----------------------------------------------------------------

#include <iostream>
#include <vector>

int main (int argc, char *argv[])
{
  std::vector<int> v(1);
       
  int tableA[] = { 0 };
  int tableB[] = { 0 };
  
  bool flag = false;
  for (std::vector<int>::const_iterator it = v.begin(); it != v.end();)
  {
    (flag ? tableA : tableB)[*it++];
    std::cerr << (it - v.begin()) << "\n";
  }

  return 0;
}
Comment 1 Richard Biener 2012-04-16 09:42:31 UTC
Confirmed.  The gimplification looks bogus:

                      if (flag != 0) goto <D.24453>; else goto <D.24454>;
                      <D.24453>:
                      D.24018 = __gnu_cxx::__normal_iterator<const int*, std::vector<int> >::operator++ (&it, 0);
                      cleanup.1 = 1;
                      D.24458 = __gnu_cxx::__normal_iterator<const int*, std::vector<int> >::operator* (&D.24018);
                      D.24459 = *D.24458;
                      goto <D.24460>;
                      <D.24454>:
                      D.24461 = __gnu_cxx::__normal_iterator<const int*, std::vector<int> >::operator* (&D.24018);
                      D.24462 = *D.24461;
                      <D.24460>:

it is only incremented on one arm.
Comment 2 Will Benfold 2012-04-18 18:08:16 UTC
Another test case; this one doesn't need any headers and also cuts out the loop.  The exit status should always be 1, but in fact it's 0 if no command-line args are supplied and 1 otherwise.

----------------------------------------------------------------

struct Counter 
{
  Counter (int val = 0)
  : m_val(val)
  {}

  int operator* () const
  {
    return m_val;
  }

  Counter operator++ (int)
  {
    return Counter(m_val++);
  }

  int m_val;
};

int main (int argc, char *[])
{
  int a[] = {0};
  int b[] = {0};

  Counter c;

  ((argc > 1) ? a : b)[*c++];  

  return *c;
}
Comment 3 Roland Wintersteller 2020-01-17 11:30:26 UTC
same with gcc-8/9/?, works with clang
Comment 4 Andrew Pinski 2021-08-02 04:03:38 UTC
        <<cleanup_point <<< Unknown tree: expr_stmt
  if (flag)
    {
      (void) tableA[(int) *__gnu_cxx::__normal_iterator<const int*, std::vector<int> >::operator* (&TARGET_EXPR <D.66535, __gnu_cxx::__normal_iterator<const int*, std::vector<int> >::operator++ (&it, 0)>)];
    }
  else
    {
      (void) tableB[(int) *__gnu_cxx::__normal_iterator<const int*, std::vector<int> >::operator* (&TARGET_EXPR <D.66535, __gnu_cxx::__normal_iterator<const int*, std::vector<int> >::operator++ (&it, 0)>)];
    } >>>>>;

This is totally bogus.  Basically we did:
(flag ? tableA[*it++] : tableB[*it++]);

But then only the *it++ in one of the branches because we only copied the tree and do a deap copy.