]>
Commit | Line | Data |
---|---|---|
5099ac24 | 1 | use rustc_hir::def::DefKind; |
487cf647 | 2 | use rustc_hir::LangItem; |
ba9703b0 | 3 | use rustc_middle::mir; |
487cf647 FG |
4 | use rustc_middle::mir::interpret::PointerArithmetic; |
5 | use rustc_middle::ty::layout::FnAbiOf; | |
04454e1e | 6 | use rustc_middle::ty::{self, Ty, TyCtxt}; |
f9f354fc | 7 | use std::borrow::Borrow; |
dfeec247 | 8 | use std::hash::Hash; |
487cf647 | 9 | use std::ops::ControlFlow; |
dfeec247 | 10 | |
2b03887a FG |
11 | use rustc_data_structures::fx::FxIndexMap; |
12 | use rustc_data_structures::fx::IndexEntry; | |
fc512014 | 13 | use std::fmt; |
dfeec247 | 14 | |
3dfed10e | 15 | use rustc_ast::Mutability; |
ba9703b0 XL |
16 | use rustc_hir::def_id::DefId; |
17 | use rustc_middle::mir::AssertMessage; | |
f9f354fc | 18 | use rustc_session::Limit; |
1b1a35ee | 19 | use rustc_span::symbol::{sym, Symbol}; |
fc512014 | 20 | use rustc_target::abi::{Align, Size}; |
064997fb | 21 | use rustc_target::spec::abi::Abi as CallAbi; |
dfeec247 XL |
22 | |
23 | use crate::interpret::{ | |
487cf647 FG |
24 | self, compile_time_machine, AllocId, ConstAllocation, FnVal, Frame, ImmTy, InterpCx, |
25 | InterpResult, OpTy, PlaceTy, Pointer, Scalar, StackPopUnwind, | |
dfeec247 XL |
26 | }; |
27 | ||
28 | use super::error::*; | |
29 | ||
ba9703b0 | 30 | /// Extra machine state for CTFE, and the Machine instance |
dfeec247 | 31 | pub struct CompileTimeInterpreter<'mir, 'tcx> { |
ba9703b0 | 32 | /// For now, the number of terminators that can be evaluated before we throw a resource |
cdc7bbd5 | 33 | /// exhaustion error. |
ba9703b0 XL |
34 | /// |
35 | /// Setting this to `0` disables the limit and allows the interpreter to run forever. | |
f2b60f7d | 36 | pub(super) steps_remaining: usize, |
74b04a01 | 37 | |
ba9703b0 | 38 | /// The virtual call stack. |
f2b60f7d | 39 | pub(super) stack: Vec<Frame<'mir, 'tcx, AllocId, ()>>, |
dfeec247 | 40 | |
f9f354fc XL |
41 | /// We need to make sure consts never point to anything mutable, even recursively. That is |
42 | /// relied on for pattern matching on consts with references. | |
43 | /// To achieve this, two pieces have to work together: | |
44 | /// * Interning makes everything outside of statics immutable. | |
45 | /// * Pointers to allocations inside of statics can never leak outside, to a non-static global. | |
46 | /// This boolean here controls the second part. | |
dfeec247 | 47 | pub(super) can_access_statics: bool, |
f2b60f7d FG |
48 | |
49 | /// Whether to check alignment during evaluation. | |
50 | pub(super) check_alignment: bool, | |
dfeec247 XL |
51 | } |
52 | ||
53 | impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> { | |
f2b60f7d FG |
54 | pub(crate) fn new( |
55 | const_eval_limit: Limit, | |
56 | can_access_statics: bool, | |
57 | check_alignment: bool, | |
58 | ) -> Self { | |
04454e1e FG |
59 | CompileTimeInterpreter { |
60 | steps_remaining: const_eval_limit.0, | |
61 | stack: Vec::new(), | |
62 | can_access_statics, | |
f2b60f7d | 63 | check_alignment, |
04454e1e | 64 | } |
dfeec247 XL |
65 | } |
66 | } | |
67 | ||
2b03887a | 68 | impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxIndexMap<K, V> { |
dfeec247 XL |
69 | #[inline(always)] |
70 | fn contains_key<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> bool | |
71 | where | |
72 | K: Borrow<Q>, | |
73 | { | |
2b03887a | 74 | FxIndexMap::contains_key(self, k) |
dfeec247 XL |
75 | } |
76 | ||
77 | #[inline(always)] | |
78 | fn insert(&mut self, k: K, v: V) -> Option<V> { | |
2b03887a | 79 | FxIndexMap::insert(self, k, v) |
dfeec247 XL |
80 | } |
81 | ||
82 | #[inline(always)] | |
83 | fn remove<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> Option<V> | |
84 | where | |
85 | K: Borrow<Q>, | |
86 | { | |
2b03887a | 87 | FxIndexMap::remove(self, k) |
dfeec247 XL |
88 | } |
89 | ||
90 | #[inline(always)] | |
91 | fn filter_map_collect<T>(&self, mut f: impl FnMut(&K, &V) -> Option<T>) -> Vec<T> { | |
92 | self.iter().filter_map(move |(k, v)| f(k, &*v)).collect() | |
93 | } | |
94 | ||
95 | #[inline(always)] | |
96 | fn get_or<E>(&self, k: K, vacant: impl FnOnce() -> Result<V, E>) -> Result<&V, E> { | |
97 | match self.get(&k) { | |
98 | Some(v) => Ok(v), | |
99 | None => { | |
100 | vacant()?; | |
101 | bug!("The CTFE machine shouldn't ever need to extend the alloc_map when reading") | |
102 | } | |
103 | } | |
104 | } | |
105 | ||
106 | #[inline(always)] | |
107 | fn get_mut_or<E>(&mut self, k: K, vacant: impl FnOnce() -> Result<V, E>) -> Result<&mut V, E> { | |
108 | match self.entry(k) { | |
2b03887a FG |
109 | IndexEntry::Occupied(e) => Ok(e.into_mut()), |
110 | IndexEntry::Vacant(e) => { | |
dfeec247 XL |
111 | let v = vacant()?; |
112 | Ok(e.insert(v)) | |
113 | } | |
114 | } | |
115 | } | |
116 | } | |
117 | ||
923072b8 | 118 | pub(crate) type CompileTimeEvalContext<'mir, 'tcx> = |
dfeec247 XL |
119 | InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>; |
120 | ||
fc512014 XL |
121 | #[derive(Debug, PartialEq, Eq, Copy, Clone)] |
122 | pub enum MemoryKind { | |
123 | Heap, | |
124 | } | |
125 | ||
126 | impl fmt::Display for MemoryKind { | |
127 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
128 | match self { | |
129 | MemoryKind::Heap => write!(f, "heap allocation"), | |
130 | } | |
131 | } | |
132 | } | |
133 | ||
134 | impl interpret::MayLeak for MemoryKind { | |
135 | #[inline(always)] | |
136 | fn may_leak(self) -> bool { | |
137 | match self { | |
138 | MemoryKind::Heap => false, | |
139 | } | |
140 | } | |
141 | } | |
142 | ||
dfeec247 XL |
143 | impl interpret::MayLeak for ! { |
144 | #[inline(always)] | |
145 | fn may_leak(self) -> bool { | |
146 | // `self` is uninhabited | |
147 | self | |
148 | } | |
149 | } | |
150 | ||
1b1a35ee | 151 | impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> { |
487cf647 FG |
152 | /// "Intercept" a function call, because we have something special to do for it. |
153 | /// All `#[rustc_do_not_const_check]` functions should be hooked here. | |
154 | /// If this returns `Some` function, which may be `instance` or a different function with | |
155 | /// compatible arguments, then evaluation should continue with that function. | |
156 | /// If this returns `None`, the function call has been handled and the function has returned. | |
157 | fn hook_special_const_fn( | |
158 | &mut self, | |
159 | instance: ty::Instance<'tcx>, | |
160 | args: &[OpTy<'tcx>], | |
161 | dest: &PlaceTy<'tcx>, | |
162 | ret: Option<mir::BasicBlock>, | |
163 | ) -> InterpResult<'tcx, Option<ty::Instance<'tcx>>> { | |
164 | let def_id = instance.def_id(); | |
165 | ||
166 | if Some(def_id) == self.tcx.lang_items().panic_display() | |
167 | || Some(def_id) == self.tcx.lang_items().begin_panic_fn() | |
168 | { | |
169 | // &str or &&str | |
170 | assert!(args.len() == 1); | |
171 | ||
172 | let mut msg_place = self.deref_operand(&args[0])?; | |
173 | while msg_place.layout.ty.is_ref() { | |
174 | msg_place = self.deref_operand(&msg_place.into())?; | |
175 | } | |
176 | ||
177 | let msg = Symbol::intern(self.read_str(&msg_place)?); | |
178 | let span = self.find_closest_untracked_caller_location(); | |
179 | let (file, line, col) = self.location_triple_for_span(span); | |
180 | return Err(ConstEvalErrKind::Panic { msg, file, line, col }.into()); | |
181 | } else if Some(def_id) == self.tcx.lang_items().panic_fmt() { | |
182 | // For panic_fmt, call const_panic_fmt instead. | |
183 | let const_def_id = self.tcx.require_lang_item(LangItem::ConstPanicFmt, None); | |
184 | let new_instance = ty::Instance::resolve( | |
185 | *self.tcx, | |
186 | ty::ParamEnv::reveal_all(), | |
187 | const_def_id, | |
188 | instance.substs, | |
189 | ) | |
190 | .unwrap() | |
191 | .unwrap(); | |
192 | ||
193 | return Ok(Some(new_instance)); | |
194 | } else if Some(def_id) == self.tcx.lang_items().align_offset_fn() { | |
195 | // For align_offset, we replace the function call if the pointer has no address. | |
196 | match self.align_offset(instance, args, dest, ret)? { | |
197 | ControlFlow::Continue(()) => return Ok(Some(instance)), | |
198 | ControlFlow::Break(()) => return Ok(None), | |
199 | } | |
200 | } | |
201 | Ok(Some(instance)) | |
202 | } | |
203 | ||
204 | /// `align_offset(ptr, target_align)` needs special handling in const eval, because the pointer | |
205 | /// may not have an address. | |
206 | /// | |
207 | /// If `ptr` does have a known address, then we return `CONTINUE` and the function call should | |
208 | /// proceed as normal. | |
209 | /// | |
210 | /// If `ptr` doesn't have an address, but its underlying allocation's alignment is at most | |
211 | /// `target_align`, then we call the function again with an dummy address relative to the | |
212 | /// allocation. | |
213 | /// | |
214 | /// If `ptr` doesn't have an address and `target_align` is stricter than the underlying | |
215 | /// allocation's alignment, then we return `usize::MAX` immediately. | |
216 | fn align_offset( | |
217 | &mut self, | |
218 | instance: ty::Instance<'tcx>, | |
219 | args: &[OpTy<'tcx>], | |
220 | dest: &PlaceTy<'tcx>, | |
221 | ret: Option<mir::BasicBlock>, | |
222 | ) -> InterpResult<'tcx, ControlFlow<()>> { | |
223 | assert_eq!(args.len(), 2); | |
224 | ||
225 | let ptr = self.read_pointer(&args[0])?; | |
226 | let target_align = self.read_scalar(&args[1])?.to_machine_usize(self)?; | |
227 | ||
228 | if !target_align.is_power_of_two() { | |
229 | throw_ub_format!("`align_offset` called with non-power-of-two align: {}", target_align); | |
230 | } | |
231 | ||
232 | match self.ptr_try_get_alloc_id(ptr) { | |
233 | Ok((alloc_id, offset, _extra)) => { | |
234 | let (_size, alloc_align, _kind) = self.get_alloc_info(alloc_id); | |
235 | ||
236 | if target_align <= alloc_align.bytes() { | |
237 | // Extract the address relative to the allocation base that is definitely | |
238 | // sufficiently aligned and call `align_offset` again. | |
239 | let addr = ImmTy::from_uint(offset.bytes(), args[0].layout).into(); | |
240 | let align = ImmTy::from_uint(target_align, args[1].layout).into(); | |
241 | let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty())?; | |
242 | ||
243 | // We replace the entire function call with a "tail call". | |
244 | // Note that this happens before the frame of the original function | |
245 | // is pushed on the stack. | |
246 | self.eval_fn_call( | |
247 | FnVal::Instance(instance), | |
248 | (CallAbi::Rust, fn_abi), | |
249 | &[addr, align], | |
250 | /* with_caller_location = */ false, | |
251 | dest, | |
252 | ret, | |
253 | StackPopUnwind::NotAllowed, | |
254 | )?; | |
255 | Ok(ControlFlow::BREAK) | |
256 | } else { | |
257 | // Not alignable in const, return `usize::MAX`. | |
258 | let usize_max = Scalar::from_machine_usize(self.machine_usize_max(), self); | |
259 | self.write_scalar(usize_max, dest)?; | |
260 | self.return_to_block(ret)?; | |
261 | Ok(ControlFlow::BREAK) | |
262 | } | |
263 | } | |
264 | Err(_addr) => { | |
265 | // The pointer has an address, continue with function call. | |
266 | Ok(ControlFlow::CONTINUE) | |
267 | } | |
268 | } | |
269 | } | |
270 | ||
f2b60f7d FG |
271 | /// See documentation on the `ptr_guaranteed_cmp` intrinsic. |
272 | fn guaranteed_cmp(&mut self, a: Scalar, b: Scalar) -> InterpResult<'tcx, u8> { | |
04454e1e | 273 | Ok(match (a, b) { |
1b1a35ee | 274 | // Comparisons between integers are always known. |
f2b60f7d FG |
275 | (Scalar::Int { .. }, Scalar::Int { .. }) => { |
276 | if a == b { | |
277 | 1 | |
278 | } else { | |
279 | 0 | |
280 | } | |
281 | } | |
1b1a35ee XL |
282 | // Comparisons of abstract pointers with null pointers are known if the pointer |
283 | // is in bounds, because if they are in bounds, the pointer can't be null. | |
1b1a35ee | 284 | // Inequality with integers other than null can never be known for sure. |
5e7ed085 | 285 | (Scalar::Int(int), ptr @ Scalar::Ptr(..)) |
f2b60f7d FG |
286 | | (ptr @ Scalar::Ptr(..), Scalar::Int(int)) |
287 | if int.is_null() && !self.scalar_may_be_null(ptr)? => | |
288 | { | |
289 | 0 | |
29967ef6 | 290 | } |
f2b60f7d FG |
291 | // Equality with integers can never be known for sure. |
292 | (Scalar::Int { .. }, Scalar::Ptr(..)) | (Scalar::Ptr(..), Scalar::Int { .. }) => 2, | |
293 | // FIXME: return a `1` for when both sides are the same pointer, *except* that | |
294 | // some things (like functions and vtables) do not have stable addresses | |
295 | // so we need to be careful around them (see e.g. #73722). | |
296 | // FIXME: return `0` for at least some comparisons where we can reliably | |
1b1a35ee XL |
297 | // determine the result of runtime inequality tests at compile-time. |
298 | // Examples include comparison of addresses in different static items. | |
f2b60f7d | 299 | (Scalar::Ptr(..), Scalar::Ptr(..)) => 2, |
04454e1e | 300 | }) |
1b1a35ee XL |
301 | } |
302 | } | |
303 | ||
dfeec247 | 304 | impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, 'tcx> { |
f9f354fc | 305 | compile_time_machine!(<'mir, 'tcx>); |
dfeec247 | 306 | |
fc512014 XL |
307 | type MemoryKind = MemoryKind; |
308 | ||
136023e0 XL |
309 | const PANIC_ON_ALLOC_FAIL: bool = false; // will be raised as a proper error |
310 | ||
f2b60f7d FG |
311 | #[inline(always)] |
312 | fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { | |
313 | ecx.machine.check_alignment | |
314 | } | |
315 | ||
316 | #[inline(always)] | |
317 | fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { | |
318 | ecx.tcx.sess.opts.unstable_opts.extra_const_ub_checks | |
319 | } | |
320 | ||
5869c6ff XL |
321 | fn load_mir( |
322 | ecx: &InterpCx<'mir, 'tcx, Self>, | |
323 | instance: ty::InstanceDef<'tcx>, | |
324 | ) -> InterpResult<'tcx, &'tcx mir::Body<'tcx>> { | |
325 | match instance { | |
326 | ty::InstanceDef::Item(def) => { | |
327 | if ecx.tcx.is_ctfe_mir_available(def.did) { | |
328 | Ok(ecx.tcx.mir_for_ctfe_opt_const_arg(def)) | |
5099ac24 | 329 | } else if ecx.tcx.def_kind(def.did) == DefKind::AssocConst { |
5e7ed085 | 330 | let guar = ecx.tcx.sess.delay_span_bug( |
5099ac24 FG |
331 | rustc_span::DUMMY_SP, |
332 | "This is likely a const item that is missing from its impl", | |
333 | ); | |
5e7ed085 | 334 | throw_inval!(AlreadyReported(guar)); |
5869c6ff | 335 | } else { |
f2b60f7d FG |
336 | // `find_mir_or_eval_fn` checks that this is a const fn before even calling us, |
337 | // so this should be unreachable. | |
94222f64 | 338 | let path = ecx.tcx.def_path_str(def.did); |
f2b60f7d | 339 | bug!("trying to call extern function `{path}` at compile-time"); |
5869c6ff XL |
340 | } |
341 | } | |
342 | _ => Ok(ecx.tcx.instance_mir(instance)), | |
343 | } | |
344 | } | |
345 | ||
dfeec247 XL |
346 | fn find_mir_or_eval_fn( |
347 | ecx: &mut InterpCx<'mir, 'tcx, Self>, | |
dfeec247 | 348 | instance: ty::Instance<'tcx>, |
064997fb | 349 | _abi: CallAbi, |
dfeec247 | 350 | args: &[OpTy<'tcx>], |
487cf647 FG |
351 | dest: &PlaceTy<'tcx>, |
352 | ret: Option<mir::BasicBlock>, | |
17df50a5 | 353 | _unwind: StackPopUnwind, // unwinding is not supported in consts |
a2a8927a | 354 | ) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>> { |
dfeec247 XL |
355 | debug!("find_mir_or_eval_fn: {:?}", instance); |
356 | ||
357 | // Only check non-glue functions | |
3dfed10e | 358 | if let ty::InstanceDef::Item(def) = instance.def { |
dfeec247 XL |
359 | // Execution might have wandered off into other crates, so we cannot do a stability- |
360 | // sensitive check here. But we can at least rule out functions that are not const | |
361 | // at all. | |
fc512014 | 362 | if !ecx.tcx.is_const_fn_raw(def.did) { |
923072b8 FG |
363 | // allow calling functions inside a trait marked with #[const_trait]. |
364 | if !ecx.tcx.is_const_default_method(def.did) { | |
3c0e092e XL |
365 | // We certainly do *not* want to actually call the fn |
366 | // though, so be sure we return here. | |
367 | throw_unsup_format!("calling non-const function `{}`", instance) | |
136023e0 | 368 | } |
dfeec247 | 369 | } |
c295e0f8 | 370 | |
487cf647 FG |
371 | let Some(new_instance) = ecx.hook_special_const_fn(instance, args, dest, ret)? else { |
372 | return Ok(None); | |
373 | }; | |
374 | ||
375 | if new_instance != instance { | |
c295e0f8 | 376 | // We call another const fn instead. |
a2a8927a XL |
377 | // However, we return the *original* instance to make backtraces work out |
378 | // (and we hope this does not confuse the FnAbi checks too much). | |
379 | return Ok(Self::find_mir_or_eval_fn( | |
380 | ecx, | |
381 | new_instance, | |
382 | _abi, | |
383 | args, | |
487cf647 FG |
384 | dest, |
385 | ret, | |
a2a8927a XL |
386 | _unwind, |
387 | )? | |
388 | .map(|(body, _instance)| (body, instance))); | |
c295e0f8 | 389 | } |
dfeec247 | 390 | } |
487cf647 | 391 | |
dfeec247 | 392 | // This is a const fn. Call it. |
a2a8927a | 393 | Ok(Some((ecx.load_mir(instance.def, None)?, instance))) |
dfeec247 XL |
394 | } |
395 | ||
dfeec247 XL |
396 | fn call_intrinsic( |
397 | ecx: &mut InterpCx<'mir, 'tcx, Self>, | |
dfeec247 XL |
398 | instance: ty::Instance<'tcx>, |
399 | args: &[OpTy<'tcx>], | |
064997fb | 400 | dest: &PlaceTy<'tcx, Self::Provenance>, |
923072b8 | 401 | target: Option<mir::BasicBlock>, |
17df50a5 | 402 | _unwind: StackPopUnwind, |
dfeec247 | 403 | ) -> InterpResult<'tcx> { |
1b1a35ee | 404 | // Shared intrinsics. |
923072b8 | 405 | if ecx.emulate_intrinsic(instance, args, dest, target)? { |
dfeec247 XL |
406 | return Ok(()); |
407 | } | |
dfeec247 | 408 | let intrinsic_name = ecx.tcx.item_name(instance.def_id()); |
1b1a35ee XL |
409 | |
410 | // CTFE-specific intrinsics. | |
923072b8 | 411 | let Some(ret) = target else { |
f2b60f7d | 412 | throw_unsup_format!("intrinsic `{intrinsic_name}` is not supported at compile-time"); |
1b1a35ee XL |
413 | }; |
414 | match intrinsic_name { | |
f2b60f7d FG |
415 | sym::ptr_guaranteed_cmp => { |
416 | let a = ecx.read_scalar(&args[0])?; | |
417 | let b = ecx.read_scalar(&args[1])?; | |
418 | let cmp = ecx.guaranteed_cmp(a, b)?; | |
419 | ecx.write_scalar(Scalar::from_u8(cmp), dest)?; | |
1b1a35ee | 420 | } |
fc512014 | 421 | sym::const_allocate => { |
6a06907d XL |
422 | let size = ecx.read_scalar(&args[0])?.to_machine_usize(ecx)?; |
423 | let align = ecx.read_scalar(&args[1])?.to_machine_usize(ecx)?; | |
fc512014 XL |
424 | |
425 | let align = match Align::from_bytes(align) { | |
426 | Ok(a) => a, | |
427 | Err(err) => throw_ub_format!("align has to be a power of 2, {}", err), | |
428 | }; | |
429 | ||
04454e1e | 430 | let ptr = ecx.allocate_ptr( |
fc512014 XL |
431 | Size::from_bytes(size as u64), |
432 | align, | |
433 | interpret::MemoryKind::Machine(MemoryKind::Heap), | |
136023e0 XL |
434 | )?; |
435 | ecx.write_pointer(ptr, dest)?; | |
fc512014 | 436 | } |
5099ac24 FG |
437 | sym::const_deallocate => { |
438 | let ptr = ecx.read_pointer(&args[0])?; | |
439 | let size = ecx.read_scalar(&args[1])?.to_machine_usize(ecx)?; | |
440 | let align = ecx.read_scalar(&args[2])?.to_machine_usize(ecx)?; | |
441 | ||
442 | let size = Size::from_bytes(size); | |
443 | let align = match Align::from_bytes(align) { | |
444 | Ok(a) => a, | |
445 | Err(err) => throw_ub_format!("align has to be a power of 2, {}", err), | |
446 | }; | |
447 | ||
448 | // If an allocation is created in an another const, | |
449 | // we don't deallocate it. | |
04454e1e | 450 | let (alloc_id, _, _) = ecx.ptr_get_alloc_id(ptr)?; |
5099ac24 | 451 | let is_allocated_in_another_const = matches!( |
064997fb | 452 | ecx.tcx.try_get_global_alloc(alloc_id), |
5099ac24 FG |
453 | Some(interpret::GlobalAlloc::Memory(_)) |
454 | ); | |
455 | ||
456 | if !is_allocated_in_another_const { | |
04454e1e | 457 | ecx.deallocate_ptr( |
5099ac24 FG |
458 | ptr, |
459 | Some((size, align)), | |
460 | interpret::MemoryKind::Machine(MemoryKind::Heap), | |
461 | )?; | |
462 | } | |
463 | } | |
1b1a35ee | 464 | _ => { |
f2b60f7d FG |
465 | throw_unsup_format!( |
466 | "intrinsic `{intrinsic_name}` is not supported at compile-time" | |
467 | ); | |
1b1a35ee XL |
468 | } |
469 | } | |
470 | ||
471 | ecx.go_to_block(ret); | |
472 | Ok(()) | |
dfeec247 XL |
473 | } |
474 | ||
475 | fn assert_panic( | |
476 | ecx: &mut InterpCx<'mir, 'tcx, Self>, | |
dfeec247 XL |
477 | msg: &AssertMessage<'tcx>, |
478 | _unwind: Option<mir::BasicBlock>, | |
479 | ) -> InterpResult<'tcx> { | |
ba9703b0 | 480 | use rustc_middle::mir::AssertKind::*; |
f035d41b XL |
481 | // Convert `AssertKind<Operand>` to `AssertKind<Scalar>`. |
482 | let eval_to_int = | |
6a06907d | 483 | |op| ecx.read_immediate(&ecx.eval_operand(op, None)?).map(|x| x.to_const_int()); |
74b04a01 | 484 | let err = match msg { |
dfeec247 | 485 | BoundsCheck { ref len, ref index } => { |
f035d41b XL |
486 | let len = eval_to_int(len)?; |
487 | let index = eval_to_int(index)?; | |
74b04a01 | 488 | BoundsCheck { len, index } |
dfeec247 | 489 | } |
f035d41b XL |
490 | Overflow(op, l, r) => Overflow(*op, eval_to_int(l)?, eval_to_int(r)?), |
491 | OverflowNeg(op) => OverflowNeg(eval_to_int(op)?), | |
492 | DivisionByZero(op) => DivisionByZero(eval_to_int(op)?), | |
493 | RemainderByZero(op) => RemainderByZero(eval_to_int(op)?), | |
74b04a01 XL |
494 | ResumedAfterReturn(generator_kind) => ResumedAfterReturn(*generator_kind), |
495 | ResumedAfterPanic(generator_kind) => ResumedAfterPanic(*generator_kind), | |
496 | }; | |
497 | Err(ConstEvalErrKind::AssertFailure(err).into()) | |
dfeec247 XL |
498 | } |
499 | ||
fc512014 XL |
500 | fn abort(_ecx: &mut InterpCx<'mir, 'tcx, Self>, msg: String) -> InterpResult<'tcx, !> { |
501 | Err(ConstEvalErrKind::Abort(msg).into()) | |
502 | } | |
503 | ||
dfeec247 XL |
504 | fn binary_ptr_op( |
505 | _ecx: &InterpCx<'mir, 'tcx, Self>, | |
506 | _bin_op: mir::BinOp, | |
6a06907d XL |
507 | _left: &ImmTy<'tcx>, |
508 | _right: &ImmTy<'tcx>, | |
dfeec247 | 509 | ) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> { |
f2b60f7d | 510 | throw_unsup_format!("pointer arithmetic or comparison is not supported at compile-time"); |
dfeec247 XL |
511 | } |
512 | ||
dfeec247 | 513 | fn before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { |
ba9703b0 XL |
514 | // The step limit has already been hit in a previous call to `before_terminator`. |
515 | if ecx.machine.steps_remaining == 0 { | |
74b04a01 XL |
516 | return Ok(()); |
517 | } | |
518 | ||
ba9703b0 XL |
519 | ecx.machine.steps_remaining -= 1; |
520 | if ecx.machine.steps_remaining == 0 { | |
521 | throw_exhaust!(StepLimitReached) | |
522 | } | |
dfeec247 | 523 | |
ba9703b0 XL |
524 | Ok(()) |
525 | } | |
dfeec247 | 526 | |
04454e1e FG |
527 | #[inline(always)] |
528 | fn expose_ptr( | |
529 | _ecx: &mut InterpCx<'mir, 'tcx, Self>, | |
530 | _ptr: Pointer<AllocId>, | |
531 | ) -> InterpResult<'tcx> { | |
f2b60f7d FG |
532 | // This is only reachable with -Zunleash-the-miri-inside-of-you. |
533 | throw_unsup_format!("exposing pointers is not possible at compile-time") | |
04454e1e FG |
534 | } |
535 | ||
3dfed10e XL |
536 | #[inline(always)] |
537 | fn init_frame_extra( | |
538 | ecx: &mut InterpCx<'mir, 'tcx, Self>, | |
539 | frame: Frame<'mir, 'tcx>, | |
540 | ) -> InterpResult<'tcx, Frame<'mir, 'tcx>> { | |
541 | // Enforce stack size limit. Add 1 because this is run before the new frame is pushed. | |
136023e0 | 542 | if !ecx.recursion_limit.value_within_limit(ecx.stack().len() + 1) { |
3dfed10e XL |
543 | throw_exhaust!(StackFrameLimitReached) |
544 | } else { | |
545 | Ok(frame) | |
546 | } | |
547 | } | |
548 | ||
ba9703b0 | 549 | #[inline(always)] |
a2a8927a | 550 | fn stack<'a>( |
ba9703b0 | 551 | ecx: &'a InterpCx<'mir, 'tcx, Self>, |
064997fb | 552 | ) -> &'a [Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>] { |
ba9703b0 | 553 | &ecx.machine.stack |
dfeec247 XL |
554 | } |
555 | ||
556 | #[inline(always)] | |
a2a8927a | 557 | fn stack_mut<'a>( |
ba9703b0 | 558 | ecx: &'a mut InterpCx<'mir, 'tcx, Self>, |
064997fb | 559 | ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>> { |
ba9703b0 | 560 | &mut ecx.machine.stack |
dfeec247 XL |
561 | } |
562 | ||
ba9703b0 | 563 | fn before_access_global( |
04454e1e FG |
564 | _tcx: TyCtxt<'tcx>, |
565 | machine: &Self, | |
ba9703b0 | 566 | alloc_id: AllocId, |
5e7ed085 | 567 | alloc: ConstAllocation<'tcx>, |
ba9703b0 XL |
568 | static_def_id: Option<DefId>, |
569 | is_write: bool, | |
dfeec247 | 570 | ) -> InterpResult<'tcx> { |
5e7ed085 | 571 | let alloc = alloc.inner(); |
ba9703b0 XL |
572 | if is_write { |
573 | // Write access. These are never allowed, but we give a targeted error message. | |
5e7ed085 | 574 | if alloc.mutability == Mutability::Not { |
ba9703b0 XL |
575 | Err(err_ub!(WriteToReadOnly(alloc_id)).into()) |
576 | } else { | |
577 | Err(ConstEvalErrKind::ModifiedGlobal.into()) | |
578 | } | |
dfeec247 | 579 | } else { |
ba9703b0 | 580 | // Read access. These are usually allowed, with some exceptions. |
04454e1e | 581 | if machine.can_access_statics { |
ba9703b0 XL |
582 | // Machine configuration allows us read from anything (e.g., `static` initializer). |
583 | Ok(()) | |
584 | } else if static_def_id.is_some() { | |
585 | // Machine configuration does not allow us to read statics | |
586 | // (e.g., `const` initializer). | |
f9f354fc XL |
587 | // See const_eval::machine::MemoryExtra::can_access_statics for why |
588 | // this check is so important: if we could read statics, we could read pointers | |
589 | // to mutable allocations *inside* statics. These allocations are not themselves | |
590 | // statics, so pointers to them can get around the check in `validity.rs`. | |
ba9703b0 XL |
591 | Err(ConstEvalErrKind::ConstAccessesStatic.into()) |
592 | } else { | |
593 | // Immutable global, this read is fine. | |
594 | // But make sure we never accept a read from something mutable, that would be | |
595 | // unsound. The reason is that as the content of this allocation may be different | |
596 | // now and at run-time, so if we permit reading now we might return the wrong value. | |
5e7ed085 | 597 | assert_eq!(alloc.mutability, Mutability::Not); |
ba9703b0 XL |
598 | Ok(()) |
599 | } | |
dfeec247 XL |
600 | } |
601 | } | |
602 | } | |
603 | ||
604 | // Please do not add any code below the above `Machine` trait impl. I (oli-obk) plan more cleanups | |
605 | // so we can end up having a file with just that impl, but for now, let's keep the impl discoverable | |
606 | // at the bottom of this file. |