15

I read in the C++17 Standard $8.5.7.4:

The expression E1 is sequenced before the expression E2.

for shift operators.

Also cppreference rule 19 says:

In a shift operator expression E1<<E2 and E1>>E2, every value
computation and side-effect of E1 is sequenced before every value
computation and side effect of E2

But when I try to compile the following code with gcc 7.3.0 or clang 6.0.0

#include <iostream>
using namespace std;

int main() {
    int i = 5;
    cout << (i++ << i) << endl;
    return 0;
}

I get the following gcc warning:

../src/Cpp_shift.cpp: In function ‘int main()’:
../src/Cpp_shift.cpp:6:12: warning: operation on ‘i’ may be undefined [-Wsequence-point]
  cout << (i++ << i) << endl;
           ~^~

The clang warning is:

warning: unsequenced modification and access to 'i' [-Wunsequenced]

I used the following commands to compile:

g++ -std=c++17 ../src/Cpp_shift.cpp -o Cpp_shift -Wall
clang++ -std=c++17 ../src/Cpp_shift.cpp -o Cpp_shift -Wall

I get the expected 320 as output in both cases ( 5 * 2 ^ 6 )

Can someone explain why I get this warning? Did I overlook something? I also read this related question, but it does not answer my question.

edit: all other variants ++i << i, i << ++i and i << i++ result in the same warning.

edit2: (i << ++i) results in 320 for clang (correct) and 384 for gcc (incorrect). It seems that gcc gives a wrong result if the ++ is at E2, (i << i++) also gives a wrong result.

