This is an archive of the discontinued LLVM Phabricator instance.

[clang][Interp] Implement left and right shifts
ClosedPublic

Authored by tbaeder on Oct 22 2022, 9:21 AM.

Details

Summary

Add a test case based on test/SemaCXX/shift.cpp.

Diff Detail

Event Timeline

tbaeder created this revision.Oct 22 2022, 9:21 AM
Herald added a project: Restricted Project. · View Herald TranscriptOct 22 2022, 9:21 AM
tbaeder requested review of this revision.Oct 22 2022, 9:21 AM
Herald added a project: Restricted Project. · View Herald TranscriptOct 22 2022, 9:21 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript
aaron.ballman added inline comments.Oct 24 2022, 8:13 AM
clang/lib/AST/Interp/Interp.h
1126–1127
1126–1128

Why do Shr and Shl check for a negative shift amount to issue a diagnostic but we check for signed in ShiftLeft to issue a diagnostic? (I would have expected the checks to all live in the same place.)

shafik added inline comments.Oct 24 2022, 5:56 PM
clang/test/AST/Interp/shifts.cpp
58

This is not correct, the operands go through integral promotions first and the result is the promoted type of the left operand see dcl.shift p1.

Also see godbolt: https://godbolt.org/z/7qzKjojMb

71

A negative left operand was made well-formed in C++20 I believe see godbolt: https://godbolt.org/z/7qzKjojMb

My reference from above for expr.shift/p1 also applies.

Although a negative right operand is still UB.

Also note shifting into the sign bit was made well-formed in C++11: https://stackoverflow.com/questions/19593938/is-left-shifting-a-negative-integer-undefined-behavior-in-c11#comment29091986_19593938

shafik added inline comments.Oct 24 2022, 5:58 PM
clang/test/AST/Interp/shifts.cpp
71

Typo, shifting into the sign bit was made well-formed after C++11

tbaeder added inline comments.Oct 25 2022, 12:10 AM
clang/lib/AST/Interp/Interp.h
1126–1128

This was already implemented and I tried to keep as much code as possible, but I'll change that strategy ;)

clang/test/AST/Interp/shifts.cpp
58

Hmm, this is copy-pasted from test/SemaCXX/shift.cpp.

71

This is in line with the test, isn't it? The warning is only for cxx17, the c++20 tests don't expect any output.

tbaeder updated this revision to Diff 470391.Oct 25 2022, 12:35 AM

Excuse the code duplication here, I'll get rid of it as soon as we all agree that the functionality is correct.

aaron.ballman added inline comments.Oct 25 2022, 5:20 AM
clang/test/AST/Interp/shifts.cpp
58

FWIW, I agree with Shafik -- you can also see the casts in the AST: https://godbolt.org/z/97dqr1TEs

71

Also worth keeping in mind: C and C++ differ here.

C2x 6.5.7p4: "... If E1 has a signed type and nonnegative value, and E1 × 2E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined."

shafik added inline comments.Oct 25 2022, 8:11 AM
clang/test/AST/Interp/shifts.cpp
58

This weird, you can see this is indeed valid in a constant expression: https://godbolt.org/z/W33janMxv

but we do obtain that diagnostic and I think I see what it is saying, that the result type is 8bit but the shift is larger than that type even though the operation is being done on the promoted type. I feel like the diagnostic is not great but could be improved to make it better.

wdyt @aaron.ballman

71

Apologies, I missed the diagnostic was only firing for C++17.

aaron.ballman added inline comments.Oct 26 2022, 5:31 AM
clang/test/AST/Interp/shifts.cpp
58

This weird, you can see this is indeed valid in a constant expression: https://godbolt.org/z/W33janMxv

Yes, because of the integral promotion of c to int.

but we do obtain that diagnostic and I think I see what it is saying, that the result type is 8bit but the shift is larger than that type even though the operation is being done on the promoted type. I feel like the diagnostic is not great but could be improved to make it better.

I think it's telling the programmer that something strange is happening here. Yes, it's well-defined because of integral promotions, but it still looks like suspect code. We could say "width of the unpromoted type" but that might make the diagnostic more confusing for non-language lawyer folks.

shafik added inline comments.Oct 26 2022, 9:59 AM
clang/test/AST/Interp/shifts.cpp
58

but we do obtain that diagnostic and I think I see what it is saying, that the result type is 8bit but the shift is larger than that type even though the operation is being done on the promoted type. I feel like the diagnostic is not great but could be improved to make it better.

I think it's telling the programmer that something strange is happening here. Yes, it's well-defined because of integral promotions, but it still looks like suspect code. We could say "width of the unpromoted type" but that might make the diagnostic more confusing for non-language lawyer folks.

I am bothered that the same diagnostic message represents two cases 1) a case which is undefined behavior 2) a case which may be surprising but well defined.

aaron.ballman added inline comments.Oct 26 2022, 10:15 AM
clang/test/AST/Interp/shifts.cpp
58

I am bothered that the same diagnostic message represents two cases 1) a case which is undefined behavior 2) a case which may be surprising but well defined.

That's reasonable, but short of users complaining about significant false positives or asking us to split the situations into two so they can control the difference, I don't think there's anything to be fixed here. The goal of the diagnostic isn't to point out only the UB cases, it's to point out "you're doing something deeply weird, are you sure you mean to do that?" which happens to cover the UB cases too.

tbaeder updated this revision to Diff 471113.Oct 27 2022, 4:00 AM
tbaeder marked 3 inline comments as done.

Added suport for >>= and <<= as well so the test cases for those can be enabled.

tbaeder added inline comments.Oct 27 2022, 4:01 AM
clang/test/AST/Interp/shifts.cpp
58

In any case, I've enabled those test cases and the output with the new interpreter is the same one we get with the current interpreter.

aaron.ballman added inline comments.Oct 27 2022, 5:16 AM
clang/lib/AST/Interp/Interp.h
1143–1162

Should we factor this logic out into a common helper since it's the same between shl and shr? (It could be done as an NFC commit after we land this, too, I don't feel super strongly about it.)

clang/test/AST/Interp/shifts.cpp
7

You can get rid of this entirely and use __CHAR_BIT__ instead.

8

You can get rid of this entirely and use __INT_WIDTH__ instead.

9

No need for this define at all, but fine to leave if you want it for parity with INT_MIN.

117

I'd also appreciate tests showing:

constexpr int i1 = 1 << -1; // ill-formed due to UB
constexpr int i2 = 1 << (WORD_BIT + 1); // ill-formed due to UB
constexpr char c = 1;
constexpr int i3 = c << (CHAR_BIT + 1); // Not ill-formed
tbaeder updated this revision to Diff 471138.Oct 27 2022, 5:39 AM
tbaeder marked 5 inline comments as done.
tbaeder added inline comments.Oct 27 2022, 5:40 AM
clang/lib/AST/Interp/Interp.h
1143–1162

Yes, this is what I meant in my earlier comment with "Excuse the code duplication here"

This revision is now accepted and ready to land.Oct 27 2022, 5:44 AM
shafik accepted this revision.Oct 27 2022, 8:40 PM

LGTM

This revision was landed with ongoing or failed builds.Oct 30 2022, 1:00 AM
This revision was automatically updated to reflect the committed changes.