@@ -127,17 +127,29 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
127
127
128
128
// Shift ops can have an RHS with a different numeric type.
129
129
if bin_op == Shl || bin_op == Shr {
130
- let signed = left_layout. abi . is_signed ( ) ;
131
130
let size = u128:: from ( left_layout. size . bits ( ) ) ;
131
+ // Even if `r` is signed, we treat it as if it was unsigned (i.e., we use its
132
+ // zero-extended form). This matches the codegen backend:
133
+ // <https://github.com/rust-lang/rust/blob/c274e4969f058b1c644243181ece9f829efa7594/compiler/rustc_codegen_ssa/src/base.rs#L315-L317>.
134
+ // The overflow check is also ignorant to the sign:
135
+ // <https://github.com/rust-lang/rust/blob/c274e4969f058b1c644243181ece9f829efa7594/compiler/rustc_codegen_ssa/src/mir/rvalue.rs#L728>.
136
+ // This would behave rather strangely if we had integer types of size 256: a shift by
137
+ // -1i8 would actually shift by 255, but that would *not* be considered overflowing. A
138
+ // shift by -1i16 though would be considered overflowing. If we had integers of size
139
+ // 512, then a shift by -1i8 would even produce a different result than one by -1i16:
140
+ // the first shifts by 255, the latter by u16::MAX % 512 = 511. Lucky enough, our
141
+ // integers are maximally 128bits wide, so negative shifts *always* overflow and we have
142
+ // consistent results for the same value represented at different bit widths.
143
+ assert ! ( size <= 128 ) ;
132
144
let overflow = r >= size;
133
145
// The shift offset is implicitly masked to the type size, to make sure this operation
134
146
// is always defined. This is the one MIR operator that does *not* directly map to a
135
147
// single LLVM operation. See
136
- // <https://github.com/rust-lang/rust/blob/a3b9405ae7bb6ab4e8103b414e75c44598a10fd2 /compiler/rustc_codegen_ssa/src/common.rs#L131-L158>
148
+ // <https://github.com/rust-lang/rust/blob/c274e4969f058b1c644243181ece9f829efa7594 /compiler/rustc_codegen_ssa/src/common.rs#L131-L158>
137
149
// for the corresponding truncation in our codegen backends.
138
150
let r = r % size;
139
151
let r = u32:: try_from ( r) . unwrap ( ) ; // we masked so this will always fit
140
- let result = if signed {
152
+ let result = if left_layout . abi . is_signed ( ) {
141
153
let l = self . sign_extend ( l, left_layout) as i128 ;
142
154
let result = match bin_op {
143
155
Shl => l. checked_shl ( r) . unwrap ( ) ,
0 commit comments