mch
  • 8,070
  • 2
  • 24
  • 39
  • 1
    Looks like the diagnostic is created by logic using old rules. I'm not sure when the sequencing of `< – Cheers and hth. - Alf Aug 10 '18 at 10:59
  • @Cheersandhth.-Alf I looked it up, the sentence was added with the C++17 Standard. – mch Aug 10 '18 at 11:02
  • the only ubsan checks clang puts in are add overflow and shift out of bounds. I'm not sure if that's relevant or not. It's surprising that both compilers give nearly identical messages in the same situation - usually both wouldn't be wrong. It's also interesting that gcc says that it **may** be undefined. – xaxxon Aug 10 '18 at 11:04
  • say in *CL* will be `160` in case 5. the `++` executed after < – RbMm Aug 10 '18 at 11:11
  • @RbMm what is cl? – xaxxon Aug 10 '18 at 11:12
  • *CL.EXE* microsoft c++ compiler. - just test it.https://godbolt.org/g/1tNmsK – RbMm Aug 10 '18 at 11:14
  • with *CL* `i++ << i` is execute as `i << i; i++` – RbMm Aug 10 '18 at 11:15
  • for return (i++ << i); I get different results between clang and gcc - 320 vs 160: https://godbolt.org/g/69La4v – xaxxon Aug 10 '18 at 11:15
  • really think bad thing use such ub (by sense) expressions. – RbMm Aug 10 '18 at 11:16
  • say compare for gcc 4.7.4 (https://godbolt.org/g/UPsE2i ) with gcc 4.8.1 (https://godbolt.org/g/tJ6aGV) anf higher – RbMm Aug 10 '18 at 11:20
  • When I compile that code with `g++ -std=c++17 ../src/Cpp_shift.cpp -o Cpp_shift -Wall` I got that warning too, but when I compile without the `-Wall` flag, I didn't get any warning at all. It might be a coding warning of this flag. So, because it's not an undefined behavior of c++17, the problem is probably with this flag implementation. – CoralK Aug 10 '18 at 11:20
  • 1
    @KorelK when I remove `-Wall` I still get the same wrong result with gcc for `(i << ++i)`. clang gives the warning also without `-Wall`. – mch Aug 10 '18 at 11:21
  • @mch There is no doubt that there is an implementation bug in gcc, but the warning occured due to this flag. – CoralK Aug 10 '18 at 11:24
  • @RbMm, The version of MSVC on CE isn't up to date and there have been conformance changes recently, so it's possible that the latest version does conform. – chris Aug 10 '18 at 11:26
  • @chris - i test on latest cl builds – RbMm Aug 10 '18 at 11:29
  • 1
    @xaxxon gcc give 320 without `-fsanitize=undefined` and 160 with it – Tyker Aug 10 '18 at 11:47
  • 1
    Duplicate question: https://stackoverflow.com/questions/51550156/ – M.M Aug 10 '18 at 11:49
  • @Tyker so weird. https://godbolt.org/g/7iCRdE – xaxxon Aug 10 '18 at 11:54
  • 1
    C++17 sequencing is not quite implemented by GCC yet. Problem of the same nature in GCC with assignment operator: https://stackoverflow.com/questions/51511102/c17-sequencing-in-assignment-still-not-implemented-in-gcc – AnT Aug 10 '18 at 18:53
  • Possible duplicate of [C++: 'cout << pointer << ++pointer' generates a compiler warning](https://stackoverflow.com/questions/51550156/c-cout-pointer-pointer-generates-a-compiler-warning) – Didier L Aug 18 '18 at 17:34
  • @DidierL not exactly, it is also about the wrong result produced by gcc. – mch Aug 20 '18 at 07:47

1 Answers1

8

Standard is clear about the order of evaluation of the operands of the shift operator.

n4659 - §8.8 (p4):

The expression E1 is sequenced before the expression E2.

There is no undefined behavior in the expression i++ << i, it is well defined. It is a bug in Clang and GCC both.

haccks
  • 97,141
  • 23
  • 153
  • 244
  • 4
    To be explicit: bug as in poor quality diagnostic, not necessarily bug as in non-conformance. The actual runtime behaviour could be / have been fixed to conform to C++17 before the diagnostic got updated. –  Aug 10 '18 at 11:04
  • @hvd it seems that only the clang runtime behaviour is fixed, gcc gives wrong results if the increment is in the second expression. – mch Aug 10 '18 at 11:12
  • 1
    @hvd; Now OP has updated the question, I can bet this is a bug in GCC. – haccks Aug 10 '18 at 11:13
  • 1
    Interesting. GCC themselves claim on [C++ Standards Support in GCC](https://gcc.gnu.org/projects/cxx-status.html) that the new behaviour was implemented in GCC 7. –  Aug 10 '18 at 11:15
  • 2
    It's surprising that they would both be wrong about a pretty fundamentally easy-to-test change like this - not just the diagnostic but the actual results. – xaxxon Aug 10 '18 at 11:40
  • @xaxxon; Sometimes what seems "easy" becomes "difficult" ;) – haccks Aug 10 '18 at 11:44
  • 1
    The examples in comments under the main question show that g++ actually implements the sequencing incorrectly, it is not just a bogus warning – M.M Aug 10 '18 at 11:56
  • @xaxxon Clang development has similar troubles. It is reported that Clang supports c++14 template variable while a static template constexpr member variable instantiation make clang crash since v3.9 and as never been fixed! Maybe the increasing language complexity causes compiler code to be less and less fixable. – Oliv Aug 10 '18 at 14:51
  • @Oliv do you have a link to the bug report? – xaxxon Aug 10 '18 at 15:56
  • @xaxxon I suspect a legitimate suspicious' suspicion:[bug 38247](https://bugs.llvm.org/show_bug.cgi?id=38247). You ll find many others similar bugs related to template if you are curious. – Oliv Aug 10 '18 at 16:27
  • @Oliv looks like it's been addressed in '17? https://godbolt.org/g/rJFzwu – xaxxon Aug 10 '18 at 17:03
  • @xaxxon No it has not been addressed, as shown in your godbolt's link. The right output should be the same as gcc. Whatsoever, clang crashes at the third static assertion. – Oliv Aug 10 '18 at 18:08