@@ -3,10 +3,13 @@ use rustc_middle::mir;
33use rustc_middle:: mir:: interpret:: { InterpResult , Scalar } ;
44use rustc_middle:: ty:: layout:: { LayoutOf , TyAndLayout } ;
55use rustc_middle:: ty:: { self , FloatTy , Ty } ;
6+ use rustc_span:: symbol:: sym;
67use rustc_target:: abi:: Abi ;
78
89use super :: { ImmTy , Immediate , InterpCx , Machine , PlaceTy } ;
910
11+ use crate :: fluent_generated as fluent;
12+
1013impl < ' mir , ' tcx : ' mir , M : Machine < ' mir , ' tcx > > InterpCx < ' mir , ' tcx , M > {
1114 /// Applies the binary operation `op` to the two operands and writes a tuple of the result
1215 /// 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> {
139142 ) -> InterpResult < ' tcx , ( Scalar < M :: Provenance > , bool , Ty < ' tcx > ) > {
140143 use rustc_middle:: mir:: BinOp :: * ;
141144
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+
142154 // 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 ) {
144156 let size = u128:: from ( left_layout. size . bits ( ) ) ;
145157 // Even if `r` is signed, we treat it as if it was unsigned (i.e., we use its
146158 // zero-extended form). This matches the codegen backend:
@@ -155,6 +167,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
155167 // integers are maximally 128bits wide, so negative shifts *always* overflow and we have
156168 // consistent results for the same value represented at different bit widths.
157169 assert ! ( size <= 128 ) ;
170+ let original_r = r;
158171 let overflow = r >= size;
159172 // The shift offset is implicitly masked to the type size, to make sure this operation
160173 // 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> {
166179 let result = if left_layout. abi . is_signed ( ) {
167180 let l = self . sign_extend ( l, left_layout) as i128 ;
168181 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 ( ) ,
171184 _ => bug ! ( ) ,
172185 } ;
173186 result as u128
174187 } else {
175188 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 ( ) ,
178191 _ => bug ! ( ) ,
179192 }
180193 } ;
181194 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+
182204 return Ok ( ( Scalar :: from_uint ( truncated, left_layout. size ) , overflow, left_layout. ty ) ) ;
183205 }
184206
@@ -216,9 +238,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
216238 Rem if r == 0 => throw_ub ! ( RemainderByZero ) ,
217239 Div => Some ( i128:: overflowing_div) ,
218240 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) ,
222244 _ => None ,
223245 } ;
224246 if let Some ( op) = op {
@@ -242,11 +264,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
242264 // If that truncation loses any information, we have an overflow.
243265 let result = result as u128 ;
244266 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 ) ) ;
250272 }
251273 }
252274
@@ -263,12 +285,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
263285 BitAnd => ( Scalar :: from_uint ( l & r, size) , left_layout. ty ) ,
264286 BitXor => ( Scalar :: from_uint ( l ^ r, size) , left_layout. ty ) ,
265287
266- Add | Sub | Mul | Rem | Div => {
288+ Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Rem | Div => {
267289 assert ! ( !left_layout. abi. is_signed( ) ) ;
268290 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,
272294 Div if r == 0 => throw_ub ! ( DivisionByZero ) ,
273295 Rem if r == 0 => throw_ub ! ( RemainderByZero ) ,
274296 Div => u128:: overflowing_div,
@@ -279,11 +301,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
279301 // Truncate to target type.
280302 // If that truncation loses any information, we have an overflow.
281303 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 ) ) ;
287309 }
288310
289311 _ => span_bug ! (
0 commit comments