Shift operands sequenced in C++17
Asked Answered
I

1

17

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, 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.

Isocline answered 10/8, 2018 at 10:48 Comment(21)
Looks like the diagnostic is created by logic using old rules. I'm not sure when the sequencing of << was introduced, but possibly it wasn't until C++14 or C++17. As it is it's clearly a bug, which ideally should be reported.Pupa
@Cheersandhth.-Alf I looked it up, the sentence was added with the C++17 Standard.Isocline
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.Trivalent
say in CL will be 160 in case 5. the ++ executed after <<Sickness
@Sickness what is cl?Trivalent
CL.EXE microsoft c++ compiler. - just test it.godbolt.org/g/1tNmsKSickness
with CL i++ << i is execute as i << i; i++Sickness
for return (i++ << i); I get different results between clang and gcc - 320 vs 160: godbolt.org/g/69La4vTrivalent
really think bad thing use such ub (by sense) expressions.Sickness
say compare for gcc 4.7.4 (godbolt.org/g/UPsE2i ) with gcc 4.8.1 (godbolt.org/g/tJ6aGV) anf higherSickness
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.Mages
@KorelK when I remove -Wall I still get the same wrong result with gcc for (i << ++i). clang gives the warning also without -Wall.Isocline
@Isocline There is no doubt that there is an implementation bug in gcc, but the warning occured due to this flag.Mages
@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.Ditheism
@Ditheism - i test on latest cl buildsSickness
@Trivalent gcc give 320 without -fsanitize=undefined and 160 with itDamascene
Duplicate question: stackoverflow.com/questions/51550156Fonteyn
@Damascene so weird. godbolt.org/g/7iCRdETrivalent
C++17 sequencing is not quite implemented by GCC yet. Problem of the same nature in GCC with assignment operator: #51511602Royroyal
Possible duplicate of C++: 'cout << pointer << ++pointer' generates a compiler warningJahveh
@DidierL not exactly, it is also about the wrong result produced by gcc.Isocline
S
10

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.

Sin answered 10/8, 2018 at 11:1 Comment(12)
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.Garthgartner
@hvd it seems that only the clang runtime behaviour is fixed, gcc gives wrong results if the increment is in the second expression.Isocline
@hvd; Now OP has updated the question, I can bet this is a bug in GCC.Sin
Interesting. GCC themselves claim on C++ Standards Support in GCC that the new behaviour was implemented in GCC 7.Garthgartner
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.Trivalent
@xaxxon; Sometimes what seems "easy" becomes "difficult" ;)Sin
The examples in comments under the main question show that g++ actually implements the sequencing incorrectly, it is not just a bogus warningFonteyn
@Trivalent 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.Willenewillet
@Willenewillet do you have a link to the bug report?Trivalent
@Trivalent I suspect a legitimate suspicious' suspicion:bug 38247. You ll find many others similar bugs related to template if you are curious.Willenewillet
@Willenewillet looks like it's been addressed in '17? godbolt.org/g/rJFzwuTrivalent
@Trivalent 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.Willenewillet

© 2022 - 2025 — McMap. All rights reserved.