Skip to main content

rustc_const_eval/interpret/
operator.rs

1use either::Either;
2use rustc_abi::Size;
3use rustc_apfloat::{Float, FloatConvert};
4use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar};
5use rustc_middle::ty::layout::TyAndLayout;
6use rustc_middle::ty::{self, FloatTy, ScalarInt};
7use rustc_middle::{bug, mir, span_bug};
8use rustc_span::sym;
9use tracing::trace;
10
11use super::{ImmTy, InterpCx, Machine, MemPlaceMeta, interp_ok, throw_ub};
12
13/// Describes an atomic RMW operation.
14pub enum AtomicRmwOp {
15    MirOp {
16        op: mir::BinOp,
17        /// Indicates whether the result of the operation should be negated (`UnOp::Not`, must be a
18        /// boolean/integer-typed operation).
19        neg: bool,
20    },
21    Max,
22    Min,
23    Swap,
24}
25
26impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
27    fn three_way_compare<T: Ord>(&self, lhs: T, rhs: T) -> ImmTy<'tcx, M::Provenance> {
28        let res = Ord::cmp(&lhs, &rhs);
29        return ImmTy::from_ordering(res, *self.tcx);
30    }
31
32    fn binary_char_op(&self, bin_op: mir::BinOp, l: char, r: char) -> ImmTy<'tcx, M::Provenance> {
33        use rustc_middle::mir::BinOp::*;
34
35        if bin_op == Cmp {
36            return self.three_way_compare(l, r);
37        }
38
39        let res = match bin_op {
40            Eq => l == r,
41            Ne => l != r,
42            Lt => l < r,
43            Le => l <= r,
44            Gt => l > r,
45            Ge => l >= r,
46            _ => ::rustc_middle::util::bug::span_bug_fmt(self.cur_span(),
    format_args!("Invalid operation on char: {0:?}", bin_op))span_bug!(self.cur_span(), "Invalid operation on char: {:?}", bin_op),
47        };
48        ImmTy::from_bool(res, *self.tcx)
49    }
50
51    fn binary_bool_op(&self, bin_op: mir::BinOp, l: bool, r: bool) -> ImmTy<'tcx, M::Provenance> {
52        use rustc_middle::mir::BinOp::*;
53
54        let res = match bin_op {
55            Eq => l == r,
56            Ne => l != r,
57            Lt => l < r,
58            Le => l <= r,
59            Gt => l > r,
60            Ge => l >= r,
61            BitAnd => l & r,
62            BitOr => l | r,
63            BitXor => l ^ r,
64            _ => ::rustc_middle::util::bug::span_bug_fmt(self.cur_span(),
    format_args!("Invalid operation on bool: {0:?}", bin_op))span_bug!(self.cur_span(), "Invalid operation on bool: {:?}", bin_op),
65        };
66        ImmTy::from_bool(res, *self.tcx)
67    }
68
69    fn binary_float_op<F: Float + FloatConvert<F> + Into<Scalar<M::Provenance>>>(
70        &self,
71        bin_op: mir::BinOp,
72        layout: TyAndLayout<'tcx>,
73        l: F,
74        r: F,
75    ) -> ImmTy<'tcx, M::Provenance> {
76        use rustc_middle::mir::BinOp::*;
77
78        // Performs appropriate non-deterministic adjustments of NaN results.
79        let adjust_nan = |f: F| -> F { self.adjust_nan(f, &[l, r]) };
80
81        match bin_op {
82            Eq => ImmTy::from_bool(l == r, *self.tcx),
83            Ne => ImmTy::from_bool(l != r, *self.tcx),
84            Lt => ImmTy::from_bool(l < r, *self.tcx),
85            Le => ImmTy::from_bool(l <= r, *self.tcx),
86            Gt => ImmTy::from_bool(l > r, *self.tcx),
87            Ge => ImmTy::from_bool(l >= r, *self.tcx),
88            Add => ImmTy::from_scalar(adjust_nan((l + r).value).into(), layout),
89            Sub => ImmTy::from_scalar(adjust_nan((l - r).value).into(), layout),
90            Mul => ImmTy::from_scalar(adjust_nan((l * r).value).into(), layout),
91            Div => ImmTy::from_scalar(adjust_nan((l / r).value).into(), layout),
92            Rem => ImmTy::from_scalar(adjust_nan((l % r).value).into(), layout),
93            _ => ::rustc_middle::util::bug::span_bug_fmt(self.cur_span(),
    format_args!("invalid float op: `{0:?}`", bin_op))span_bug!(self.cur_span(), "invalid float op: `{:?}`", bin_op),
94        }
95    }
96
97    fn binary_int_op(
98        &self,
99        bin_op: mir::BinOp,
100        left: &ImmTy<'tcx, M::Provenance>,
101        right: &ImmTy<'tcx, M::Provenance>,
102    ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
103        use rustc_middle::mir::BinOp::*;
104
105        // This checks the size, so that we can just assert it below.
106        let l = left.to_scalar_int()?;
107        let r = right.to_scalar_int()?;
108        // Prepare to convert the values to signed or unsigned form.
109        let l_signed = || l.to_int(left.layout.size);
110        let l_unsigned = || l.to_uint(left.layout.size);
111        let r_signed = || r.to_int(right.layout.size);
112        let r_unsigned = || r.to_uint(right.layout.size);
113
114        let throw_ub_on_overflow = match bin_op {
115            AddUnchecked => Some(sym::unchecked_add),
116            SubUnchecked => Some(sym::unchecked_sub),
117            MulUnchecked => Some(sym::unchecked_mul),
118            ShlUnchecked => Some(sym::unchecked_shl),
119            ShrUnchecked => Some(sym::unchecked_shr),
120            _ => None,
121        };
122        let with_overflow = bin_op.is_overflowing();
123
124        // Shift ops can have an RHS with a different numeric type.
125        if #[allow(non_exhaustive_omitted_patterns)] match bin_op {
    Shl | ShlUnchecked | Shr | ShrUnchecked => true,
    _ => false,
}matches!(bin_op, Shl | ShlUnchecked | Shr | ShrUnchecked) {
126            let l_bits = left.layout.size.bits();
127            // Compute the equivalent shift modulo `size` that is in the range `0..size`. (This is
128            // the one MIR operator that does *not* directly map to a single LLVM operation.)
129            let (shift_amount, overflow) = if right.layout.backend_repr.is_signed() {
130                let shift_amount = r_signed();
131                let rem = shift_amount.rem_euclid(l_bits.into());
132                // `rem` is guaranteed positive, so the `unwrap` cannot fail
133                (u128::try_from(rem).unwrap(), rem != shift_amount)
134            } else {
135                let shift_amount = r_unsigned();
136                let rem = shift_amount.rem_euclid(l_bits.into());
137                (rem, rem != shift_amount)
138            };
139            let shift_amount = u32::try_from(shift_amount).unwrap(); // we brought this in the range `0..size` so this will always fit
140            // Compute the shifted result.
141            let result = if left.layout.backend_repr.is_signed() {
142                let l = l_signed();
143                let result = match bin_op {
144                    Shl | ShlUnchecked => l.checked_shl(shift_amount).unwrap(),
145                    Shr | ShrUnchecked => l.checked_shr(shift_amount).unwrap(),
146                    _ => ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))bug!(),
147                };
148                ScalarInt::truncate_from_int(result, left.layout.size).0
149            } else {
150                let l = l_unsigned();
151                let result = match bin_op {
152                    Shl | ShlUnchecked => l.checked_shl(shift_amount).unwrap(),
153                    Shr | ShrUnchecked => l.checked_shr(shift_amount).unwrap(),
154                    _ => ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))bug!(),
155                };
156                ScalarInt::truncate_from_uint(result, left.layout.size).0
157            };
158
159            if overflow && let Some(intrinsic) = throw_ub_on_overflow {
160                do yeet ::rustc_middle::mir::interpret::InterpErrorKind::UndefinedBehavior(::rustc_middle::mir::interpret::UndefinedBehaviorInfo::ShiftOverflow {
            intrinsic,
            shift_amount: if right.layout.backend_repr.is_signed() {
                Either::Right(r_signed())
            } else { Either::Left(r_unsigned()) },
        });throw_ub!(ShiftOverflow {
161                    intrinsic,
162                    shift_amount: if right.layout.backend_repr.is_signed() {
163                        Either::Right(r_signed())
164                    } else {
165                        Either::Left(r_unsigned())
166                    }
167                });
168            }
169
170            return interp_ok(ImmTy::from_scalar_int(result, left.layout));
171        }
172
173        // For the remaining ops, the types must be the same on both sides
174        if left.layout.ty != right.layout.ty {
175            ::rustc_middle::util::bug::span_bug_fmt(self.cur_span(),
    format_args!("invalid asymmetric binary op {2:?}: {3:?} ({0}), {4:?} ({1})",
        left.layout.ty, right.layout.ty, bin_op, l, r))span_bug!(
176                self.cur_span(),
177                "invalid asymmetric binary op {bin_op:?}: {l:?} ({l_ty}), {r:?} ({r_ty})",
178                l_ty = left.layout.ty,
179                r_ty = right.layout.ty,
180            )
181        }
182
183        let size = left.layout.size;
184
185        // Operations that need special treatment for signed integers
186        if left.layout.backend_repr.is_signed() {
187            let op: Option<fn(&i128, &i128) -> bool> = match bin_op {
188                Lt => Some(i128::lt),
189                Le => Some(i128::le),
190                Gt => Some(i128::gt),
191                Ge => Some(i128::ge),
192                _ => None,
193            };
194            if let Some(op) = op {
195                return interp_ok(ImmTy::from_bool(op(&l_signed(), &r_signed()), *self.tcx));
196            }
197            if bin_op == Cmp {
198                return interp_ok(self.three_way_compare(l_signed(), r_signed()));
199            }
200            let op: Option<fn(i128, i128) -> (i128, bool)> = match bin_op {
201                Div if r.is_null() => do yeet ::rustc_middle::mir::interpret::InterpErrorKind::UndefinedBehavior(::rustc_middle::mir::interpret::UndefinedBehaviorInfo::DivisionByZero)throw_ub!(DivisionByZero),
202                Rem if r.is_null() => do yeet ::rustc_middle::mir::interpret::InterpErrorKind::UndefinedBehavior(::rustc_middle::mir::interpret::UndefinedBehaviorInfo::RemainderByZero)throw_ub!(RemainderByZero),
203                Div => Some(i128::overflowing_div),
204                Rem => Some(i128::overflowing_rem),
205                Add | AddUnchecked | AddWithOverflow => Some(i128::overflowing_add),
206                Sub | SubUnchecked | SubWithOverflow => Some(i128::overflowing_sub),
207                Mul | MulUnchecked | MulWithOverflow => Some(i128::overflowing_mul),
208                _ => None,
209            };
210            if let Some(op) = op {
211                let l = l_signed();
212                let r = r_signed();
213
214                // We need a special check for overflowing Rem and Div since they are *UB*
215                // on overflow, which can happen with "int_min $OP -1".
216                if #[allow(non_exhaustive_omitted_patterns)] match bin_op {
    Rem | Div => true,
    _ => false,
}matches!(bin_op, Rem | Div) {
217                    if l == size.signed_int_min() && r == -1 {
218                        if bin_op == Rem {
219                            do yeet ::rustc_middle::mir::interpret::InterpErrorKind::UndefinedBehavior(::rustc_middle::mir::interpret::UndefinedBehaviorInfo::RemainderOverflow)throw_ub!(RemainderOverflow)
220                        } else {
221                            do yeet ::rustc_middle::mir::interpret::InterpErrorKind::UndefinedBehavior(::rustc_middle::mir::interpret::UndefinedBehaviorInfo::DivisionOverflow)throw_ub!(DivisionOverflow)
222                        }
223                    }
224                }
225
226                let (result, oflo) = op(l, r);
227                // This may be out-of-bounds for the result type, so we have to truncate.
228                // If that truncation loses any information, we have an overflow.
229                let (result, lossy) = ScalarInt::truncate_from_int(result, left.layout.size);
230                let overflow = oflo || lossy;
231                if overflow && let Some(intrinsic) = throw_ub_on_overflow {
232                    do yeet ::rustc_middle::mir::interpret::InterpErrorKind::UndefinedBehavior(::rustc_middle::mir::interpret::UndefinedBehaviorInfo::ArithOverflow {
            intrinsic,
        });throw_ub!(ArithOverflow { intrinsic });
233                }
234                let res = ImmTy::from_scalar_int(result, left.layout);
235                return interp_ok(if with_overflow {
236                    let overflow = ImmTy::from_bool(overflow, *self.tcx);
237                    ImmTy::from_pair(res, overflow, self)
238                } else {
239                    res
240                });
241            }
242        }
243        // From here on it's okay to treat everything as unsigned.
244        let l = l_unsigned();
245        let r = r_unsigned();
246
247        if bin_op == Cmp {
248            return interp_ok(self.three_way_compare(l, r));
249        }
250
251        interp_ok(match bin_op {
252            Eq => ImmTy::from_bool(l == r, *self.tcx),
253            Ne => ImmTy::from_bool(l != r, *self.tcx),
254
255            Lt => ImmTy::from_bool(l < r, *self.tcx),
256            Le => ImmTy::from_bool(l <= r, *self.tcx),
257            Gt => ImmTy::from_bool(l > r, *self.tcx),
258            Ge => ImmTy::from_bool(l >= r, *self.tcx),
259
260            BitOr => ImmTy::from_uint(l | r, left.layout),
261            BitAnd => ImmTy::from_uint(l & r, left.layout),
262            BitXor => ImmTy::from_uint(l ^ r, left.layout),
263
264            _ => {
265                if !!left.layout.backend_repr.is_signed() {
    ::core::panicking::panic("assertion failed: !left.layout.backend_repr.is_signed()")
};assert!(!left.layout.backend_repr.is_signed());
266                let op: fn(u128, u128) -> (u128, bool) = match bin_op {
267                    Add | AddUnchecked | AddWithOverflow => u128::overflowing_add,
268                    Sub | SubUnchecked | SubWithOverflow => u128::overflowing_sub,
269                    Mul | MulUnchecked | MulWithOverflow => u128::overflowing_mul,
270                    Div if r == 0 => do yeet ::rustc_middle::mir::interpret::InterpErrorKind::UndefinedBehavior(::rustc_middle::mir::interpret::UndefinedBehaviorInfo::DivisionByZero)throw_ub!(DivisionByZero),
271                    Rem if r == 0 => do yeet ::rustc_middle::mir::interpret::InterpErrorKind::UndefinedBehavior(::rustc_middle::mir::interpret::UndefinedBehaviorInfo::RemainderByZero)throw_ub!(RemainderByZero),
272                    Div => u128::overflowing_div,
273                    Rem => u128::overflowing_rem,
274                    _ => ::rustc_middle::util::bug::span_bug_fmt(self.cur_span(),
    format_args!("invalid binary op {0:?}: {1:?}, {2:?} (both {3})", bin_op,
        left, right, right.layout.ty))span_bug!(
275                        self.cur_span(),
276                        "invalid binary op {:?}: {:?}, {:?} (both {})",
277                        bin_op,
278                        left,
279                        right,
280                        right.layout.ty,
281                    ),
282                };
283                let (result, oflo) = op(l, r);
284                // Truncate to target type.
285                // If that truncation loses any information, we have an overflow.
286                let (result, lossy) = ScalarInt::truncate_from_uint(result, left.layout.size);
287                let overflow = oflo || lossy;
288                if overflow && let Some(intrinsic) = throw_ub_on_overflow {
289                    do yeet ::rustc_middle::mir::interpret::InterpErrorKind::UndefinedBehavior(::rustc_middle::mir::interpret::UndefinedBehaviorInfo::ArithOverflow {
            intrinsic,
        });throw_ub!(ArithOverflow { intrinsic });
290                }
291                let res = ImmTy::from_scalar_int(result, left.layout);
292                if with_overflow {
293                    let overflow = ImmTy::from_bool(overflow, *self.tcx);
294                    ImmTy::from_pair(res, overflow, self)
295                } else {
296                    res
297                }
298            }
299        })
300    }
301
302    /// Computes the total size of this access, `count * elem_size`,
303    /// checking for overflow beyond isize::MAX.
304    pub fn compute_size_in_bytes(&self, elem_size: Size, count: u64) -> Option<Size> {
305        // `checked_mul` applies `u64` limits independent of the target pointer size... but the
306        // subsequent check for `max_size_of_val` means we also handle 32bit targets correctly.
307        // (We cannot use `Size::checked_mul` as that enforces `obj_size_bound` as the limit, which
308        // would be wrong here.)
309        elem_size
310            .bytes()
311            .checked_mul(count)
312            .map(Size::from_bytes)
313            .filter(|&total| total <= self.max_size_of_val())
314    }
315
316    fn binary_ptr_op(
317        &self,
318        bin_op: mir::BinOp,
319        left: &ImmTy<'tcx, M::Provenance>,
320        right: &ImmTy<'tcx, M::Provenance>,
321    ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
322        use rustc_middle::mir::BinOp::*;
323
324        match bin_op {
325            // Pointer ops that are always supported.
326            Offset => {
327                let ptr = left.to_scalar().to_pointer(self);
328                let pointee_ty = left.layout.ty.builtin_deref(true).unwrap();
329                let pointee_layout = self.layout_of(pointee_ty)?;
330                if !pointee_layout.is_sized() {
    ::core::panicking::panic("assertion failed: pointee_layout.is_sized()")
};assert!(pointee_layout.is_sized());
331
332                // The size always fits in `i64` as it can be at most `isize::MAX`.
333                let pointee_size = i64::try_from(pointee_layout.size.bytes()).unwrap();
334                // This uses the same type as `right`, which can be `isize` or `usize`.
335                // `pointee_size` is guaranteed to fit into both types.
336                let pointee_size = ImmTy::from_int(pointee_size, right.layout);
337                // Multiply element size and element count.
338                let (val, overflowed) = self
339                    .binary_op(mir::BinOp::MulWithOverflow, right, &pointee_size)?
340                    .to_scalar_pair();
341                // This must not overflow.
342                if overflowed.to_bool()? {
343                    do yeet ::rustc_middle::mir::interpret::InterpErrorKind::UndefinedBehavior(::rustc_middle::mir::interpret::UndefinedBehaviorInfo::PointerArithOverflow)throw_ub!(PointerArithOverflow)
344                }
345
346                let offset_bytes = val.to_target_isize(self)?;
347                if !right.layout.backend_repr.is_signed() && offset_bytes < 0 {
348                    // We were supposed to do an unsigned offset but the result is negative -- this
349                    // can only mean that the cast wrapped around.
350                    do yeet ::rustc_middle::mir::interpret::InterpErrorKind::UndefinedBehavior(::rustc_middle::mir::interpret::UndefinedBehaviorInfo::PointerArithOverflow)throw_ub!(PointerArithOverflow)
351                }
352                let offset_ptr = self.ptr_offset_inbounds(ptr, offset_bytes)?;
353                interp_ok(ImmTy::from_scalar(
354                    Scalar::from_maybe_pointer(offset_ptr, self),
355                    left.layout,
356                ))
357            }
358
359            // Fall back to machine hook so Miri can support more pointer ops.
360            _ => M::binary_ptr_op(self, bin_op, left, right),
361        }
362    }
363
364    /// Returns the result of the specified operation.
365    ///
366    /// Whether this produces a scalar or a pair depends on the specific `bin_op`.
367    pub fn binary_op(
368        &self,
369        bin_op: mir::BinOp,
370        left: &ImmTy<'tcx, M::Provenance>,
371        right: &ImmTy<'tcx, M::Provenance>,
372    ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
373        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_const_eval/src/interpret/operator.rs:373",
                        "rustc_const_eval::interpret::operator",
                        ::tracing::Level::TRACE,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_const_eval/src/interpret/operator.rs"),
                        ::tracing_core::__macro_support::Option::Some(373u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_const_eval::interpret::operator"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::TRACE <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("Running binary op {0:?}: {1:?} ({2}), {3:?} ({4})",
                                                    bin_op, *left, left.layout.ty, *right, right.layout.ty) as
                                            &dyn ::tracing::field::Value))])
            });
    } else { ; }
};trace!(
374            "Running binary op {:?}: {:?} ({}), {:?} ({})",
375            bin_op, *left, left.layout.ty, *right, right.layout.ty
376        );
377
378        match left.layout.ty.kind() {
379            ty::Char => {
380                {
    match (&left.layout.ty, &right.layout.ty) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    }
};assert_eq!(left.layout.ty, right.layout.ty);
381                let left = left.to_scalar();
382                let right = right.to_scalar();
383                interp_ok(self.binary_char_op(bin_op, left.to_char()?, right.to_char()?))
384            }
385            ty::Bool => {
386                {
    match (&left.layout.ty, &right.layout.ty) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    }
};assert_eq!(left.layout.ty, right.layout.ty);
387                let left = left.to_scalar();
388                let right = right.to_scalar();
389                interp_ok(self.binary_bool_op(bin_op, left.to_bool()?, right.to_bool()?))
390            }
391            ty::Float(fty) => {
392                {
    match (&left.layout.ty, &right.layout.ty) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    }
};assert_eq!(left.layout.ty, right.layout.ty);
393                let layout = left.layout;
394                let left = left.to_scalar();
395                let right = right.to_scalar();
396                interp_ok(match fty {
397                    FloatTy::F16 => {
398                        self.binary_float_op(bin_op, layout, left.to_f16()?, right.to_f16()?)
399                    }
400                    FloatTy::F32 => {
401                        self.binary_float_op(bin_op, layout, left.to_f32()?, right.to_f32()?)
402                    }
403                    FloatTy::F64 => {
404                        self.binary_float_op(bin_op, layout, left.to_f64()?, right.to_f64()?)
405                    }
406                    FloatTy::F128 => {
407                        self.binary_float_op(bin_op, layout, left.to_f128()?, right.to_f128()?)
408                    }
409                })
410            }
411            _ if left.layout.ty.is_integral() => {
412                // the RHS type can be different, e.g. for shifts -- but it has to be integral, too
413                if !right.layout.ty.is_integral() {
    {
        ::core::panicking::panic_fmt(format_args!("Unexpected types for BinOp: {0} {1:?} {2}",
                left.layout.ty, bin_op, right.layout.ty));
    }
};assert!(
414                    right.layout.ty.is_integral(),
415                    "Unexpected types for BinOp: {} {:?} {}",
416                    left.layout.ty,
417                    bin_op,
418                    right.layout.ty
419                );
420
421                self.binary_int_op(bin_op, left, right)
422            }
423            _ if left.layout.ty.is_any_ptr() => {
424                // The RHS type must be a `pointer` *or an integer type* (for `Offset`).
425                // (Even when both sides are pointers, their type might differ, see issue #91636)
426                if !(right.layout.ty.is_any_ptr() || right.layout.ty.is_integral()) {
    {
        ::core::panicking::panic_fmt(format_args!("Unexpected types for BinOp: {0} {1:?} {2}",
                left.layout.ty, bin_op, right.layout.ty));
    }
};assert!(
427                    right.layout.ty.is_any_ptr() || right.layout.ty.is_integral(),
428                    "Unexpected types for BinOp: {} {:?} {}",
429                    left.layout.ty,
430                    bin_op,
431                    right.layout.ty
432                );
433
434                self.binary_ptr_op(bin_op, left, right)
435            }
436            _ => ::rustc_middle::util::bug::span_bug_fmt(self.cur_span(),
    format_args!("Invalid MIR: bad LHS type for binop: {0}", left.layout.ty))span_bug!(
437                self.cur_span(),
438                "Invalid MIR: bad LHS type for binop: {}",
439                left.layout.ty
440            ),
441        }
442    }
443
444    /// Returns the result of the specified operation.
445    pub fn unary_op(
446        &self,
447        un_op: mir::UnOp,
448        val: &ImmTy<'tcx, M::Provenance>,
449    ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
450        use rustc_middle::mir::UnOp::*;
451
452        let layout = val.layout;
453        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_const_eval/src/interpret/operator.rs:453",
                        "rustc_const_eval::interpret::operator",
                        ::tracing::Level::TRACE,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_const_eval/src/interpret/operator.rs"),
                        ::tracing_core::__macro_support::Option::Some(453u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_const_eval::interpret::operator"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::TRACE <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("Running unary op {0:?}: {1:?} ({2})",
                                                    un_op, val, layout.ty) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};trace!("Running unary op {:?}: {:?} ({})", un_op, val, layout.ty);
