@@ -3,10 +3,13 @@ use rustc_middle::mir;
3
3
use rustc_middle:: mir:: interpret:: { InterpResult , Scalar } ;
4
4
use rustc_middle:: ty:: layout:: { LayoutOf , TyAndLayout } ;
5
5
use rustc_middle:: ty:: { self , FloatTy , Ty } ;
6
+ use rustc_span:: symbol:: sym;
6
7
use rustc_target:: abi:: Abi ;
7
8
8
9
use super :: { ImmTy , Immediate , InterpCx , Machine , PlaceTy } ;
9
10
11
+ use crate :: fluent_generated as fluent;
12
+
10
13
impl < ' mir , ' tcx : ' mir , M : Machine < ' mir , ' tcx > > InterpCx < ' mir , ' tcx , M > {
11
14
/// Applies the binary operation `op` to the two operands and writes a tuple of the result
12
15
/// and a boolean signifying the potential overflow to the destination.
@@ -139,8 +142,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
139
142
) -> InterpResult < ' tcx , ( Scalar < M :: Provenance > , bool , Ty < ' tcx > ) > {
140
143
use rustc_middle:: mir:: BinOp :: * ;
141
144
145
+ let throw_ub_on_overflow = match bin_op {
146
+ AddUnchecked => Some ( sym:: unchecked_add) ,
147
+ SubUnchecked => Some ( sym:: unchecked_sub) ,
148
+ MulUnchecked => Some ( sym:: unchecked_mul) ,
149
+ ShlUnchecked => Some ( sym:: unchecked_shl) ,
150
+ ShrUnchecked => Some ( sym:: unchecked_shr) ,
151
+ _ => None ,
152
+ } ;
153
+
142
154
// Shift ops can have an RHS with a different numeric type.
143
- if bin_op == Shl || bin_op == Shr {
155
+ if matches ! ( bin_op, Shl | ShlUnchecked | Shr | ShrUnchecked ) {
144
156
let size = u128:: from ( left_layout. size . bits ( ) ) ;
145
157
// Even if `r` is signed, we treat it as if it was unsigned (i.e., we use its
146
158
// zero-extended form). This matches the codegen backend:
@@ -155,6 +167,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
155
167
// integers are maximally 128bits wide, so negative shifts *always* overflow and we have
156
168
// consistent results for the same value represented at different bit widths.
157
169
assert ! ( size <= 128 ) ;
170
+ let original_r = r;
158
171
let overflow = r >= size;
159
172
// The shift offset is implicitly masked to the type size, to make sure this operation
160
173
// is always defined. This is the one MIR operator that does *not* directly map to a
@@ -166,19 +179,28 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
166
179
let result = if left_layout. abi . is_signed ( ) {
167
180
let l = self . sign_extend ( l, left_layout) as i128 ;
168
181
let result = match bin_op {
169
- Shl => l. checked_shl ( r) . unwrap ( ) ,
170
- Shr => l. checked_shr ( r) . unwrap ( ) ,
182
+ Shl | ShlUnchecked => l. checked_shl ( r) . unwrap ( ) ,
183
+ Shr | ShrUnchecked => l. checked_shr ( r) . unwrap ( ) ,
171
184
_ => bug ! ( ) ,
172
185
} ;
173
186
result as u128
174
187
} else {
175
188
match bin_op {
176
- Shl => l. checked_shl ( r) . unwrap ( ) ,
177
- Shr => l. checked_shr ( r) . unwrap ( ) ,
189
+ Shl | ShlUnchecked => l. checked_shl ( r) . unwrap ( ) ,
190
+ Shr | ShrUnchecked => l. checked_shr ( r) . unwrap ( ) ,
178
191
_ => bug ! ( ) ,
179
192
}
180
193
} ;
181
194
let truncated = self . truncate ( result, left_layout) ;
195
+
196
+ if overflow && let Some ( intrinsic_name) = throw_ub_on_overflow {
197
+ throw_ub_custom ! (
198
+ fluent:: const_eval_overflow_shift,
199
+ val = original_r,
200
+ name = intrinsic_name
201
+ ) ;
202
+ }
203
+
182
204
return Ok ( ( Scalar :: from_uint ( truncated, left_layout. size ) , overflow, left_layout. ty ) ) ;
183
205
}
184
206
@@ -216,9 +238,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
216
238
Rem if r == 0 => throw_ub ! ( RemainderByZero ) ,
217
239
Div => Some ( i128:: overflowing_div) ,
218
240
Rem => Some ( i128:: overflowing_rem) ,
219
- Add => Some ( i128:: overflowing_add) ,
220
- Sub => Some ( i128:: overflowing_sub) ,
221
- Mul => Some ( i128:: overflowing_mul) ,
241
+ Add | AddUnchecked => Some ( i128:: overflowing_add) ,
242
+ Sub | SubUnchecked => Some ( i128:: overflowing_sub) ,
243
+ Mul | MulUnchecked => Some ( i128:: overflowing_mul) ,
222
244
_ => None ,
223
245
} ;
224
246
if let Some ( op) = op {
@@ -242,11 +264,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
242
264
// If that truncation loses any information, we have an overflow.
243
265
let result = result as u128 ;
244
266
let truncated = self . truncate ( result, left_layout) ;
245
- return Ok ( (
246
- Scalar :: from_uint ( truncated , size ) ,
247
- oflo || self . sign_extend ( truncated , left_layout ) != result ,
248
- left_layout . ty ,
249
- ) ) ;
267
+ let overflow = oflo || self . sign_extend ( truncated , left_layout ) != result ;
268
+ if overflow && let Some ( intrinsic_name ) = throw_ub_on_overflow {
269
+ throw_ub_custom ! ( fluent :: const_eval_overflow , name = intrinsic_name ) ;
270
+ }
271
+ return Ok ( ( Scalar :: from_uint ( truncated , size ) , overflow , left_layout . ty ) ) ;
250
272
}
251
273
}
252
274
@@ -263,12 +285,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
263
285
BitAnd => ( Scalar :: from_uint ( l & r, size) , left_layout. ty ) ,
264
286
BitXor => ( Scalar :: from_uint ( l ^ r, size) , left_layout. ty ) ,
265
287
266
- Add | Sub | Mul | Rem | Div => {
288
+ Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Rem | Div => {
267
289
assert ! ( !left_layout. abi. is_signed( ) ) ;
268
290
let op: fn ( u128 , u128 ) -> ( u128 , bool ) = match bin_op {
269
- Add => u128:: overflowing_add,
270
- Sub => u128:: overflowing_sub,
271
- Mul => u128:: overflowing_mul,
291
+ Add | AddUnchecked => u128:: overflowing_add,
292
+ Sub | SubUnchecked => u128:: overflowing_sub,
293
+ Mul | MulUnchecked => u128:: overflowing_mul,
272
294
Div if r == 0 => throw_ub ! ( DivisionByZero ) ,
273
295
Rem if r == 0 => throw_ub ! ( RemainderByZero ) ,
274
296
Div => u128:: overflowing_div,
@@ -279,11 +301,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
279
301
// Truncate to target type.
280
302
// If that truncation loses any information, we have an overflow.
281
303
let truncated = self . truncate ( result, left_layout) ;
282
- return Ok ( (
283
- Scalar :: from_uint ( truncated , size ) ,
284
- oflo || truncated != result ,
285
- left_layout . ty ,
286
- ) ) ;
304
+ let overflow = oflo || truncated != result ;
305
+ if overflow && let Some ( intrinsic_name ) = throw_ub_on_overflow {
306
+ throw_ub_custom ! ( fluent :: const_eval_overflow , name = intrinsic_name ) ;
307
+ }
308
+ return Ok ( ( Scalar :: from_uint ( truncated , size ) , overflow , left_layout . ty ) ) ;
287
309
}
288
310
289
311
_ => span_bug ! (
0 commit comments