rustc_const_eval/const_eval/
dummy_machine.rs

1use rustc_middle::mir::interpret::{AllocId, ConstAllocation, InterpResult};
2use rustc_middle::mir::*;
3use rustc_middle::query::TyCtxtAt;
4use rustc_middle::ty::Ty;
5use rustc_middle::ty::layout::TyAndLayout;
6use rustc_middle::{bug, span_bug, ty};
7use rustc_span::def_id::DefId;
8use rustc_target::callconv::FnAbi;
9
10use crate::interpret::{
11    self, HasStaticRootDefId, ImmTy, Immediate, InterpCx, PointerArithmetic, interp_ok,
12    throw_machine_stop,
13};
14
15/// Macro for machine-specific `InterpError` without allocation.
16/// (These will never be shown to the user, but they help diagnose ICEs.)
17pub macro throw_machine_stop_str($($tt:tt)*) {{
18    // We make a new local type for it. The type itself does not carry any information,
19    // but its vtable (for the `MachineStopType` trait) does.
20    #[derive(Debug)]
21    struct Zst;
22    // Printing this type shows the desired string.
23    impl std::fmt::Display for Zst {
24        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25            write!(f, $($tt)*)
26        }
27    }
28
29    impl rustc_middle::mir::interpret::MachineStopType for Zst {
30        fn diagnostic_message(&self) -> rustc_errors::DiagMessage {
31            self.to_string().into()
32        }
33
34        fn add_args(
35            self: Box<Self>,
36            _: &mut dyn FnMut(rustc_errors::DiagArgName, rustc_errors::DiagArgValue),
37        ) {}
38    }
39    throw_machine_stop!(Zst)
40}}
41
42pub struct DummyMachine;
43
44impl HasStaticRootDefId for DummyMachine {
45    fn static_def_id(&self) -> Option<rustc_hir::def_id::LocalDefId> {
46        None
47    }
48}
49
50impl<'tcx> interpret::Machine<'tcx> for DummyMachine {
51    interpret::compile_time_machine!(<'tcx>);
52    const PANIC_ON_ALLOC_FAIL: bool = true;
53
54    // We want to just eval random consts in the program, so `eval_mir_const` can fail.
55    const ALL_CONSTS_ARE_PRECHECKED: bool = false;
56
57    #[inline(always)]
58    fn enforce_alignment(_ecx: &InterpCx<'tcx, Self>) -> bool {
59        false // no reason to enforce alignment
60    }
61
62    fn enforce_validity(_ecx: &InterpCx<'tcx, Self>, _layout: TyAndLayout<'tcx>) -> bool {
63        false
64    }
65
66    fn before_access_global(
67        _tcx: TyCtxtAt<'tcx>,
68        _machine: &Self,
69        _alloc_id: AllocId,
70        alloc: ConstAllocation<'tcx>,
71        _static_def_id: Option<DefId>,
72        is_write: bool,
73    ) -> InterpResult<'tcx> {
74        if is_write {
75            throw_machine_stop_str!("can't write to global");
76        }
77
78        // If the static allocation is mutable, then we can't const prop it as its content
79        // might be different at runtime.
80        if alloc.inner().mutability.is_mut() {
81            throw_machine_stop_str!("can't access mutable globals in ConstProp");
82        }
83
84        interp_ok(())
85    }
86
87    fn find_mir_or_eval_fn(
88        _ecx: &mut InterpCx<'tcx, Self>,
89        _instance: ty::Instance<'tcx>,
90        _abi: &FnAbi<'tcx, Ty<'tcx>>,
91        _args: &[interpret::FnArg<'tcx, Self::Provenance>],
92        _destination: &interpret::PlaceTy<'tcx, Self::Provenance>,
93        _target: Option<BasicBlock>,
94        _unwind: UnwindAction,
95    ) -> interpret::InterpResult<'tcx, Option<(&'tcx Body<'tcx>, ty::Instance<'tcx>)>> {
96        unimplemented!()
97    }
98
99    fn panic_nounwind(
100        _ecx: &mut InterpCx<'tcx, Self>,
101        _msg: &str,
102    ) -> interpret::InterpResult<'tcx> {
103        unimplemented!()
104    }
105
106    fn call_intrinsic(
107        _ecx: &mut InterpCx<'tcx, Self>,
108        _instance: ty::Instance<'tcx>,
109        _args: &[interpret::OpTy<'tcx, Self::Provenance>],
110        _destination: &interpret::PlaceTy<'tcx, Self::Provenance>,
111        _target: Option<BasicBlock>,
112        _unwind: UnwindAction,
113    ) -> interpret::InterpResult<'tcx, Option<ty::Instance<'tcx>>> {
114        unimplemented!()
115    }
116
117    fn assert_panic(
118        _ecx: &mut InterpCx<'tcx, Self>,
119        _msg: &rustc_middle::mir::AssertMessage<'tcx>,
120        _unwind: UnwindAction,
121    ) -> interpret::InterpResult<'tcx> {
122        unimplemented!()
123    }
124
125    fn binary_ptr_op(
126        ecx: &InterpCx<'tcx, Self>,
127        bin_op: BinOp,
128        left: &interpret::ImmTy<'tcx, Self::Provenance>,
129        right: &interpret::ImmTy<'tcx, Self::Provenance>,
130    ) -> interpret::InterpResult<'tcx, ImmTy<'tcx, Self::Provenance>> {
131        use rustc_middle::mir::BinOp::*;
132        interp_ok(match bin_op {
133            Eq | Ne | Lt | Le | Gt | Ge => {
134                // Types can differ, e.g. fn ptrs with different `for`.
135                assert_eq!(left.layout.backend_repr, right.layout.backend_repr);
136                let size = ecx.pointer_size();
137                // Just compare the bits. ScalarPairs are compared lexicographically.
138                // We thus always compare pairs and simply fill scalars up with 0.
139                // If the pointer has provenance, `to_bits` will return `Err` and we bail out.
140                let left = match **left {
141                    Immediate::Scalar(l) => (l.to_bits(size)?, 0),
142                    Immediate::ScalarPair(l1, l2) => (l1.to_bits(size)?, l2.to_bits(size)?),
143                    Immediate::Uninit => panic!("we should never see uninit data here"),
144                };
145                let right = match **right {
146                    Immediate::Scalar(r) => (r.to_bits(size)?, 0),
147                    Immediate::ScalarPair(r1, r2) => (r1.to_bits(size)?, r2.to_bits(size)?),
148                    Immediate::Uninit => panic!("we should never see uninit data here"),
149                };
150                let res = match bin_op {
151                    Eq => left == right,
152                    Ne => left != right,
153                    Lt => left < right,
154                    Le => left <= right,
155                    Gt => left > right,
156                    Ge => left >= right,
157                    _ => bug!(),
158                };
159                ImmTy::from_bool(res, *ecx.tcx)
160            }
161
162            // Some more operations are possible with atomics.
163            // The return value always has the provenance of the *left* operand.
164            Add | Sub | BitOr | BitAnd | BitXor => {
165                throw_machine_stop_str!("pointer arithmetic is not handled")
166            }
167
168            _ => span_bug!(ecx.cur_span(), "Invalid operator on pointers: {:?}", bin_op),
169        })
170    }
171
172    fn expose_provenance(
173        _ecx: &InterpCx<'tcx, Self>,
174        _provenance: Self::Provenance,
175    ) -> interpret::InterpResult<'tcx> {
176        unimplemented!()
177    }
178
179    fn init_frame(
180        _ecx: &mut InterpCx<'tcx, Self>,
181        _frame: interpret::Frame<'tcx, Self::Provenance>,
182    ) -> interpret::InterpResult<'tcx, interpret::Frame<'tcx, Self::Provenance, Self::FrameExtra>>
183    {
184        unimplemented!()
185    }
186
187    fn stack<'a>(
188        _ecx: &'a InterpCx<'tcx, Self>,
189    ) -> &'a [interpret::Frame<'tcx, Self::Provenance, Self::FrameExtra>] {
190        // Return an empty stack instead of panicking, as `cur_span` uses it to evaluate constants.
191        &[]
192    }
193
194    fn stack_mut<'a>(
195        _ecx: &'a mut InterpCx<'tcx, Self>,
196    ) -> &'a mut Vec<interpret::Frame<'tcx, Self::Provenance, Self::FrameExtra>> {
197        unimplemented!()
198    }
199
200    fn get_default_alloc_params(
201        &self,
202    ) -> <Self::Bytes as rustc_middle::mir::interpret::AllocBytes>::AllocParams {
203    }
204}