]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_const_eval/src/interpret/operator.rs
New upstream version 1.74.1+dfsg1
[rustc.git] / compiler / rustc_const_eval / src / interpret / operator.rs
CommitLineData
dfeec247 1use rustc_apfloat::Float;
ba9703b0
XL
2use rustc_middle::mir;
3use rustc_middle::mir::interpret::{InterpResult, Scalar};
781aab86 4use rustc_middle::ty::layout::TyAndLayout;
c295e0f8 5use rustc_middle::ty::{self, FloatTy, Ty};
fe692bf9 6use rustc_span::symbol::sym;
064997fb 7use rustc_target::abi::Abi;
ea8adc8c 8
dfeec247 9use super::{ImmTy, Immediate, InterpCx, Machine, PlaceTy};
ea8adc8c 10
fe692bf9
FG
11use crate::fluent_generated as fluent;
12
ba9703b0 13impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
ea8adc8c
XL
14 /// Applies the binary operation `op` to the two operands and writes a tuple of the result
15 /// and a boolean signifying the potential overflow to the destination.
b7449926 16 pub fn binop_with_overflow(
ea8adc8c
XL
17 &mut self,
18 op: mir::BinOp,
064997fb
FG
19 left: &ImmTy<'tcx, M::Provenance>,
20 right: &ImmTy<'tcx, M::Provenance>,
21 dest: &PlaceTy<'tcx, M::Provenance>,
dc9dc135 22 ) -> InterpResult<'tcx> {
781aab86 23 let (val, overflowed) = self.overflowing_binary_op(op, &left, &right)?;
e1599b0c 24 debug_assert_eq!(
781aab86 25 Ty::new_tup(self.tcx.tcx, &[val.layout.ty, self.tcx.types.bool]),
e1599b0c 26 dest.layout.ty,
add651ee 27 "type mismatch for result of {op:?}",
e1599b0c 28 );
064997fb
FG
29 // Write the result to `dest`.
30 if let Abi::ScalarPair(..) = dest.layout.abi {
31 // We can use the optimized path and avoid `place_field` (which might do
32 // `force_allocation`).
781aab86 33 let pair = Immediate::ScalarPair(val.to_scalar(), Scalar::from_bool(overflowed));
064997fb
FG
34 self.write_immediate(pair, dest)?;
35 } else {
36 assert!(self.tcx.sess.opts.unstable_opts.randomize_layout);
37 // With randomized layout, `(int, bool)` might cease to be a `ScalarPair`, so we have to
38 // do a component-wise write here. This code path is slower than the above because
39 // `place_field` will have to `force_allocate` locals here.
add651ee 40 let val_field = self.project_field(dest, 0)?;
781aab86 41 self.write_scalar(val.to_scalar(), &val_field)?;
add651ee 42 let overflowed_field = self.project_field(dest, 1)?;
064997fb
FG
43 self.write_scalar(Scalar::from_bool(overflowed), &overflowed_field)?;
44 }
45 Ok(())
ea8adc8c
XL
46 }
47
48 /// Applies the binary operation `op` to the arguments and writes the result to the
b7449926
XL
49 /// destination.
50 pub fn binop_ignore_overflow(
ea8adc8c
XL
51 &mut self,
52 op: mir::BinOp,
064997fb
FG
53 left: &ImmTy<'tcx, M::Provenance>,
54 right: &ImmTy<'tcx, M::Provenance>,
55 dest: &PlaceTy<'tcx, M::Provenance>,
dc9dc135 56 ) -> InterpResult<'tcx> {
781aab86
FG
57 let val = self.wrapping_binary_op(op, left, right)?;
58 assert_eq!(val.layout.ty, dest.layout.ty, "type mismatch for result of {op:?}");
59 self.write_immediate(*val, dest)
ea8adc8c
XL
60 }
61}
62
ba9703b0 63impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
b7449926 64 fn binary_char_op(
ea8adc8c
XL
65 &self,
66 bin_op: mir::BinOp,
b7449926
XL
67 l: char,
68 r: char,
781aab86 69 ) -> (ImmTy<'tcx, M::Provenance>, bool) {
ba9703b0 70 use rustc_middle::mir::BinOp::*;
ea8adc8c 71
b7449926
XL
72 let res = match bin_op {
73 Eq => l == r,
74 Ne => l != r,
75 Lt => l < r,
76 Le => l <= r,
77 Gt => l > r,
78 Ge => l >= r,
f035d41b 79 _ => span_bug!(self.cur_span(), "Invalid operation on char: {:?}", bin_op),
94b46f34 80 };
781aab86 81 (ImmTy::from_bool(res, *self.tcx), false)
b7449926
XL
82 }
83
84 fn binary_bool_op(
85 &self,
86 bin_op: mir::BinOp,
87 l: bool,
88 r: bool,
781aab86 89 ) -> (ImmTy<'tcx, M::Provenance>, bool) {
ba9703b0 90 use rustc_middle::mir::BinOp::*;
b7449926
XL
91
92 let res = match bin_op {
93 Eq => l == r,
94 Ne => l != r,
95 Lt => l < r,
96 Le => l <= r,
97 Gt => l > r,
98 Ge => l >= r,
99 BitAnd => l & r,
100 BitOr => l | r,
101 BitXor => l ^ r,
f035d41b 102 _ => span_bug!(self.cur_span(), "Invalid operation on bool: {:?}", bin_op),
94b46f34 103 };
781aab86 104 (ImmTy::from_bool(res, *self.tcx), false)
b7449926 105 }
ea8adc8c 106
064997fb 107 fn binary_float_op<F: Float + Into<Scalar<M::Provenance>>>(
b7449926
XL
108 &self,
109 bin_op: mir::BinOp,
781aab86 110 layout: TyAndLayout<'tcx>,
dc9dc135
XL
111 l: F,
112 r: F,
781aab86 113 ) -> (ImmTy<'tcx, M::Provenance>, bool) {
ba9703b0 114 use rustc_middle::mir::BinOp::*;
b7449926 115
781aab86
FG
116 let val = match bin_op {
117 Eq => ImmTy::from_bool(l == r, *self.tcx),
118 Ne => ImmTy::from_bool(l != r, *self.tcx),
119 Lt => ImmTy::from_bool(l < r, *self.tcx),
120 Le => ImmTy::from_bool(l <= r, *self.tcx),
121 Gt => ImmTy::from_bool(l > r, *self.tcx),
122 Ge => ImmTy::from_bool(l >= r, *self.tcx),
123 Add => ImmTy::from_scalar((l + r).value.into(), layout),
124 Sub => ImmTy::from_scalar((l - r).value.into(), layout),
125 Mul => ImmTy::from_scalar((l * r).value.into(), layout),
126 Div => ImmTy::from_scalar((l / r).value.into(), layout),
127 Rem => ImmTy::from_scalar((l % r).value.into(), layout),
f035d41b 128 _ => span_bug!(self.cur_span(), "invalid float op: `{:?}`", bin_op),
dc9dc135 129 };
781aab86 130 (val, false)
b7449926 131 }
ea8adc8c 132
b7449926
XL
133 fn binary_int_op(
134 &self,
135 bin_op: mir::BinOp,
136 // passing in raw bits
137 l: u128,
ba9703b0 138 left_layout: TyAndLayout<'tcx>,
b7449926 139 r: u128,
ba9703b0 140 right_layout: TyAndLayout<'tcx>,
781aab86 141 ) -> InterpResult<'tcx, (ImmTy<'tcx, M::Provenance>, bool)> {
ba9703b0 142 use rustc_middle::mir::BinOp::*;
0531ce1d 143
fe692bf9
FG
144 let throw_ub_on_overflow = match bin_op {
145 AddUnchecked => Some(sym::unchecked_add),
146 SubUnchecked => Some(sym::unchecked_sub),
147 MulUnchecked => Some(sym::unchecked_mul),
148 ShlUnchecked => Some(sym::unchecked_shl),
149 ShrUnchecked => Some(sym::unchecked_shr),
150 _ => None,
151 };
152
b7449926 153 // Shift ops can have an RHS with a different numeric type.
fe692bf9 154 if matches!(bin_op, Shl | ShlUnchecked | Shr | ShrUnchecked) {
ba9703b0 155 let size = u128::from(left_layout.size.bits());
5e7ed085
FG
156 // Even if `r` is signed, we treat it as if it was unsigned (i.e., we use its
157 // zero-extended form). This matches the codegen backend:
158 // <https://github.com/rust-lang/rust/blob/c274e4969f058b1c644243181ece9f829efa7594/compiler/rustc_codegen_ssa/src/base.rs#L315-L317>.
159 // The overflow check is also ignorant to the sign:
160 // <https://github.com/rust-lang/rust/blob/c274e4969f058b1c644243181ece9f829efa7594/compiler/rustc_codegen_ssa/src/mir/rvalue.rs#L728>.
161 // This would behave rather strangely if we had integer types of size 256: a shift by
162 // -1i8 would actually shift by 255, but that would *not* be considered overflowing. A
163 // shift by -1i16 though would be considered overflowing. If we had integers of size
164 // 512, then a shift by -1i8 would even produce a different result than one by -1i16:
165 // the first shifts by 255, the latter by u16::MAX % 512 = 511. Lucky enough, our
166 // integers are maximally 128bits wide, so negative shifts *always* overflow and we have
167 // consistent results for the same value represented at different bit widths.
168 assert!(size <= 128);
fe692bf9 169 let original_r = r;
ba9703b0 170 let overflow = r >= size;
3c0e092e
XL
171 // The shift offset is implicitly masked to the type size, to make sure this operation
172 // is always defined. This is the one MIR operator that does *not* directly map to a
173 // single LLVM operation. See
5e7ed085 174 // <https://github.com/rust-lang/rust/blob/c274e4969f058b1c644243181ece9f829efa7594/compiler/rustc_codegen_ssa/src/common.rs#L131-L158>
3c0e092e
XL
175 // for the corresponding truncation in our codegen backends.
176 let r = r % size;
ba9703b0 177 let r = u32::try_from(r).unwrap(); // we masked so this will always fit
5e7ed085 178 let result = if left_layout.abi.is_signed() {
b7449926 179 let l = self.sign_extend(l, left_layout) as i128;
0531ce1d 180 let result = match bin_op {
fe692bf9
FG
181 Shl | ShlUnchecked => l.checked_shl(r).unwrap(),
182 Shr | ShrUnchecked => l.checked_shr(r).unwrap(),
923072b8 183 _ => bug!(),
0531ce1d
XL
184 };
185 result as u128
186 } else {
187 match bin_op {
fe692bf9
FG
188 Shl | ShlUnchecked => l.checked_shl(r).unwrap(),
189 Shr | ShrUnchecked => l.checked_shr(r).unwrap(),
923072b8 190 _ => bug!(),
0531ce1d 191 }
ea8adc8c 192 };
b7449926 193 let truncated = self.truncate(result, left_layout);
fe692bf9
FG
194
195 if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
196 throw_ub_custom!(
197 fluent::const_eval_overflow_shift,
198 val = original_r,
199 name = intrinsic_name
200 );
201 }
202
781aab86 203 return Ok((ImmTy::from_uint(truncated, left_layout), overflow));
ea8adc8c
XL
204 }
205
b7449926
XL
206 // For the remaining ops, the types must be the same on both sides
207 if left_layout.ty != right_layout.ty {
f035d41b
XL
208 span_bug!(
209 self.cur_span(),
781aab86
FG
210 "invalid asymmetric binary op {bin_op:?}: {l:?} ({l_ty}), {r:?} ({r_ty})",
211 l_ty = left_layout.ty,
212 r_ty = right_layout.ty,
416331ca 213 )
ea8adc8c
XL
214 }
215
74b04a01
XL
216 let size = left_layout.size;
217
b7449926 218 // Operations that need special treatment for signed integers
0531ce1d
XL
219 if left_layout.abi.is_signed() {
220 let op: Option<fn(&i128, &i128) -> bool> = match bin_op {
221 Lt => Some(i128::lt),
222 Le => Some(i128::le),
223 Gt => Some(i128::gt),
224 Ge => Some(i128::ge),
225 _ => None,
226 };
227 if let Some(op) = op {
b7449926
XL
228 let l = self.sign_extend(l, left_layout) as i128;
229 let r = self.sign_extend(r, right_layout) as i128;
781aab86 230 return Ok((ImmTy::from_bool(op(&l, &r), *self.tcx), false));
0531ce1d
XL
231 }
232 let op: Option<fn(i128, i128) -> (i128, bool)> = match bin_op {
60c5eb7d
XL
233 Div if r == 0 => throw_ub!(DivisionByZero),
234 Rem if r == 0 => throw_ub!(RemainderByZero),
0531ce1d
XL
235 Div => Some(i128::overflowing_div),
236 Rem => Some(i128::overflowing_rem),
fe692bf9
FG
237 Add | AddUnchecked => Some(i128::overflowing_add),
238 Sub | SubUnchecked => Some(i128::overflowing_sub),
239 Mul | MulUnchecked => Some(i128::overflowing_mul),
0531ce1d
XL
240 _ => None,
241 };
242 if let Some(op) = op {
5e7ed085 243 let l = self.sign_extend(l, left_layout) as i128;
b7449926 244 let r = self.sign_extend(r, right_layout) as i128;
5e7ed085
FG
245
246 // We need a special check for overflowing Rem and Div since they are *UB*
247 // on overflow, which can happen with "int_min $OP -1".
248 if matches!(bin_op, Rem | Div) {
249 if l == size.signed_int_min() && r == -1 {
250 if bin_op == Rem {
251 throw_ub!(RemainderOverflow)
252 } else {
253 throw_ub!(DivisionOverflow)
254 }
dfeec247 255 }
0531ce1d 256 }
74b04a01 257
ba9703b0 258 let (result, oflo) = op(l, r);
74b04a01
XL
259 // This may be out-of-bounds for the result type, so we have to truncate ourselves.
260 // If that truncation loses any information, we have an overflow.
0531ce1d 261 let result = result as u128;
b7449926 262 let truncated = self.truncate(result, left_layout);
fe692bf9
FG
263 let overflow = oflo || self.sign_extend(truncated, left_layout) != result;
264 if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
265 throw_ub_custom!(fluent::const_eval_overflow, name = intrinsic_name);
266 }
781aab86 267 return Ok((ImmTy::from_uint(truncated, left_layout), overflow));
83c7162d 268 }
0531ce1d 269 }
ea8adc8c 270
781aab86
FG
271 let val = match bin_op {
272 Eq => ImmTy::from_bool(l == r, *self.tcx),
273 Ne => ImmTy::from_bool(l != r, *self.tcx),
0531ce1d 274
781aab86
FG
275 Lt => ImmTy::from_bool(l < r, *self.tcx),
276 Le => ImmTy::from_bool(l <= r, *self.tcx),
277 Gt => ImmTy::from_bool(l > r, *self.tcx),
278 Ge => ImmTy::from_bool(l >= r, *self.tcx),
0531ce1d 279
781aab86
FG
280 BitOr => ImmTy::from_uint(l | r, left_layout),
281 BitAnd => ImmTy::from_uint(l & r, left_layout),
282 BitXor => ImmTy::from_uint(l ^ r, left_layout),
0531ce1d 283
fe692bf9 284 Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Rem | Div => {
74b04a01 285 assert!(!left_layout.abi.is_signed());
0531ce1d 286 let op: fn(u128, u128) -> (u128, bool) = match bin_op {
fe692bf9
FG
287 Add | AddUnchecked => u128::overflowing_add,
288 Sub | SubUnchecked => u128::overflowing_sub,
289 Mul | MulUnchecked => u128::overflowing_mul,
60c5eb7d
XL
290 Div if r == 0 => throw_ub!(DivisionByZero),
291 Rem if r == 0 => throw_ub!(RemainderByZero),
0531ce1d
XL
292 Div => u128::overflowing_div,
293 Rem => u128::overflowing_rem,
294 _ => bug!(),
295 };
296 let (result, oflo) = op(l, r);
74b04a01
XL
297 // Truncate to target type.
298 // If that truncation loses any information, we have an overflow.
b7449926 299 let truncated = self.truncate(result, left_layout);
fe692bf9
FG
300 let overflow = oflo || truncated != result;
301 if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
302 throw_ub_custom!(fluent::const_eval_overflow, name = intrinsic_name);
303 }
781aab86 304 return Ok((ImmTy::from_uint(truncated, left_layout), overflow));
0531ce1d 305 }
ea8adc8c 306
f035d41b
XL
307 _ => span_bug!(
308 self.cur_span(),
781aab86 309 "invalid binary op {:?}: {:?}, {:?} (both {})",
dfeec247
XL
310 bin_op,
311 l,
312 r,
313 right_layout.ty,
314 ),
ea8adc8c
XL
315 };
316
781aab86 317 Ok((val, false))
ea8adc8c 318 }
ea8adc8c 319
49aad941
FG
320 fn binary_ptr_op(
321 &self,
322 bin_op: mir::BinOp,
323 left: &ImmTy<'tcx, M::Provenance>,
324 right: &ImmTy<'tcx, M::Provenance>,
781aab86 325 ) -> InterpResult<'tcx, (ImmTy<'tcx, M::Provenance>, bool)> {
49aad941
FG
326 use rustc_middle::mir::BinOp::*;
327
328 match bin_op {
329 // Pointer ops that are always supported.
330 Offset => {
331 let ptr = left.to_scalar().to_pointer(self)?;
332 let offset_count = right.to_scalar().to_target_isize(self)?;
333 let pointee_ty = left.layout.ty.builtin_deref(true).unwrap().ty;
334
335 let offset_ptr = self.ptr_offset_inbounds(ptr, pointee_ty, offset_count)?;
781aab86
FG
336 Ok((
337 ImmTy::from_scalar(Scalar::from_maybe_pointer(offset_ptr, self), left.layout),
338 false,
339 ))
49aad941
FG
340 }
341
342 // Fall back to machine hook so Miri can support more pointer ops.
343 _ => M::binary_ptr_op(self, bin_op, left, right),
344 }
345 }
346
781aab86 347 /// Returns the result of the specified operation, and whether it overflowed.
e1599b0c 348 pub fn overflowing_binary_op(
b7449926
XL
349 &self,
350 bin_op: mir::BinOp,
064997fb
FG
351 left: &ImmTy<'tcx, M::Provenance>,
352 right: &ImmTy<'tcx, M::Provenance>,
781aab86 353 ) -> InterpResult<'tcx, (ImmTy<'tcx, M::Provenance>, bool)> {
dfeec247 354 trace!(
781aab86 355 "Running binary op {:?}: {:?} ({}), {:?} ({})",
dfeec247
XL
356 bin_op,
357 *left,
358 left.layout.ty,
359 *right,
360 right.layout.ty
361 );
b7449926 362
1b1a35ee 363 match left.layout.ty.kind() {
b7449926 364 ty::Char => {
9fa01778 365 assert_eq!(left.layout.ty, right.layout.ty);
f2b60f7d
FG
366 let left = left.to_scalar();
367 let right = right.to_scalar();
dc9dc135 368 Ok(self.binary_char_op(bin_op, left.to_char()?, right.to_char()?))
b7449926
XL
369 }
370 ty::Bool => {
9fa01778 371 assert_eq!(left.layout.ty, right.layout.ty);
f2b60f7d
FG
372 let left = left.to_scalar();
373 let right = right.to_scalar();
dc9dc135 374 Ok(self.binary_bool_op(bin_op, left.to_bool()?, right.to_bool()?))
b7449926
XL
375 }
376 ty::Float(fty) => {
9fa01778 377 assert_eq!(left.layout.ty, right.layout.ty);
781aab86 378 let layout = left.layout;
f2b60f7d
FG
379 let left = left.to_scalar();
380 let right = right.to_scalar();
dc9dc135 381 Ok(match fty {
dfeec247 382 FloatTy::F32 => {
781aab86 383 self.binary_float_op(bin_op, layout, left.to_f32()?, right.to_f32()?)
dfeec247
XL
384 }
385 FloatTy::F64 => {
781aab86 386 self.binary_float_op(bin_op, layout, left.to_f64()?, right.to_f64()?)
dfeec247 387 }
dc9dc135 388 })
b7449926 389 }
416331ca
XL
390 _ if left.layout.ty.is_integral() => {
391 // the RHS type can be different, e.g. for shifts -- but it has to be integral, too
dc9dc135 392 assert!(
416331ca 393 right.layout.ty.is_integral(),
781aab86 394 "Unexpected types for BinOp: {} {:?} {}",
dfeec247
XL
395 left.layout.ty,
396 bin_op,
397 right.layout.ty
416331ca 398 );
b7449926 399
f2b60f7d
FG
400 let l = left.to_scalar().to_bits(left.layout.size)?;
401 let r = right.to_scalar().to_bits(right.layout.size)?;
9fa01778 402 self.binary_int_op(bin_op, l, left.layout, r, right.layout)
b7449926 403 }
416331ca 404 _ if left.layout.ty.is_any_ptr() => {
a2a8927a
XL
405 // The RHS type must be a `pointer` *or an integer type* (for `Offset`).
406 // (Even when both sides are pointers, their type might differ, see issue #91636)
416331ca 407 assert!(
a2a8927a 408 right.layout.ty.is_any_ptr() || right.layout.ty.is_integral(),
781aab86 409 "Unexpected types for BinOp: {} {:?} {}",
dfeec247
XL
410 left.layout.ty,
411 bin_op,
412 right.layout.ty
416331ca
XL
413 );
414
49aad941 415 self.binary_ptr_op(bin_op, left, right)
416331ca 416 }
f035d41b
XL
417 _ => span_bug!(
418 self.cur_span(),
781aab86 419 "Invalid MIR: bad LHS type for binop: {}",
f035d41b
XL
420 left.layout.ty
421 ),
b7449926
XL
422 }
423 }
424
e1599b0c 425 #[inline]
781aab86 426 pub fn wrapping_binary_op(
e1599b0c
XL
427 &self,
428 bin_op: mir::BinOp,
064997fb
FG
429 left: &ImmTy<'tcx, M::Provenance>,
430 right: &ImmTy<'tcx, M::Provenance>,
431 ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
781aab86
FG
432 let (val, _overflow) = self.overflowing_binary_op(bin_op, left, right)?;
433 Ok(val)
e1599b0c
XL
434 }
435
74b04a01
XL
436 /// Returns the result of the specified operation, whether it overflowed, and
437 /// the result type.
438 pub fn overflowing_unary_op(
0531ce1d
XL
439 &self,
440 un_op: mir::UnOp,
064997fb 441 val: &ImmTy<'tcx, M::Provenance>,
781aab86 442 ) -> InterpResult<'tcx, (ImmTy<'tcx, M::Provenance>, bool)> {
ba9703b0 443 use rustc_middle::mir::UnOp::*;
ea8adc8c 444
9fa01778 445 let layout = val.layout;
f2b60f7d 446 let val = val.to_scalar();
781aab86 447 trace!("Running unary op {:?}: {:?} ({})", un_op, val, layout.ty);
ea8adc8c 448
1b1a35ee 449 match layout.ty.kind() {
b7449926
XL
450 ty::Bool => {
451 let val = val.to_bool()?;
452 let res = match un_op {
453 Not => !val,
f035d41b 454 _ => span_bug!(self.cur_span(), "Invalid bool op {:?}", un_op),
b7449926 455 };
781aab86 456 Ok((ImmTy::from_bool(res, *self.tcx), false))
b7449926
XL
457 }
458 ty::Float(fty) => {
b7449926 459 let res = match (un_op, fty) {
dc9dc135
XL
460 (Neg, FloatTy::F32) => Scalar::from_f32(-val.to_f32()?),
461 (Neg, FloatTy::F64) => Scalar::from_f64(-val.to_f64()?),
f035d41b 462 _ => span_bug!(self.cur_span(), "Invalid float op {:?}", un_op),
b7449926 463 };
781aab86 464 Ok((ImmTy::from_scalar(res, layout), false))
b7449926
XL
465 }
466 _ => {
467 assert!(layout.ty.is_integral());
136023e0 468 let val = val.to_bits(layout.size)?;
74b04a01
XL
469 let (res, overflow) = match un_op {
470 Not => (self.truncate(!val, layout), false), // bitwise negation, then truncate
b7449926 471 Neg => {
74b04a01 472 // arithmetic negation
b7449926 473 assert!(layout.abi.is_signed());
74b04a01
XL
474 let val = self.sign_extend(val, layout) as i128;
475 let (res, overflow) = val.overflowing_neg();
476 let res = res as u128;
477 // Truncate to target type.
478 // If that truncation loses any information, we have an overflow.
479 let truncated = self.truncate(res, layout);
480 (truncated, overflow || self.sign_extend(truncated, layout) != res)
b7449926
XL
481 }
482 };
781aab86 483 Ok((ImmTy::from_uint(res, layout), overflow))
b7449926
XL
484 }
485 }
0531ce1d 486 }
74b04a01 487
781aab86
FG
488 #[inline]
489 pub fn wrapping_unary_op(
74b04a01
XL
490 &self,
491 un_op: mir::UnOp,
064997fb
FG
492 val: &ImmTy<'tcx, M::Provenance>,
493 ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
781aab86
FG
494 let (val, _overflow) = self.overflowing_unary_op(un_op, val)?;
495 Ok(val)
74b04a01 496 }
ea8adc8c 497}