454
455        match layout.ty.kind() {
456            ty::Bool => {
457                let val = val.to_scalar();
458                let val = val.to_bool()?;
459                let res = match un_op {
460                    Not => !val,
461                    _ => ::rustc_middle::util::bug::span_bug_fmt(self.cur_span(),
    format_args!("Invalid bool op {0:?}", un_op))span_bug!(self.cur_span(), "Invalid bool op {:?}", un_op),
462                };
463                interp_ok(ImmTy::from_bool(res, *self.tcx))
464            }
465            ty::Float(fty) => {
466                let val = val.to_scalar();
467                if un_op != Neg {
468                    ::rustc_middle::util::bug::span_bug_fmt(self.cur_span(),
    format_args!("Invalid float op {0:?}", un_op));span_bug!(self.cur_span(), "Invalid float op {:?}", un_op);
469                }
470
471                // No NaN adjustment here, `-` is a bitwise operation!
472                let res = match fty {
473                    FloatTy::F16 => Scalar::from_f16(-val.to_f16()?),
474                    FloatTy::F32 => Scalar::from_f32(-val.to_f32()?),
475                    FloatTy::F64 => Scalar::from_f64(-val.to_f64()?),
476                    FloatTy::F128 => Scalar::from_f128(-val.to_f128()?),
477                };
478                interp_ok(ImmTy::from_scalar(res, layout))
479            }
480            ty::Int(..) => {
481                let val = val.to_scalar().to_int(layout.size)?;
482                let res = match un_op {
483                    Not => !val,
484                    Neg => val.wrapping_neg(),
485                    _ => ::rustc_middle::util::bug::span_bug_fmt(self.cur_span(),
    format_args!("Invalid integer op {0:?}", un_op))span_bug!(self.cur_span(), "Invalid integer op {:?}", un_op),
486                };
487                let res = ScalarInt::truncate_from_int(res, layout.size).0;
488                interp_ok(ImmTy::from_scalar(res.into(), layout))
489            }
490            ty::Uint(..) => {
491                let val = val.to_scalar().to_uint(layout.size)?;
492                let res = match un_op {
493                    Not => !val,
494                    _ => ::rustc_middle::util::bug::span_bug_fmt(self.cur_span(),
    format_args!("Invalid unsigned integer op {0:?}", un_op))span_bug!(self.cur_span(), "Invalid unsigned integer op {:?}", un_op),
495                };
496                let res = ScalarInt::truncate_from_uint(res, layout.size).0;
497                interp_ok(ImmTy::from_scalar(res.into(), layout))
498            }
499            ty::RawPtr(..) | ty::Ref(..) => {
500                {
    match (&un_op, &PtrMetadata) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    }
};assert_eq!(un_op, PtrMetadata);
501                self.deref_pointer(val)?; // validity check
502                let (_, meta) = val.to_scalar_and_meta();
503                interp_ok(match meta {
504                    MemPlaceMeta::Meta(scalar) => {
505                        let ty = un_op.ty(*self.tcx, val.layout.ty);
506                        let layout = self.layout_of(ty)?;
507                        ImmTy::from_scalar(scalar, layout)
508                    }
509                    MemPlaceMeta::None => {
510                        let unit_layout = self.layout_of(self.tcx.types.unit)?;
511                        ImmTy::uninit(unit_layout)
512                    }
513                })
514            }
515            _ => {
516                ::rustc_middle::util::bug::bug_fmt(format_args!("Unexpected unary op argument {0:?}",
        val))bug!("Unexpected unary op argument {val:?}")
517            }
518        }
519    }
520
521    pub fn atomic_rmw_op(
522        &self,
523        op: AtomicRmwOp,
524        left: &ImmTy<'tcx, M::Provenance>,
525        right: &ImmTy<'tcx, M::Provenance>,
526    ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
527        interp_ok(match op {
528            AtomicRmwOp::MirOp { op, neg } => {
529                let val = self.binary_op(op, &left, right)?;
530                if neg { self.unary_op(mir::UnOp::Not, &val)? } else { val }
531            }
532            AtomicRmwOp::Max => {
533                let lt = self.binary_op(mir::BinOp::Lt, &left, right)?.to_scalar().to_bool()?;
534                if lt { right } else { &left }.clone()
535            }
536            AtomicRmwOp::Min => {
537                let lt = self.binary_op(mir::BinOp::Lt, &left, right)?.to_scalar().to_bool()?;
538                if lt { &left } else { right }.clone()
539            }
540            AtomicRmwOp::Swap => right.clone(),
541        })
542    }
543}