1use std::{borrow::Cow, cell::RefCell, fmt::Write, iter, mem, ops::Range};
4
5use base_db::{Crate, target::TargetLoadError};
6use either::Either;
7use hir_def::{
8 AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, StaticId,
9 VariantId,
10 expr_store::{Body, ExpressionStore, HygieneId},
11 item_tree::FieldsShape,
12 lang_item::LangItems,
13 layout::{TagEncoding, Variants},
14 resolver::{HasResolver, ValueNs},
15 signatures::{
16 EnumSignature, FunctionSignature, StaticFlags, StaticSignature, StructFlags,
17 StructSignature, TraitSignature,
18 },
19};
20use hir_expand::{InFile, mod_path::path};
21use la_arena::ArenaMap;
22use macros::GenericTypeVisitable;
23use rustc_abi::{Size, TargetDataLayout};
24use rustc_apfloat::{
25 Float,
26 ieee::{Half as f16, Quad as f128},
27};
28use rustc_ast_ir::Mutability;
29use rustc_hash::{FxHashMap, FxHashSet};
30use rustc_type_ir::{
31 AliasTyKind,
32 inherent::{GenericArgs as _, IntoKind, Region as _, SliceLike, Ty as _},
33};
34use salsa::SalsaValue;
35use span::FileId;
36use stdx::never;
37use syntax::{SyntaxNodePtr, TextRange};
38use triomphe::Arc;
39
40use crate::{
41 CallableDefId, ComplexMemoryMap, InferBodyId, InferenceResult, MemoryMap, ParamEnvAndCrate,
42 consteval::{self, ConstEvalError, try_const_usize},
43 db::{GeneralConstId, HirDatabase, InternedClosureId},
44 display::{ClosureStyle, DisplayTarget, HirDisplay},
45 infer::PointerCast,
46 layout::{Layout, LayoutError, RustcEnumVariantIdx},
47 method_resolution::{is_dyn_method, lookup_impl_const},
48 next_solver::{
49 AliasTy, Allocation, AllocationData, Const, ConstKind, DbInterner, ErrorGuaranteed,
50 GenericArgs, Region, StoredTy, Ty, TyKind, TypingMode, UnevaluatedConst, ValTree,
51 infer::{DbInternerInferExt, InferCtxt, traits::ObligationCause},
52 obligation_ctxt::ObligationCtxt,
53 },
54 traits::FnTrait,
55 utils::detect_variant_from_bytes,
56};
57
58use super::{
59 AggregateKind, BasicBlockId, BinOp, CastKind, LocalId, MirBody, MirLowerError, MirSpan,
60 Operand, OperandKind, Place, PlaceElem, PlaceTy, ProjectionElem, Rvalue, StatementKind,
61 StoredPlace, TerminatorKind, UnOp, return_slot,
62};
63
64mod shim;
65#[cfg(test)]
66mod tests;
67
68macro_rules! from_bytes {
69 ($ty:tt, $value:expr) => {
70 ($ty::from_le_bytes(match ($value).try_into() {
71 Ok(it) => it,
72 Err(_) => return Err(MirEvalError::InternalError(stringify!(mismatched size in constructing $ty).into())),
73 }))
74 };
75 ($apfloat:tt, $bits:tt, $value:expr) => {
76 $apfloat::from_bits($bits::from_le_bytes(match ($value).try_into() {
78 Ok(it) => it,
79 Err(_) => return Err(MirEvalError::InternalError(stringify!(mismatched size in constructing $apfloat).into())),
80 }).into())
81 };
82}
83use from_bytes;
84
85macro_rules! not_supported {
86 ($it: expr) => {
87 return Err($crate::mir::eval::MirEvalError::NotSupported(format!($it)))
88 };
89}
90use not_supported;
91
92#[derive(Debug, Default, Clone, PartialEq, Eq, GenericTypeVisitable)]
93pub struct VTableMap<'db> {
94 ty_to_id: FxHashMap<Ty<'db>, usize>,
95 id_to_ty: Vec<Ty<'db>>,
96}
97
98impl<'db> VTableMap<'db> {
99 const OFFSET: usize = 1000; fn id(&mut self, ty: Ty<'db>) -> usize {
102 if let Some(it) = self.ty_to_id.get(&ty) {
103 return *it;
104 }
105 let id = self.id_to_ty.len() + VTableMap::OFFSET;
106 self.id_to_ty.push(ty);
107 self.ty_to_id.insert(ty, id);
108 id
109 }
110
111 pub(crate) fn ty(&self, id: usize) -> Result<'db, Ty<'db>> {
112 id.checked_sub(VTableMap::OFFSET)
113 .and_then(|id| self.id_to_ty.get(id).copied())
114 .ok_or(MirEvalError::InvalidVTableId(id))
115 }
116
117 fn ty_of_bytes(&self, bytes: &[u8]) -> Result<'db, Ty<'db>> {
118 let id = from_bytes!(usize, bytes);
119 self.ty(id)
120 }
121
122 pub fn shrink_to_fit(&mut self) {
123 self.id_to_ty.shrink_to_fit();
124 self.ty_to_id.shrink_to_fit();
125 }
126
127 fn is_empty(&self) -> bool {
128 self.id_to_ty.is_empty() && self.ty_to_id.is_empty()
129 }
130}
131
132#[derive(Debug, Default, Clone, PartialEq, Eq)]
133struct TlsData {
134 keys: Vec<u128>,
135}
136
137impl TlsData {
138 fn create_key(&mut self) -> usize {
139 self.keys.push(0);
140 self.keys.len() - 1
141 }
142
143 fn get_key(&mut self, key: usize) -> Result<'static, u128> {
144 let r = self.keys.get(key).ok_or_else(|| {
145 MirEvalError::UndefinedBehavior(format!("Getting invalid tls key {key}"))
146 })?;
147 Ok(*r)
148 }
149
150 fn set_key(&mut self, key: usize, value: u128) -> Result<'static, ()> {
151 let r = self.keys.get_mut(key).ok_or_else(|| {
152 MirEvalError::UndefinedBehavior(format!("Setting invalid tls key {key}"))
153 })?;
154 *r = value;
155 Ok(())
156 }
157}
158
159struct StackFrame<'a, 'db> {
160 locals: Locals<'a, 'db>,
161 destination: Option<BasicBlockId>,
162 prev_stack_ptr: usize,
163 span: (MirSpan, InferBodyId<'db>),
164}
165
166#[derive(Clone)]
167enum MirOrDynIndex<'db> {
168 Mir(&'db MirBody<'db>),
169 Dyn(usize),
170}
171
172pub struct Evaluator<'a, 'db> {
173 db: &'db dyn HirDatabase,
174 param_env: ParamEnvAndCrate<'db>,
175 target_data_layout: &'db TargetDataLayout,
176 stack: Vec<u8>,
177 heap: Vec<u8>,
178 code_stack: Vec<StackFrame<'a, 'db>>,
179 static_locations: FxHashMap<StaticId, Address>,
182 vtable_map: VTableMap<'db>,
186 thread_local_storage: TlsData,
187 random_state: oorandom::Rand64,
188 stdout: Vec<u8>,
189 stderr: Vec<u8>,
190 layout_cache: RefCell<FxHashMap<Ty<'db>, Arc<Layout>>>,
191 projected_ty_cache: RefCell<FxHashMap<(PlaceTy<'db>, PlaceElem), PlaceTy<'db>>>,
192 not_special_fn_cache: RefCell<FxHashSet<FunctionId>>,
193 mir_or_dyn_index_cache: RefCell<FxHashMap<(FunctionId, GenericArgs<'db>), MirOrDynIndex<'db>>>,
194 unused_locals_store: RefCell<FxHashMap<InferBodyId<'db>, Vec<Locals<'a, 'db>>>>,
198 cached_ptr_size: usize,
199 cached_fn_trait_func: Option<FunctionId>,
200 cached_fn_mut_trait_func: Option<FunctionId>,
201 cached_fn_once_trait_func: Option<FunctionId>,
202 crate_id: Crate,
203 assert_placeholder_ty_is_unused: bool,
205 execution_limit: usize,
207 stack_depth_limit: usize,
209 memory_limit: usize,
211 infcx: InferCtxt<'db>,
212}
213
214#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
215enum Address {
216 Stack(usize),
217 Heap(usize),
218 Invalid(usize),
219}
220
221use Address::*;
222
223#[derive(Debug, Clone, Copy)]
224struct Interval {
225 addr: Address,
226 size: usize,
227}
228
229#[derive(Debug, Clone)]
230struct IntervalAndTy<'db> {
231 interval: Interval,
232 ty: Ty<'db>,
233}
234
235impl Interval {
236 fn new(addr: Address, size: usize) -> Self {
237 Self { addr, size }
238 }
239
240 fn get<'b, 'a, 'db>(&self, memory: &'b Evaluator<'a, 'db>) -> Result<'db, &'b [u8]> {
241 memory.read_memory(self.addr, self.size)
242 }
243
244 fn write_from_bytes<'a, 'db>(
245 &self,
246 memory: &mut Evaluator<'a, 'db>,
247 bytes: &[u8],
248 ) -> Result<'db, ()> {
249 memory.write_memory(self.addr, bytes)
250 }
251
252 fn write_from_interval<'a, 'db>(
253 &self,
254 memory: &mut Evaluator<'a, 'db>,
255 interval: Interval,
256 ) -> Result<'db, ()> {
257 memory.copy_from_interval(self.addr, interval)
258 }
259
260 fn slice(self, range: Range<usize>) -> Interval {
261 Interval { addr: self.addr.offset(range.start), size: range.len() }
262 }
263}
264
265impl<'db> IntervalAndTy<'db> {
266 fn get<'b, 'a>(&self, memory: &'b Evaluator<'a, 'db>) -> Result<'db, &'b [u8]> {
267 memory.read_memory(self.interval.addr, self.interval.size)
268 }
269
270 fn new<'a>(
271 addr: Address,
272 ty: Ty<'db>,
273 evaluator: &Evaluator<'a, 'db>,
274 locals: &Locals<'a, 'db>,
275 ) -> Result<'db, IntervalAndTy<'db>> {
276 let size = evaluator.size_of_sized(ty, locals, "type of interval")?;
277 Ok(IntervalAndTy { interval: Interval { addr, size }, ty })
278 }
279}
280
281enum IntervalOrOwned {
282 Owned(Vec<u8>),
283 Borrowed(Interval),
284}
285
286impl From<Interval> for IntervalOrOwned {
287 fn from(it: Interval) -> IntervalOrOwned {
288 IntervalOrOwned::Borrowed(it)
289 }
290}
291
292impl IntervalOrOwned {
293 fn get<'b, 'a, 'db>(&'b self, memory: &'b Evaluator<'a, 'db>) -> Result<'db, &'b [u8]> {
294 Ok(match self {
295 IntervalOrOwned::Owned(o) => o,
296 IntervalOrOwned::Borrowed(b) => b.get(memory)?,
297 })
298 }
299}
300
301#[cfg(target_pointer_width = "64")]
302const STACK_OFFSET: usize = 1 << 60;
303#[cfg(target_pointer_width = "64")]
304const HEAP_OFFSET: usize = 1 << 59;
305
306#[cfg(target_pointer_width = "32")]
307const STACK_OFFSET: usize = 1 << 30;
308#[cfg(target_pointer_width = "32")]
309const HEAP_OFFSET: usize = 1 << 29;
310
311impl Address {
312 fn from_bytes<'db>(it: &[u8]) -> Result<'db, Self> {
313 Ok(Address::from_usize(from_bytes!(usize, it)))
314 }
315
316 fn from_usize(it: usize) -> Self {
317 if it > STACK_OFFSET {
318 Stack(it - STACK_OFFSET)
319 } else if it > HEAP_OFFSET {
320 Heap(it - HEAP_OFFSET)
321 } else {
322 Invalid(it)
323 }
324 }
325
326 fn to_bytes(&self) -> [u8; size_of::<usize>()] {
327 usize::to_le_bytes(self.to_usize())
328 }
329
330 fn to_usize(&self) -> usize {
331 match self {
332 Stack(it) => *it + STACK_OFFSET,
333 Heap(it) => *it + HEAP_OFFSET,
334 Invalid(it) => *it,
335 }
336 }
337
338 fn map(&self, f: impl FnOnce(usize) -> usize) -> Address {
339 match self {
340 Stack(it) => Stack(f(*it)),
341 Heap(it) => Heap(f(*it)),
342 Invalid(it) => Invalid(f(*it)),
343 }
344 }
345
346 fn offset(&self, offset: usize) -> Address {
347 self.map(|it| it + offset)
348 }
349}
350
351#[derive(Clone, PartialEq, Eq, SalsaValue)]
352pub enum MirEvalError<'db> {
353 ConstEvalError(String, Box<ConstEvalError<'db>>),
354 LayoutError(LayoutError, StoredTy),
355 TargetDataLayoutNotAvailable(TargetLoadError),
356 UndefinedBehavior(String),
359 Panic(String),
360 MirLowerError(FunctionId, MirLowerError<'db>),
362 MirLowerErrorForClosure(InternedClosureId<'db>, MirLowerError<'db>),
363 TypeIsUnsized(StoredTy, &'static str),
364 NotSupported(String),
365 InvalidConst,
366 InFunction(
367 Box<MirEvalError<'db>>,
368 Vec<(Either<FunctionId, InternedClosureId<'db>>, MirSpan, InferBodyId<'db>)>,
369 ),
370 ExecutionLimitExceeded,
371 StackOverflow,
372 InvalidVTableId(usize),
374 CoerceUnsizedError(StoredTy),
376 InternalError(Box<str>),
378}
379
380impl MirEvalError<'_> {
381 pub fn pretty_print(
382 &self,
383 f: &mut String,
384 db: &dyn HirDatabase,
385 span_formatter: impl Fn(FileId, TextRange) -> String,
386 display_target: DisplayTarget,
387 ) -> std::result::Result<(), std::fmt::Error> {
388 writeln!(f, "Mir eval error:")?;
389 let mut err = self;
390 while let MirEvalError::InFunction(e, stack) = err {
391 err = e;
392 for (func, span, def) in stack.iter().take(30).rev() {
393 match func {
394 Either::Left(func) => {
395 let function_name = FunctionSignature::of(db, *func);
396 writeln!(
397 f,
398 "In function {} ({:?})",
399 function_name.name.display(db, display_target.edition),
400 func
401 )?;
402 }
403 Either::Right(closure) => {
404 writeln!(f, "In {closure:?}")?;
405 }
406 }
407 let (source_map, self_param_syntax) = match *def {
408 InferBodyId::DefWithBodyId(def) => {
409 let body = &Body::with_source_map(db, def).1;
410 (&**body, body.self_param_syntax())
411 }
412 InferBodyId::AnonConstId(def) => {
413 let store = ExpressionStore::with_source_map(db, def.loc(db).owner).1;
414 (store, None)
415 }
416 };
417 let span: InFile<SyntaxNodePtr> = match *span {
418 MirSpan::ExprId(e) => match source_map.expr_syntax(e) {
419 Ok(s) => s.map(|it| it.into()),
420 Err(_) => continue,
421 },
422 MirSpan::PatId(p) => match source_map.pat_syntax(p) {
423 Ok(s) => s.map(|it| it.syntax_node_ptr()),
424 Err(_) => continue,
425 },
426 MirSpan::BindingId(b) => {
427 match source_map
428 .patterns_for_binding(b)
429 .iter()
430 .find_map(|p| source_map.pat_syntax(*p).ok())
431 {
432 Some(s) => s.map(|it| it.syntax_node_ptr()),
433 None => continue,
434 }
435 }
436 MirSpan::SelfParam => match self_param_syntax {
437 Some(s) => s.map(|it| it.syntax_node_ptr()),
438 None => continue,
439 },
440 MirSpan::Unknown => continue,
441 };
442 let file_id = span.file_id.original_file(db);
443 let text_range = span.value.text_range();
444 writeln!(f, "{}", span_formatter(file_id.file_id(db), text_range))?;
445 }
446 }
447 match err {
448 MirEvalError::InFunction(..) => unreachable!(),
449 MirEvalError::LayoutError(err, ty) => {
450 write!(
451 f,
452 "Layout for type `{}` is not available due {err:?}",
453 ty.as_ref()
454 .display(db, display_target)
455 .with_closure_style(ClosureStyle::ClosureWithId)
456 )?;
457 }
458 MirEvalError::MirLowerError(func, err) => {
459 let function_name = FunctionSignature::of(db, *func);
460 let self_ = match func.lookup(db).container {
461 ItemContainerId::ImplId(impl_id) => Some({
462 db.impl_self_ty(impl_id)
463 .instantiate_identity()
464 .skip_norm_wip()
465 .display(db, display_target)
466 .to_string()
467 }),
468 ItemContainerId::TraitId(it) => Some(
469 TraitSignature::of(db, it)
470 .name
471 .display(db, display_target.edition)
472 .to_string(),
473 ),
474 _ => None,
475 };
476 writeln!(
477 f,
478 "MIR lowering for function `{}{}{}` ({:?}) failed due:",
479 self_.as_deref().unwrap_or_default(),
480 if self_.is_some() { "::" } else { "" },
481 function_name.name.display(db, display_target.edition),
482 func
483 )?;
484 err.pretty_print(f, db, span_formatter, display_target)?;
485 }
486 MirEvalError::ConstEvalError(name, err) => {
487 MirLowerError::ConstEvalError((**name).into(), err.clone()).pretty_print(
488 f,
489 db,
490 span_formatter,
491 display_target,
492 )?;
493 }
494 MirEvalError::UndefinedBehavior(_)
495 | MirEvalError::TargetDataLayoutNotAvailable(_)
496 | MirEvalError::Panic(_)
497 | MirEvalError::MirLowerErrorForClosure(_, _)
498 | MirEvalError::TypeIsUnsized(_, _)
499 | MirEvalError::NotSupported(_)
500 | MirEvalError::InvalidConst
501 | MirEvalError::ExecutionLimitExceeded
502 | MirEvalError::StackOverflow
503 | MirEvalError::CoerceUnsizedError(_)
504 | MirEvalError::InternalError(_)
505 | MirEvalError::InvalidVTableId(_) => writeln!(f, "{err:?}")?,
506 }
507 Ok(())
508 }
509
510 pub fn is_panic(&self) -> Option<&str> {
511 let mut err = self;
512 while let MirEvalError::InFunction(e, _) = err {
513 err = e;
514 }
515 match err {
516 MirEvalError::Panic(msg) => Some(msg),
517 _ => None,
518 }
519 }
520}
521
522impl std::fmt::Debug for MirEvalError<'_> {
523 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
524 match self {
525 Self::ConstEvalError(arg0, arg1) => {
526 f.debug_tuple("ConstEvalError").field(arg0).field(arg1).finish()
527 }
528 Self::LayoutError(arg0, arg1) => {
529 f.debug_tuple("LayoutError").field(arg0).field(arg1).finish()
530 }
531 Self::UndefinedBehavior(arg0) => {
532 f.debug_tuple("UndefinedBehavior").field(arg0).finish()
533 }
534 Self::Panic(msg) => write!(f, "Panic with message:\n{msg:?}"),
535 Self::TargetDataLayoutNotAvailable(arg0) => {
536 f.debug_tuple("TargetDataLayoutNotAvailable").field(arg0).finish()
537 }
538 Self::TypeIsUnsized(ty, it) => write!(f, "{ty:?} is unsized. {it} should be sized."),
539 Self::ExecutionLimitExceeded => write!(f, "execution limit exceeded"),
540 Self::StackOverflow => write!(f, "stack overflow"),
541 Self::MirLowerError(arg0, arg1) => {
542 f.debug_tuple("MirLowerError").field(arg0).field(arg1).finish()
543 }
544 Self::MirLowerErrorForClosure(arg0, arg1) => {
545 f.debug_tuple("MirLowerError").field(arg0).field(arg1).finish()
546 }
547 Self::CoerceUnsizedError(arg0) => {
548 f.debug_tuple("CoerceUnsizedError").field(arg0).finish()
549 }
550 Self::InternalError(arg0) => f.debug_tuple("InternalError").field(arg0).finish(),
551 Self::InvalidVTableId(arg0) => f.debug_tuple("InvalidVTableId").field(arg0).finish(),
552 Self::NotSupported(arg0) => f.debug_tuple("NotSupported").field(arg0).finish(),
553 Self::InvalidConst => f.write_str("InvalidConst"),
554 Self::InFunction(e, stack) => {
555 f.debug_struct("WithStack").field("error", e).field("stack", &stack).finish()
556 }
557 }
558 }
559}
560
561type Result<'db, T> = std::result::Result<T, MirEvalError<'db>>;
562
563#[derive(Debug, Default)]
564struct DropFlags<'db> {
565 need_drop: FxHashSet<Place<'db>>,
566}
567
568impl<'db> DropFlags<'db> {
569 fn add_place(&mut self, p: Place<'db>) {
570 if p.iterate_over_parents().any(|it| self.need_drop.contains(&it)) {
571 return;
572 }
573 self.need_drop.retain(|it| !p.is_parent(*it));
574 self.need_drop.insert(p);
575 }
576
577 fn remove_place(&mut self, p: Place<'db>) -> bool {
578 if let Some(parent) = p.iterate_over_parents().find(|it| self.need_drop.contains(it)) {
580 self.need_drop.remove(&parent);
581 return true;
582 }
583 self.need_drop.remove(&p)
584 }
585
586 fn clear(&mut self) {
587 self.need_drop.clear();
588 }
589}
590
591#[derive(Debug)]
592struct Locals<'a, 'db> {
593 ptr: ArenaMap<LocalId, Interval>,
594 body: &'db MirBody<'db>,
595 drop_flags: DropFlags<'a>,
596}
597
598pub struct MirOutput {
599 stdout: Vec<u8>,
600 stderr: Vec<u8>,
601}
602
603impl MirOutput {
604 pub fn stdout(&self) -> Cow<'_, str> {
605 String::from_utf8_lossy(&self.stdout)
606 }
607 pub fn stderr(&self) -> Cow<'_, str> {
608 String::from_utf8_lossy(&self.stderr)
609 }
610}
611
612pub fn interpret_mir<'db>(
613 db: &'db dyn HirDatabase,
614 body: &'db MirBody<'db>,
615 assert_placeholder_ty_is_unused: bool,
621 trait_env: Option<ParamEnvAndCrate<'db>>,
622) -> Result<'db, (Result<'db, Allocation<'db>>, MirOutput)> {
623 let ty = body.locals[return_slot()].ty.as_ref();
624 let mut evaluator = Evaluator::new(db, body.owner, assert_placeholder_ty_is_unused, trait_env)?;
625 let it: Result<'db, Allocation<'db>> = (|| {
626 if evaluator.ptr_size() != size_of::<usize>() {
627 not_supported!("targets with different pointer size from host");
628 }
629 let interval = evaluator.interpret_mir(body, None.into_iter())?;
630 let bytes = interval.get(&evaluator)?;
631 let mut memory_map = evaluator.create_memory_map(
632 bytes,
633 ty,
634 &Locals { ptr: ArenaMap::new(), body, drop_flags: DropFlags::default() },
635 )?;
636 let bytes = Box::from(bytes);
637 let memory_map = if memory_map.memory.is_empty() && evaluator.vtable_map.is_empty() {
638 MemoryMap::Empty
639 } else {
640 memory_map.vtable = mem::take(&mut evaluator.vtable_map);
641 memory_map.vtable.shrink_to_fit();
642 MemoryMap::Complex(Box::new(memory_map))
643 };
644 Ok(Allocation::new(AllocationData { ty, memory: bytes, memory_map }))
645 })();
646 Ok((it, MirOutput { stdout: evaluator.stdout, stderr: evaluator.stderr }))
647}
648
649#[cfg(test)]
650const EXECUTION_LIMIT: usize = 100_000;
651#[cfg(not(test))]
652const EXECUTION_LIMIT: usize = 10_000_000;
653
654impl<'a, 'db> Evaluator<'a, 'db> {
655 pub fn new(
656 db: &'db dyn HirDatabase,
657 owner: InferBodyId<'db>,
658 assert_placeholder_ty_is_unused: bool,
659 trait_env: Option<ParamEnvAndCrate<'db>>,
660 ) -> Result<'db, Evaluator<'a, 'db>> {
661 let module = owner.module(db);
662 let crate_id = module.krate(db);
663 let target_data_layout = match db.target_data_layout(crate_id) {
664 Ok(target_data_layout) => target_data_layout,
665 Err(e) => return Err(MirEvalError::TargetDataLayoutNotAvailable(e)),
666 };
667 let cached_ptr_size = target_data_layout.pointer_size().bytes_usize();
668 let interner = DbInterner::new_with(db, crate_id);
669 let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
670 let lang_items = interner.lang_items();
671 Ok(Evaluator {
672 target_data_layout,
673 stack: vec![0],
674 heap: vec![0],
675 code_stack: vec![],
676 vtable_map: VTableMap::default(),
677 thread_local_storage: TlsData::default(),
678 static_locations: Default::default(),
679 db,
680 random_state: oorandom::Rand64::new(0),
681 param_env: trait_env.unwrap_or_else(|| ParamEnvAndCrate {
682 param_env: db.trait_environment(owner.generic_def(db)),
683 krate: crate_id,
684 }),
685 crate_id,
686 stdout: vec![],
687 stderr: vec![],
688 assert_placeholder_ty_is_unused,
689 stack_depth_limit: 100,
690 execution_limit: EXECUTION_LIMIT,
691 memory_limit: 1_000_000_000, layout_cache: RefCell::new(Default::default()),
693 projected_ty_cache: RefCell::new(Default::default()),
694 not_special_fn_cache: RefCell::new(Default::default()),
695 mir_or_dyn_index_cache: RefCell::new(Default::default()),
696 unused_locals_store: RefCell::new(Default::default()),
697 cached_ptr_size,
698 cached_fn_trait_func: lang_items.Fn_call,
699 cached_fn_mut_trait_func: lang_items.FnMut_call_mut,
700 cached_fn_once_trait_func: lang_items.FnOnce_call_once,
701 infcx,
702 })
703 }
704
705 #[inline]
706 fn interner(&self) -> DbInterner<'db> {
707 self.infcx.interner
708 }
709
710 #[inline]
711 fn lang_items(&self) -> &'db LangItems {
712 self.infcx.interner.lang_items()
713 }
714
715 fn place_addr(&self, p: &StoredPlace, locals: &Locals<'a, 'db>) -> Result<'db, Address> {
716 Ok(self.place_addr_and_ty_and_metadata(p, locals)?.0)
717 }
718
719 fn place_interval(&self, p: &StoredPlace, locals: &Locals<'a, 'db>) -> Result<'db, Interval> {
720 let place_addr_and_ty = self.place_addr_and_ty_and_metadata(p, locals)?;
721 Ok(Interval {
722 addr: place_addr_and_ty.0,
723 size: self.size_of_sized(
724 place_addr_and_ty.1,
725 locals,
726 "Type of place that we need its interval",
727 )?,
728 })
729 }
730
731 fn ptr_size(&self) -> usize {
732 self.cached_ptr_size
733 }
734
735 fn caller_location_fields(&self, owner: InferBodyId<'db>, span: MirSpan) -> (String, u32, u32) {
736 let Some((file_id, text_range)) = self.resolve_mir_span(owner, span) else {
737 return (String::new(), 0, 0);
738 };
739 let source_root = self.db.file_source_root(file_id).source_root_id(self.db);
740 let source_root = self.db.source_root(source_root).source_root(self.db);
741 let path = source_root.path_for_file(&file_id).map(|path| path.to_string());
742 let (line, col) = self.db.line_column(file_id, text_range.start()).unwrap_or((0, 0));
743 (path.unwrap_or_default(), line + 1, col + 1)
744 }
745
746 fn resolve_mir_span(
747 &self,
748 owner: InferBodyId<'db>,
749 span: MirSpan,
750 ) -> Option<(FileId, TextRange)> {
751 let (source_map, self_param_syntax) = match owner {
752 InferBodyId::DefWithBodyId(def) => {
753 let body = &Body::with_source_map(self.db, def).1;
754 (&**body, body.self_param_syntax())
755 }
756 InferBodyId::AnonConstId(def) => {
757 (ExpressionStore::with_source_map(self.db, def.loc(self.db).owner).1, None)
758 }
759 };
760 let span: InFile<SyntaxNodePtr> = match span {
761 MirSpan::ExprId(e) => source_map.expr_syntax(e).ok()?.map(|it| it.into()),
762 MirSpan::PatId(p) => source_map.pat_syntax(p).ok()?.map(|it| it.syntax_node_ptr()),
763 MirSpan::BindingId(b) => source_map
764 .patterns_for_binding(b)
765 .iter()
766 .find_map(|p| source_map.pat_syntax(*p).ok())?
767 .map(|it| it.syntax_node_ptr()),
768 MirSpan::SelfParam => self_param_syntax?.map(|it| it.syntax_node_ptr()),
769 MirSpan::Unknown => return None,
770 };
771 let file_id = span.file_id.original_file(self.db);
772 Some((file_id.file_id(self.db), span.value.text_range()))
773 }
774
775 fn projected_ty(&self, ty: PlaceTy<'db>, proj: PlaceElem) -> PlaceTy<'db> {
776 let pair = (ty, proj);
777 if let Some(r) = self.projected_ty_cache.borrow().get(&pair) {
778 return *r;
779 }
780 let (ty, proj) = pair;
781 let r = ty.projection_ty(&self.infcx, &proj, self.param_env.param_env);
782 self.projected_ty_cache.borrow_mut().insert((ty, proj), r);
783 r
784 }
785
786 fn place_addr_and_ty_and_metadata<'b>(
787 &'b self,
788 p: &StoredPlace,
789 locals: &'b Locals<'a, 'db>,
790 ) -> Result<'db, (Address, Ty<'db>, Option<IntervalOrOwned>)> {
791 let mut addr = locals.ptr[p.local].addr;
792 let mut ty = PlaceTy::from_ty(locals.body.locals[p.local].ty.as_ref());
793 let mut metadata: Option<IntervalOrOwned> = None; for proj in p.projection.as_slice() {
795 let prev_ty = ty;
796 ty = self.projected_ty(ty, *proj);
797 match proj {
798 ProjectionElem::Deref => {
799 metadata = if self.size_align_of(ty.ty, locals)?.is_none() {
800 Some(
801 Interval { addr: addr.offset(self.ptr_size()), size: self.ptr_size() }
802 .into(),
803 )
804 } else {
805 None
806 };
807 let it = from_bytes!(usize, self.read_memory(addr, self.ptr_size())?);
808 addr = Address::from_usize(it);
809 }
810 ProjectionElem::Index(op) => {
811 let offset = from_bytes!(
812 usize,
813 self.read_memory(locals.ptr[*op].addr, self.ptr_size())?
814 );
815 metadata = None; let ty_size = self.size_of_sized(ty.ty, locals, "array inner type")?;
817 addr = addr.offset(ty_size * offset);
818 }
819 &ProjectionElem::ConstantIndex { from_end, offset } => {
820 let offset = if from_end {
821 let len = match prev_ty.ty.kind() {
822 TyKind::Array(_, c) => match try_const_usize(self.db, c) {
823 Some(it) => it as u64,
824 None => {
825 not_supported!("indexing array with unknown const from end")
826 }
827 },
828 TyKind::Slice(_) => match metadata {
829 Some(it) => from_bytes!(u64, it.get(self)?),
830 None => not_supported!("slice place without metadata"),
831 },
832 _ => not_supported!("bad type for const index"),
833 };
834 (len - offset - 1) as usize
835 } else {
836 offset as usize
837 };
838 metadata = None; let ty_size = self.size_of_sized(ty.ty, locals, "array inner type")?;
840 addr = addr.offset(ty_size * offset);
841 }
842 &ProjectionElem::Subslice { from, to } => {
843 let inner_ty = match ty.ty.kind() {
844 TyKind::Array(inner, _) | TyKind::Slice(inner) => inner,
845 _ => Ty::new_error(self.interner(), ErrorGuaranteed),
846 };
847 metadata = match metadata {
848 Some(it) => {
849 let prev_len = from_bytes!(u64, it.get(self)?);
850 Some(IntervalOrOwned::Owned(
851 (prev_len - from - to).to_le_bytes().to_vec(),
852 ))
853 }
854 None => None,
855 };
856 let ty_size = self.size_of_sized(inner_ty, locals, "array inner type")?;
857 addr = addr.offset(ty_size * (from as usize));
858 }
859 ProjectionElem::Field(f) => {
860 let layout = self.layout(prev_ty.ty)?;
861 let variant_layout = match &layout.variants {
862 Variants::Single { .. } | Variants::Empty => &layout,
863 Variants::Multiple { variants, .. } => {
864 &variants[match prev_ty.variant_id {
865 Some(hir_def::VariantId::EnumVariantId(it)) => {
866 RustcEnumVariantIdx(it.index(self.db))
867 }
868 _ => {
869 return Err(MirEvalError::InternalError(
870 "mismatched layout".into(),
871 ));
872 }
873 }]
874 }
875 };
876 let offset = variant_layout.fields.offset(f.0 as usize).bytes_usize();
877 addr = addr.offset(offset);
878 if self.size_align_of(ty.ty, locals)?.is_some() {
880 metadata = None;
881 }
882 }
883 ProjectionElem::Downcast(_) => {
884 }
886 }
887 }
888 Ok((addr, ty.ty, metadata))
889 }
890
891 fn layout(&self, ty: Ty<'db>) -> Result<'db, Arc<Layout>> {
892 if let Some(x) = self.layout_cache.borrow().get(&ty) {
893 return Ok(x.clone());
894 }
895 let r = self
896 .db
897 .layout_of_ty(ty.store(), self.param_env.store())
898 .map_err(|e| MirEvalError::LayoutError(e, ty.store()))?;
899 self.layout_cache.borrow_mut().insert(ty, r.clone());
900 Ok(r)
901 }
902
903 fn layout_adt(&self, adt: AdtId, subst: GenericArgs<'db>) -> Result<'db, Arc<Layout>> {
904 self.layout(Ty::new_adt(self.interner(), adt, subst))
905 }
906
907 fn place_ty<'b>(
908 &'b self,
909 p: &StoredPlace,
910 locals: &'b Locals<'a, 'db>,
911 ) -> Result<'db, Ty<'db>> {
912 Ok(self.place_addr_and_ty_and_metadata(p, locals)?.1)
913 }
914
915 fn operand_ty(&self, o: &Operand, locals: &Locals<'a, 'db>) -> Result<'db, Ty<'db>> {
916 Ok(match &o.kind {
917 OperandKind::Copy(p) | OperandKind::Move(p) => self.place_ty(p, locals)?,
918 OperandKind::Constant { konst: _, ty } => ty.as_ref(),
919 OperandKind::Allocation { allocation } => allocation.as_ref().ty,
920 &OperandKind::Static(s) => {
921 let ty = InferenceResult::of(self.db, DefWithBodyId::from(s))
922 .expr_ty(Body::of(self.db, s.into()).root_expr());
923 Ty::new_ref(
924 self.interner(),
925 Region::new_static(self.interner()),
926 ty,
927 Mutability::Not,
928 )
929 }
930 })
931 }
932
933 fn operand_ty_and_eval(
934 &mut self,
935 o: &Operand,
936 locals: &mut Locals<'a, 'db>,
937 ) -> Result<'db, IntervalAndTy<'db>> {
938 Ok(IntervalAndTy {
939 interval: self.eval_operand(o, locals)?,
940 ty: self.operand_ty(o, locals)?,
941 })
942 }
943
944 fn interpret_mir(
945 &mut self,
946 body: &'db MirBody<'db>,
947 args: impl Iterator<Item = IntervalOrOwned>,
948 ) -> Result<'db, Interval> {
949 if let Some(it) = self.stack_depth_limit.checked_sub(1) {
950 self.stack_depth_limit = it;
951 } else {
952 return Err(MirEvalError::StackOverflow);
953 }
954 let mut current_block_idx = body.start_block;
955 let (mut locals, prev_stack_ptr) = self.create_locals_for_body(body, None)?;
956 self.fill_locals_for_body(body, &mut locals, args)?;
957 let prev_code_stack = mem::take(&mut self.code_stack);
958 let span = (MirSpan::Unknown, body.owner);
959 self.code_stack.push(StackFrame { locals, destination: None, prev_stack_ptr, span });
960 'stack: loop {
961 let Some(mut my_stack_frame) = self.code_stack.pop() else {
962 not_supported!("missing stack frame");
963 };
964 let e = (|| {
965 let locals = &mut my_stack_frame.locals;
966 let body = locals.body.clone();
967 loop {
968 let current_block = &body.basic_blocks[current_block_idx];
969 if let Some(it) = self.execution_limit.checked_sub(1) {
970 self.execution_limit = it;
971 } else {
972 return Err(MirEvalError::ExecutionLimitExceeded);
973 }
974 for statement in ¤t_block.statements {
975 match &statement.kind {
976 StatementKind::Assign(l, r) => {
977 let addr = self.place_addr(l, locals)?;
978 let result = self.eval_rvalue(r, locals)?;
979 self.copy_from_interval_or_owned(addr, result)?;
980 locals.drop_flags.add_place(l.as_ref());
981 }
982 StatementKind::Deinit(_) => not_supported!("de-init statement"),
983 StatementKind::StorageLive(_)
984 | StatementKind::FakeRead(_)
985 | StatementKind::StorageDead(_)
986 | StatementKind::Nop => (),
987 }
988 }
989 let Some(terminator) = current_block.terminator.as_ref() else {
990 not_supported!("block without terminator");
991 };
992 match &terminator.kind {
993 TerminatorKind::Goto { target } => {
994 current_block_idx = *target;
995 }
996 TerminatorKind::Call {
997 func,
998 args,
999 destination,
1000 target,
1001 cleanup: _,
1002 from_hir_call: _,
1003 } => {
1004 let destination_interval = self.place_interval(destination, locals)?;
1005 let fn_ty = self.operand_ty(func, locals)?;
1006 let args = args
1007 .iter()
1008 .map(|it| self.operand_ty_and_eval(it, locals))
1009 .collect::<Result<'db, Vec<_>>>()?;
1010 let stack_frame = match fn_ty.kind() {
1011 TyKind::FnPtr(..) => {
1012 let bytes = self.eval_operand(func, locals)?;
1013 self.exec_fn_pointer(
1014 bytes,
1015 destination_interval,
1016 &args,
1017 locals,
1018 *target,
1019 terminator.span,
1020 )?
1021 }
1022 TyKind::FnDef(def, generic_args) => self.exec_fn_def(
1023 def.0,
1024 generic_args,
1025 destination_interval,
1026 &args,
1027 locals,
1028 *target,
1029 terminator.span,
1030 )?,
1031 it => not_supported!("unknown function type {it:?}"),
1032 };
1033 locals.drop_flags.add_place(destination.as_ref());
1034 if let Some(stack_frame) = stack_frame {
1035 self.code_stack.push(my_stack_frame);
1036 current_block_idx = stack_frame.locals.body.start_block;
1037 self.code_stack.push(stack_frame);
1038 return Ok(None);
1039 } else {
1040 current_block_idx =
1041 target.ok_or(MirEvalError::UndefinedBehavior(
1042 "Diverging function returned".to_owned(),
1043 ))?;
1044 }
1045 }
1046 TerminatorKind::SwitchInt { discr, targets } => {
1047 let val = u128::from_le_bytes(pad16(
1048 self.eval_operand(discr, locals)?.get(self)?,
1049 IsSigned::No,
1050 ));
1051 current_block_idx = targets.target_for_value(val);
1052 }
1053 TerminatorKind::Return => {
1054 break;
1055 }
1056 TerminatorKind::Unreachable => {
1057 return Err(MirEvalError::UndefinedBehavior(
1058 "unreachable executed".to_owned(),
1059 ));
1060 }
1061 TerminatorKind::Drop { place, target, unwind: _ } => {
1062 self.drop_place(place, locals, terminator.span)?;
1063 current_block_idx = *target;
1064 }
1065 _ => not_supported!("unknown terminator"),
1066 }
1067 }
1068 Ok(Some(my_stack_frame))
1069 })();
1070 let my_stack_frame = match e {
1071 Ok(None) => continue 'stack,
1072 Ok(Some(x)) => x,
1073 Err(e) => {
1074 let my_code_stack = mem::replace(&mut self.code_stack, prev_code_stack);
1075 let mut error_stack = vec![];
1076 for frame in my_code_stack.into_iter().rev() {
1077 if let Some(f) = frame.locals.body.owner.as_function() {
1078 error_stack.push((Either::Left(f), frame.span.0, frame.span.1));
1079 }
1080 }
1081 return Err(MirEvalError::InFunction(Box::new(e), error_stack));
1082 }
1083 };
1084 let return_interval = my_stack_frame.locals.ptr[return_slot()];
1085 self.unused_locals_store
1086 .borrow_mut()
1087 .entry(my_stack_frame.locals.body.owner)
1088 .or_default()
1089 .push(my_stack_frame.locals);
1090 match my_stack_frame.destination {
1091 None => {
1092 self.code_stack = prev_code_stack;
1093 self.stack_depth_limit += 1;
1094 return Ok(return_interval);
1095 }
1096 Some(bb) => {
1097 let _ = my_stack_frame.prev_stack_ptr;
1099 current_block_idx = bb;
1101 }
1102 }
1103 }
1104 }
1105
1106 fn fill_locals_for_body(
1107 &mut self,
1108 body: &'db MirBody<'db>,
1109 locals: &mut Locals<'a, 'db>,
1110 args: impl Iterator<Item = IntervalOrOwned>,
1111 ) -> Result<'db, ()> {
1112 let mut remain_args = body.param_locals.len();
1113 for ((l, interval), value) in locals.ptr.iter().skip(1).zip(args) {
1114 locals.drop_flags.add_place(l.into());
1115 match value {
1116 IntervalOrOwned::Owned(value) => interval.write_from_bytes(self, &value)?,
1117 IntervalOrOwned::Borrowed(value) => interval.write_from_interval(self, value)?,
1118 }
1119 if remain_args == 0 {
1120 return Err(MirEvalError::InternalError("too many arguments".into()));
1121 }
1122 remain_args -= 1;
1123 }
1124 if remain_args > 0 {
1125 return Err(MirEvalError::InternalError("too few arguments".into()));
1126 }
1127 Ok(())
1128 }
1129
1130 fn create_locals_for_body(
1131 &mut self,
1132 body: &'db MirBody<'db>,
1133 destination: Option<Interval>,
1134 ) -> Result<'db, (Locals<'a, 'db>, usize)> {
1135 let mut locals =
1136 match self.unused_locals_store.borrow_mut().entry(body.owner).or_default().pop() {
1137 None => Locals { ptr: ArenaMap::new(), body, drop_flags: DropFlags::default() },
1138 Some(mut l) => {
1139 l.drop_flags.clear();
1140 l.body = body;
1141 l
1142 }
1143 };
1144 let stack_size = {
1145 let mut stack_ptr = self.stack.len();
1146 for (id, it) in body.locals.iter() {
1147 if id == return_slot()
1148 && let Some(destination) = destination
1149 {
1150 locals.ptr.insert(id, destination);
1151 continue;
1152 }
1153 let (size, align) = self.size_align_of_sized(
1154 it.ty.as_ref(),
1155 &locals,
1156 "no unsized local in extending stack",
1157 )?;
1158 while !stack_ptr.is_multiple_of(align) {
1159 stack_ptr += 1;
1160 }
1161 let my_ptr = stack_ptr;
1162 stack_ptr += size;
1163 locals.ptr.insert(id, Interval { addr: Stack(my_ptr), size });
1164 }
1165 stack_ptr - self.stack.len()
1166 };
1167 let prev_stack_pointer = self.stack.len();
1168 if stack_size > self.memory_limit {
1169 return Err(MirEvalError::Panic(format!(
1170 "Stack overflow. Tried to grow stack to {stack_size} bytes"
1171 )));
1172 }
1173 self.stack.extend(std::iter::repeat_n(0, stack_size));
1174 Ok((locals, prev_stack_pointer))
1175 }
1176
1177 fn eval_rvalue(
1178 &mut self,
1179 r: &Rvalue,
1180 locals: &mut Locals<'a, 'db>,
1181 ) -> Result<'db, IntervalOrOwned> {
1182 use IntervalOrOwned::*;
1183 Ok(match r {
1184 Rvalue::Use(it) => Borrowed(self.eval_operand(it, locals)?),
1185 Rvalue::Ref(_, p) => {
1186 let (addr, _, metadata) = self.place_addr_and_ty_and_metadata(p, locals)?;
1187 let mut r = addr.to_bytes().to_vec();
1188 if let Some(metadata) = metadata {
1189 r.extend(metadata.get(self)?);
1190 }
1191 Owned(r)
1192 }
1193 Rvalue::Len(p) => {
1194 let (_, _, metadata) = self.place_addr_and_ty_and_metadata(p, locals)?;
1195 match metadata {
1196 Some(m) => m,
1197 None => {
1198 return Err(MirEvalError::InternalError(
1199 "type without metadata is used for Rvalue::Len".into(),
1200 ));
1201 }
1202 }
1203 }
1204 Rvalue::UnaryOp(op, val) => {
1205 let mut c = self.eval_operand(val, locals)?.get(self)?;
1206 let mut ty = self.operand_ty(val, locals)?;
1207 while let TyKind::Ref(_, z, _) = ty.kind() {
1208 ty = z;
1209 let size = self.size_of_sized(ty, locals, "operand of unary op")?;
1210 c = self.read_memory(Address::from_bytes(c)?, size)?;
1211 }
1212 if let TyKind::Float(f) = ty.kind() {
1213 match f {
1214 rustc_type_ir::FloatTy::F16 => {
1215 let c = -from_bytes!(f16, u16, c);
1216 Owned(u16::try_from(c.to_bits()).unwrap().to_le_bytes().into())
1217 }
1218 rustc_type_ir::FloatTy::F32 => {
1219 let c = -from_bytes!(f32, c);
1220 Owned(c.to_le_bytes().into())
1221 }
1222 rustc_type_ir::FloatTy::F64 => {
1223 let c = -from_bytes!(f64, c);
1224 Owned(c.to_le_bytes().into())
1225 }
1226 rustc_type_ir::FloatTy::F128 => {
1227 let c = -from_bytes!(f128, u128, c);
1228 Owned(c.to_bits().to_le_bytes().into())
1229 }
1230 }
1231 } else {
1232 let mut c = c.to_vec();
1233 if matches!(ty.kind(), TyKind::Bool) {
1234 c[0] = 1 - c[0];
1235 } else {
1236 match op {
1237 UnOp::Not => c.iter_mut().for_each(|it| *it = !*it),
1238 UnOp::Neg => {
1239 c.iter_mut().for_each(|it| *it = !*it);
1240 for k in c.iter_mut() {
1241 let o;
1242 (*k, o) = k.overflowing_add(1);
1243 if !o {
1244 break;
1245 }
1246 }
1247 }
1248 }
1249 }
1250 Owned(c)
1251 }
1252 }
1253 Rvalue::CheckedBinaryOp(op, lhs, rhs) => 'binary_op: {
1254 let lc = self.eval_operand(lhs, locals)?;
1255 let rc = self.eval_operand(rhs, locals)?;
1256 let mut lc = lc.get(self)?;
1257 let mut rc = rc.get(self)?;
1258 let mut ty = self.operand_ty(lhs, locals)?;
1259 while let TyKind::Ref(_, z, _) = ty.kind() {
1260 ty = z;
1261 let size = if ty.is_str() {
1262 if *op != BinOp::Eq {
1263 return Err(MirEvalError::NotSupported(
1264 "Only eq is builtin for `str`".to_owned(),
1265 ));
1266 }
1267 let ls = from_bytes!(usize, &lc[self.ptr_size()..self.ptr_size() * 2]);
1268 let rs = from_bytes!(usize, &rc[self.ptr_size()..self.ptr_size() * 2]);
1269 if ls != rs {
1270 break 'binary_op Owned(vec![0]);
1271 }
1272 lc = &lc[..self.ptr_size()];
1273 rc = &rc[..self.ptr_size()];
1274 lc = self.read_memory(Address::from_bytes(lc)?, ls)?;
1275 rc = self.read_memory(Address::from_bytes(rc)?, ls)?;
1276 break 'binary_op Owned(vec![u8::from(lc == rc)]);
1277 } else {
1278 self.size_of_sized(ty, locals, "operand of binary op")?
1279 };
1280 lc = self.read_memory(Address::from_bytes(lc)?, size)?;
1281 rc = self.read_memory(Address::from_bytes(rc)?, size)?;
1282 }
1283 if let TyKind::Float(f) = ty.kind() {
1284 match f {
1285 rustc_type_ir::FloatTy::F16 => {
1286 let l = from_bytes!(f16, u16, lc);
1287 let r = from_bytes!(f16, u16, rc);
1288 match op {
1289 BinOp::Ge
1290 | BinOp::Gt
1291 | BinOp::Le
1292 | BinOp::Lt
1293 | BinOp::Eq
1294 | BinOp::Ne => {
1295 let r = op.run_compare(l, r) as u8;
1296 Owned(vec![r])
1297 }
1298 BinOp::Add | BinOp::Sub | BinOp::Mul | BinOp::Div => {
1299 let r = match op {
1300 BinOp::Add => l + r,
1301 BinOp::Sub => l - r,
1302 BinOp::Mul => l * r,
1303 BinOp::Div => l / r,
1304 _ => unreachable!(),
1305 };
1306 Owned(
1307 u16::try_from(r.value.to_bits())
1308 .unwrap()
1309 .to_le_bytes()
1310 .into(),
1311 )
1312 }
1313 it => not_supported!(
1314 "invalid binop {it:?} on floating point operators"
1315 ),
1316 }
1317 }
1318 rustc_type_ir::FloatTy::F32 => {
1319 let l = from_bytes!(f32, lc);
1320 let r = from_bytes!(f32, rc);
1321 match op {
1322 BinOp::Ge
1323 | BinOp::Gt
1324 | BinOp::Le
1325 | BinOp::Lt
1326 | BinOp::Eq
1327 | BinOp::Ne => {
1328 let r = op.run_compare(l, r) as u8;
1329 Owned(vec![r])
1330 }
1331 BinOp::Add | BinOp::Sub | BinOp::Mul | BinOp::Div => {
1332 let r = match op {
1333 BinOp::Add => l + r,
1334 BinOp::Sub => l - r,
1335 BinOp::Mul => l * r,
1336 BinOp::Div => l / r,
1337 _ => unreachable!(),
1338 };
1339 Owned(r.to_le_bytes().into())
1340 }
1341 it => not_supported!(
1342 "invalid binop {it:?} on floating point operators"
1343 ),
1344 }
1345 }
1346 rustc_type_ir::FloatTy::F64 => {
1347 let l = from_bytes!(f64, lc);
1348 let r = from_bytes!(f64, rc);
1349 match op {
1350 BinOp::Ge
1351 | BinOp::Gt
1352 | BinOp::Le
1353 | BinOp::Lt
1354 | BinOp::Eq
1355 | BinOp::Ne => {
1356 let r = op.run_compare(l, r) as u8;
1357 Owned(vec![r])
1358 }
1359 BinOp::Add | BinOp::Sub | BinOp::Mul | BinOp::Div => {
1360 let r = match op {
1361 BinOp::Add => l + r,
1362 BinOp::Sub => l - r,
1363 BinOp::Mul => l * r,
1364 BinOp::Div => l / r,
1365 _ => unreachable!(),
1366 };
1367 Owned(r.to_le_bytes().into())
1368 }
1369 it => not_supported!(
1370 "invalid binop {it:?} on floating point operators"
1371 ),
1372 }
1373 }
1374 rustc_type_ir::FloatTy::F128 => {
1375 let l = from_bytes!(f128, u128, lc);
1376 let r = from_bytes!(f128, u128, rc);
1377 match op {
1378 BinOp::Ge
1379 | BinOp::Gt
1380 | BinOp::Le
1381 | BinOp::Lt
1382 | BinOp::Eq
1383 | BinOp::Ne => {
1384 let r = op.run_compare(l, r) as u8;
1385 Owned(vec![r])
1386 }
1387 BinOp::Add | BinOp::Sub | BinOp::Mul | BinOp::Div => {
1388 let r = match op {
1389 BinOp::Add => l + r,
1390 BinOp::Sub => l - r,
1391 BinOp::Mul => l * r,
1392 BinOp::Div => l / r,
1393 _ => unreachable!(),
1394 };
1395 Owned(r.value.to_bits().to_le_bytes().into())
1396 }
1397 it => not_supported!(
1398 "invalid binop {it:?} on floating point operators"
1399 ),
1400 }
1401 }
1402 }
1403 } else {
1404 let is_signed = matches!(ty.kind(), TyKind::Int(_));
1405 let l128 = IntValue::from_bytes(lc, is_signed);
1406 let r128 = IntValue::from_bytes(rc, is_signed);
1407 match op {
1408 BinOp::Ge | BinOp::Gt | BinOp::Le | BinOp::Lt | BinOp::Eq | BinOp::Ne => {
1409 let r = op.run_compare(l128, r128) as u8;
1410 Owned(vec![r])
1411 }
1412 BinOp::BitAnd
1413 | BinOp::BitOr
1414 | BinOp::BitXor
1415 | BinOp::Add
1416 | BinOp::Mul
1417 | BinOp::Div
1418 | BinOp::Rem
1419 | BinOp::Sub => {
1420 let r = match op {
1421 BinOp::Add => l128.checked_add(r128).ok_or_else(|| {
1422 MirEvalError::Panic(format!("Overflow in {op:?}"))
1423 })?,
1424 BinOp::Mul => l128.checked_mul(r128).ok_or_else(|| {
1425 MirEvalError::Panic(format!("Overflow in {op:?}"))
1426 })?,
1427 BinOp::Div => l128.checked_div(r128).ok_or_else(|| {
1428 MirEvalError::Panic(format!("Overflow in {op:?}"))
1429 })?,
1430 BinOp::Rem => l128.checked_rem(r128).ok_or_else(|| {
1431 MirEvalError::Panic(format!("Overflow in {op:?}"))
1432 })?,
1433 BinOp::Sub => l128.checked_sub(r128).ok_or_else(|| {
1434 MirEvalError::Panic(format!("Overflow in {op:?}"))
1435 })?,
1436 BinOp::BitAnd => l128 & r128,
1437 BinOp::BitOr => l128 | r128,
1438 BinOp::BitXor => l128 ^ r128,
1439 _ => unreachable!(),
1440 };
1441 Owned(r.to_bytes())
1442 }
1443 BinOp::Shl | BinOp::Shr => {
1444 let r = 'b: {
1445 if let Some(shift_amount) = r128.as_u32() {
1446 let r = match op {
1447 BinOp::Shl => l128.checked_shl(shift_amount),
1448 BinOp::Shr => l128.checked_shr(shift_amount),
1449 _ => unreachable!(),
1450 };
1451 if shift_amount as usize >= lc.len() * 8 {
1452 return Err(MirEvalError::Panic(format!(
1453 "Overflow in {op:?}"
1454 )));
1455 }
1456 if let Some(r) = r {
1457 break 'b r;
1458 }
1459 };
1460 return Err(MirEvalError::Panic(format!("Overflow in {op:?}")));
1461 };
1462 Owned(r.to_bytes())
1463 }
1464 BinOp::Offset => not_supported!("offset binop"),
1465 }
1466 }
1467 }
1468 Rvalue::Discriminant(p) => {
1469 let ty = self.place_ty(p, locals)?;
1470 let bytes = self.eval_place(p, locals)?.get(self)?;
1471 let result = self.compute_discriminant(ty, bytes)?;
1472 Owned(result.to_le_bytes().to_vec())
1473 }
1474 Rvalue::Repeat(it, len) => {
1475 let len = match try_const_usize(self.db, len.as_ref()) {
1476 Some(it) => it as usize,
1477 None => not_supported!("non evaluatable array len in repeat Rvalue"),
1478 };
1479 let val = self.eval_operand(it, locals)?.get(self)?;
1480 let size = len * val.len();
1481 Owned(val.iter().copied().cycle().take(size).collect())
1482 }
1483 Rvalue::CopyForDeref(_) => not_supported!("copy for deref"),
1484 Rvalue::Aggregate(kind, values) => {
1485 let values = values
1486 .iter()
1487 .map(|it| self.eval_operand(it, locals))
1488 .collect::<Result<'db, Vec<_>>>()?;
1489 match kind {
1490 AggregateKind::Array(_) => {
1491 let mut r = vec![];
1492 for it in values {
1493 let value = it.get(self)?;
1494 r.extend(value);
1495 }
1496 Owned(r)
1497 }
1498 AggregateKind::Tuple(ty) => {
1499 let layout = self.layout(ty.as_ref())?;
1500 Owned(self.construct_with_layout(
1501 layout.size.bytes_usize(),
1502 &layout,
1503 None,
1504 values.iter().map(|&it| it.into()),
1505 )?)
1506 }
1507 AggregateKind::Union(it, f) => {
1508 let layout = self.layout_adt((*it).into(), GenericArgs::empty())?;
1509 let offset = layout
1510 .fields
1511 .offset(u32::from(f.local_id.into_raw()) as usize)
1512 .bytes_usize();
1513 let op = values[0].get(self)?;
1514 let mut result = vec![0; layout.size.bytes_usize()];
1515 result[offset..offset + op.len()].copy_from_slice(op);
1516 Owned(result)
1517 }
1518 AggregateKind::Adt(it, subst) => {
1519 let (size, variant_layout, tag) =
1520 self.layout_of_variant(*it, subst.as_ref(), locals)?;
1521 Owned(self.construct_with_layout(
1522 size,
1523 &variant_layout,
1524 tag,
1525 values.iter().map(|&it| it.into()),
1526 )?)
1527 }
1528 AggregateKind::Closure(ty) => {
1529 let layout = self.layout(ty.as_ref())?;
1530 Owned(self.construct_with_layout(
1531 layout.size.bytes_usize(),
1532 &layout,
1533 None,
1534 values.iter().map(|&it| it.into()),
1535 )?)
1536 }
1537 }
1538 }
1539 Rvalue::Cast(kind, operand, target_ty) => match kind {
1540 CastKind::PointerCoercion(cast) => match cast {
1541 PointerCast::ReifyFnPointer | PointerCast::ClosureFnPointer(_) => {
1542 let current_ty = self.operand_ty(operand, locals)?;
1543 if let TyKind::FnDef(_, _) | TyKind::Closure(_, _) = current_ty.kind() {
1544 let id = self.vtable_map.id(current_ty);
1545 let ptr_size = self.ptr_size();
1546 Owned(id.to_le_bytes()[0..ptr_size].to_vec())
1547 } else {
1548 not_supported!(
1549 "creating a fn pointer from a non FnDef or Closure type"
1550 );
1551 }
1552 }
1553 PointerCast::Unsize => {
1554 let current_ty = self.operand_ty(operand, locals)?;
1555 let addr = self.eval_operand(operand, locals)?;
1556 self.coerce_unsized(addr, current_ty, target_ty.as_ref())?
1557 }
1558 PointerCast::MutToConstPointer | PointerCast::UnsafeFnPointer => {
1559 Borrowed(self.eval_operand(operand, locals)?)
1561 }
1562 PointerCast::ArrayToPointer => {
1563 Borrowed(self.eval_operand(operand, locals)?.slice(0..self.ptr_size()))
1565 }
1566 },
1567 CastKind::DynStar => not_supported!("dyn star cast"),
1568 CastKind::IntToInt
1569 | CastKind::PtrToPtr
1570 | CastKind::PointerExposeAddress
1571 | CastKind::PointerFromExposedAddress => {
1572 let current_ty = self.operand_ty(operand, locals)?;
1573 let is_signed = matches!(current_ty.kind(), TyKind::Int(_)).into();
1574 let current = pad16(self.eval_operand(operand, locals)?.get(self)?, is_signed);
1575 let dest_size = self.size_of_sized(
1576 target_ty.as_ref(),
1577 locals,
1578 "destination of int to int cast",
1579 )?;
1580 Owned(current[0..dest_size].to_vec())
1581 }
1582 CastKind::FloatToInt => {
1583 let ty = self.operand_ty(operand, locals)?;
1584 let TyKind::Float(ty) = ty.kind() else {
1585 not_supported!("invalid float to int cast");
1586 };
1587 let value = self.eval_operand(operand, locals)?.get(self)?;
1588 let value = match ty {
1589 rustc_type_ir::FloatTy::F32 => {
1590 let value = value.try_into().unwrap();
1591 f32::from_le_bytes(value) as f64
1592 }
1593 rustc_type_ir::FloatTy::F64 => {
1594 let value = value.try_into().unwrap();
1595 f64::from_le_bytes(value)
1596 }
1597 rustc_type_ir::FloatTy::F16 | rustc_type_ir::FloatTy::F128 => {
1598 not_supported!("unstable floating point type f16 and f128");
1599 }
1600 };
1601 let is_signed = matches!(target_ty.as_ref().kind(), TyKind::Int(_));
1602 let dest_size = self.size_of_sized(
1603 target_ty.as_ref(),
1604 locals,
1605 "destination of float to int cast",
1606 )?;
1607 let dest_bits = dest_size * 8;
1608 let (max, min) = if dest_bits == 128 {
1609 (i128::MAX, i128::MIN)
1610 } else if is_signed {
1611 let max = 1i128 << (dest_bits - 1);
1612 (max - 1, -max)
1613 } else {
1614 ((1i128 << dest_bits) - 1, 0)
1615 };
1616 let value = (value as i128).min(max).max(min);
1617 let result = value.to_le_bytes();
1618 Owned(result[0..dest_size].to_vec())
1619 }
1620 CastKind::FloatToFloat => {
1621 let ty = self.operand_ty(operand, locals)?;
1622 let TyKind::Float(ty) = ty.kind() else {
1623 not_supported!("invalid float to int cast");
1624 };
1625 let value = self.eval_operand(operand, locals)?.get(self)?;
1626 let value = match ty {
1627 rustc_type_ir::FloatTy::F32 => {
1628 let value = value.try_into().unwrap();
1629 f32::from_le_bytes(value) as f64
1630 }
1631 rustc_type_ir::FloatTy::F64 => {
1632 let value = value.try_into().unwrap();
1633 f64::from_le_bytes(value)
1634 }
1635 rustc_type_ir::FloatTy::F16 | rustc_type_ir::FloatTy::F128 => {
1636 not_supported!("unstable floating point type f16 and f128");
1637 }
1638 };
1639 let TyKind::Float(target_ty) = target_ty.as_ref().kind() else {
1640 not_supported!("invalid float to float cast");
1641 };
1642 match target_ty {
1643 rustc_type_ir::FloatTy::F32 => Owned((value as f32).to_le_bytes().to_vec()),
1644 rustc_type_ir::FloatTy::F64 => Owned(value.to_le_bytes().to_vec()),
1645 rustc_type_ir::FloatTy::F16 | rustc_type_ir::FloatTy::F128 => {
1646 not_supported!("unstable floating point type f16 and f128");
1647 }
1648 }
1649 }
1650 CastKind::IntToFloat => {
1651 let current_ty = self.operand_ty(operand, locals)?;
1652 let is_signed = matches!(current_ty.kind(), TyKind::Int(_)).into();
1653 let value = pad16(self.eval_operand(operand, locals)?.get(self)?, is_signed);
1654 let value = i128::from_le_bytes(value);
1655 let TyKind::Float(target_ty) = target_ty.as_ref().kind() else {
1656 not_supported!("invalid int to float cast");
1657 };
1658 match target_ty {
1659 rustc_type_ir::FloatTy::F32 => Owned((value as f32).to_le_bytes().to_vec()),
1660 rustc_type_ir::FloatTy::F64 => Owned((value as f64).to_le_bytes().to_vec()),
1661 rustc_type_ir::FloatTy::F16 | rustc_type_ir::FloatTy::F128 => {
1662 not_supported!("unstable floating point type f16 and f128");
1663 }
1664 }
1665 }
1666 CastKind::FnPtrToPtr => not_supported!("fn ptr to ptr cast"),
1667 },
1668 Rvalue::ThreadLocalRef(n)
1669 | Rvalue::AddressOf(n)
1670 | Rvalue::BinaryOp(n)
1671 | Rvalue::NullaryOp(n) => match *n {},
1672 })
1673 }
1674
1675 fn compute_discriminant(&self, ty: Ty<'db>, bytes: &[u8]) -> Result<'db, i128> {
1676 let layout = self.layout(ty)?;
1677 let TyKind::Adt(adt_def, _) = ty.kind() else {
1678 return Ok(0);
1679 };
1680 let AdtId::EnumId(e) = adt_def.def_id() else {
1681 return Ok(0);
1682 };
1683 match &layout.variants {
1684 Variants::Empty => Err(MirEvalError::UndefinedBehavior(
1685 "reading the discriminant of an uninhabited enum".to_owned(),
1686 )),
1687 Variants::Single { index } => {
1688 let r =
1689 self.const_eval_discriminant(e.enum_variants(self.db).variants[index.0].0)?;
1690 Ok(r)
1691 }
1692 Variants::Multiple { tag, tag_encoding, variants, .. } => {
1693 let size = tag.size(self.target_data_layout).bytes_usize();
1694 let offset = layout.fields.offset(0).bytes_usize(); let is_signed = tag.is_signed().into();
1696 match tag_encoding {
1697 TagEncoding::Direct => {
1698 let tag = &bytes[offset..offset + size];
1699 Ok(i128::from_le_bytes(pad16(tag, is_signed)))
1700 }
1701 TagEncoding::Niche { untagged_variant, niche_start, .. } => {
1702 let tag = &bytes[offset..offset + size];
1703 let candidate_tag = i128::from_le_bytes(pad16(tag, is_signed))
1704 .wrapping_sub(*niche_start as i128)
1705 as usize;
1706 let idx = variants
1707 .iter_enumerated()
1708 .map(|(it, _)| it)
1709 .filter(|it| it != untagged_variant)
1710 .nth(candidate_tag)
1711 .unwrap_or(*untagged_variant)
1712 .0;
1713 let result =
1714 self.const_eval_discriminant(e.enum_variants(self.db).variants[idx].0)?;
1715 Ok(result)
1716 }
1717 }
1718 }
1719 }
1720 }
1721
1722 fn coerce_unsized_look_through_fields<T>(
1723 &self,
1724 ty: Ty<'db>,
1725 goal: impl Fn(TyKind<'db>) -> Option<T>,
1726 ) -> Result<'db, T> {
1727 let kind = ty.kind();
1728 if let Some(it) = goal(kind) {
1729 return Ok(it);
1730 }
1731 match kind {
1732 TyKind::Adt(adt_ef, subst) if let AdtId::StructId(struct_id) = adt_ef.def_id() => {
1733 let field_types = self.db.field_types(struct_id.into());
1734 if let Some(ty) = field_types
1735 .iter()
1736 .last()
1737 .map(|it| it.1.ty().instantiate(self.interner(), subst))
1738 {
1739 return self.coerce_unsized_look_through_fields(ty.skip_norm_wip(), goal);
1740 }
1741 }
1742 TyKind::Pat(ty, _) => return self.coerce_unsized_look_through_fields(ty, goal),
1743 _ => (),
1744 }
1745 Err(MirEvalError::CoerceUnsizedError(ty.store()))
1746 }
1747
1748 fn coerce_unsized(
1749 &mut self,
1750 addr: Interval,
1751 current_ty: Ty<'db>,
1752 target_ty: Ty<'db>,
1753 ) -> Result<'db, IntervalOrOwned> {
1754 fn for_ptr<'db>(it: TyKind<'db>) -> Option<Ty<'db>> {
1755 match it {
1756 TyKind::RawPtr(ty, _) | TyKind::Ref(_, ty, _) => Some(ty),
1757 _ => None,
1758 }
1759 }
1760 let target_ty = self.coerce_unsized_look_through_fields(target_ty, for_ptr)?;
1761 let current_ty = self.coerce_unsized_look_through_fields(current_ty, for_ptr)?;
1762
1763 self.unsizing_ptr_from_addr(target_ty, current_ty, addr)
1764 }
1765
1766 fn unsizing_ptr_from_addr(
1768 &mut self,
1769 target_ty: Ty<'db>,
1770 current_ty: Ty<'db>,
1771 addr: Interval,
1772 ) -> Result<'db, IntervalOrOwned> {
1773 use IntervalOrOwned::*;
1774 Ok(match &target_ty.kind() {
1775 TyKind::Slice(_) => match ¤t_ty.kind() {
1776 TyKind::Array(_, size) => {
1777 let len = match try_const_usize(self.db, *size) {
1778 None => {
1779 not_supported!("unevaluatble len of array in coerce unsized")
1780 }
1781 Some(it) => it as usize,
1782 };
1783 let mut r = Vec::with_capacity(16);
1784 let addr = addr.get(self)?;
1785 r.extend(addr.iter().copied());
1786 r.extend(len.to_le_bytes());
1787 Owned(r)
1788 }
1789 t => {
1790 not_supported!("slice unsizing from non array type {t:?}")
1791 }
1792 },
1793 TyKind::Dynamic(..) => {
1794 let vtable = self.vtable_map.id(current_ty);
1795 let mut r = Vec::with_capacity(16);
1796 let addr = addr.get(self)?;
1797 r.extend(addr.iter().copied());
1798 r.extend(vtable.to_le_bytes());
1799 Owned(r)
1800 }
1801 TyKind::Adt(adt_def, target_subst) => match ¤t_ty.kind() {
1802 TyKind::Adt(current_adt_def, current_subst) => {
1803 let id = adt_def.def_id();
1804 let current_id = current_adt_def.def_id();
1805 if id != current_id {
1806 not_supported!("unsizing struct with different type");
1807 }
1808 let id = match id {
1809 AdtId::StructId(s) => s,
1810 AdtId::UnionId(_) => not_supported!("unsizing unions"),
1811 AdtId::EnumId(_) => not_supported!("unsizing enums"),
1812 };
1813 let Some((last_field, _)) = id.fields(self.db).fields().iter().next_back()
1814 else {
1815 not_supported!("unsizing struct without field");
1816 };
1817 let target_last_field = self.db.field_types(id.into())[last_field]
1818 .ty()
1819 .instantiate(self.interner(), target_subst)
1820 .skip_norm_wip();
1821 let current_last_field = self.db.field_types(id.into())[last_field]
1822 .ty()
1823 .instantiate(self.interner(), current_subst)
1824 .skip_norm_wip();
1825 return self.unsizing_ptr_from_addr(
1826 target_last_field,
1827 current_last_field,
1828 addr,
1829 );
1830 }
1831 _ => not_supported!("unsizing struct with non adt type"),
1832 },
1833 _ => not_supported!("unknown unsized cast"),
1834 })
1835 }
1836
1837 fn layout_of_variant(
1838 &mut self,
1839 it: VariantId,
1840 subst: GenericArgs<'db>,
1841 locals: &Locals<'a, 'db>,
1842 ) -> Result<'db, (usize, Arc<Layout>, Option<(usize, usize, i128)>)> {
1843 let adt = it.adt_id(self.db);
1844 if let Some(f) = locals.body.owner.as_variant()
1845 && let VariantId::EnumVariantId(it) = it
1846 && let AdtId::EnumId(e) = adt
1847 && f.lookup(self.db).parent == e
1848 {
1849 let i = self.const_eval_discriminant(it)?;
1852 return Ok((16, self.layout(Ty::new_empty_tuple(self.interner()))?, Some((0, 16, i))));
1853 }
1854 let layout = self.layout_adt(adt, subst)?;
1855 Ok(match &layout.variants {
1856 Variants::Single { .. } | Variants::Empty => (layout.size.bytes_usize(), layout, None),
1857 Variants::Multiple { variants, tag, tag_encoding, .. } => {
1858 let enum_variant_id = match it {
1859 VariantId::EnumVariantId(it) => it,
1860 _ => not_supported!("multi variant layout for non-enums"),
1861 };
1862 let mut discriminant = self.const_eval_discriminant(enum_variant_id)?;
1863 let rustc_enum_variant_idx = RustcEnumVariantIdx(enum_variant_id.index(self.db));
1864 let variant_layout = variants[rustc_enum_variant_idx].clone();
1865 let have_tag = match tag_encoding {
1866 TagEncoding::Direct => true,
1867 TagEncoding::Niche { untagged_variant, niche_variants: _, niche_start } => {
1868 if *untagged_variant == rustc_enum_variant_idx {
1869 false
1870 } else {
1871 discriminant = (variants
1872 .iter_enumerated()
1873 .filter(|(it, _)| it != untagged_variant)
1874 .position(|(it, _)| it == rustc_enum_variant_idx)
1875 .unwrap() as i128)
1876 .wrapping_add(*niche_start as i128);
1877 true
1878 }
1879 }
1880 };
1881 (
1882 layout.size.bytes_usize(),
1883 Arc::new(variant_layout),
1884 if have_tag {
1885 Some((
1886 layout.fields.offset(0).bytes_usize(),
1887 tag.size(self.target_data_layout).bytes_usize(),
1888 discriminant,
1889 ))
1890 } else {
1891 None
1892 },
1893 )
1894 }
1895 })
1896 }
1897
1898 fn construct_with_layout(
1899 &mut self,
1900 size: usize, variant_layout: &Layout,
1902 tag: Option<(usize, usize, i128)>,
1903 values: impl Iterator<Item = IntervalOrOwned>,
1904 ) -> Result<'db, Vec<u8>> {
1905 let mut result = vec![0; size];
1906 if let Some((offset, size, value)) = tag {
1907 match result.get_mut(offset..offset + size) {
1908 Some(it) => it.copy_from_slice(&value.to_le_bytes()[0..size]),
1909 None => {
1910 return Err(MirEvalError::InternalError(
1911 format!(
1912 "encoded tag ({offset}, {size}, {value}) is out of bounds 0..{size}"
1913 )
1914 .into(),
1915 ));
1916 }
1917 }
1918 }
1919 for (i, op) in values.enumerate() {
1920 let offset = variant_layout.fields.offset(i).bytes_usize();
1921 let op = op.get(self)?;
1922 match result.get_mut(offset..offset + op.len()) {
1923 Some(it) => it.copy_from_slice(op),
1924 None => {
1925 return Err(MirEvalError::InternalError(
1926 format!("field offset ({offset}) is out of bounds 0..{size}").into(),
1927 ));
1928 }
1929 }
1930 }
1931 Ok(result)
1932 }
1933
1934 fn eval_operand(
1935 &mut self,
1936 it: &Operand,
1937 locals: &mut Locals<'a, 'db>,
1938 ) -> Result<'db, Interval> {
1939 Ok(match &it.kind {
1940 OperandKind::Copy(p) | OperandKind::Move(p) => {
1941 locals.drop_flags.remove_place(p.as_ref());
1942 self.eval_place(p, locals)?
1943 }
1944 OperandKind::Static(st) => {
1945 let addr = self.eval_static(*st, locals)?;
1946 Interval::new(addr, self.ptr_size())
1947 }
1948 OperandKind::Constant { konst, .. } => {
1949 self.allocate_const_in_heap(locals, konst.as_ref())?
1950 }
1951 OperandKind::Allocation { allocation } => {
1952 self.allocate_allocation_in_heap(locals, allocation.as_ref())?
1953 }
1954 })
1955 }
1956
1957 fn allocate_valtree_in_heap(
1958 &mut self,
1959 ty: Ty<'db>,
1960 valtree: ValTree<'db>,
1961 ) -> Result<'db, Interval> {
1962 match ty.kind() {
1963 TyKind::Bool => {
1964 let value = valtree.inner().to_leaf().try_to_bool().unwrap();
1965 let addr = self.heap_allocate(1, 1)?;
1966 self.write_memory(addr, &[u8::from(value)])?;
1967 Ok(Interval::new(addr, 1))
1968 }
1969 TyKind::Char => {
1970 let value = valtree.inner().to_leaf().to_u32();
1971 let addr = self.heap_allocate(4, 4)?;
1972 self.write_memory(addr, &value.to_le_bytes())?;
1973 Ok(Interval::new(addr, 4))
1974 }
1975 TyKind::Int(int_ty) => {
1976 let size = int_ty
1977 .bit_width()
1978 .map(Size::from_bits)
1979 .unwrap_or_else(|| Size::from_bytes(self.ptr_size() as u64));
1980 let bytes = size.bytes_usize();
1981
1982 let value = valtree.inner().to_leaf().to_int(size);
1983 let addr = self.heap_allocate(bytes, bytes)?;
1984 self.write_memory(addr, &value.to_le_bytes()[..bytes])?;
1985 Ok(Interval::new(addr, bytes))
1986 }
1987 TyKind::Uint(uint_ty) => {
1988 let size = uint_ty
1989 .bit_width()
1990 .map(Size::from_bits)
1991 .unwrap_or_else(|| Size::from_bytes(self.ptr_size() as u64));
1992 let bytes = size.bytes_usize();
1993
1994 let value = valtree.inner().to_leaf().to_uint(size);
1995 let addr = self.heap_allocate(bytes, bytes)?;
1996 self.write_memory(addr, &value.to_le_bytes()[..bytes])?;
1997 Ok(Interval::new(addr, bytes))
1998 }
1999 TyKind::Float(float_ty) => {
2000 let size = Size::from_bits(float_ty.bit_width());
2001 let bytes = size.bytes_usize();
2002
2003 let value = valtree.inner().to_leaf().to_uint(size);
2004 let addr = self.heap_allocate(bytes, bytes)?;
2005 self.write_memory(addr, &value.to_le_bytes()[..bytes])?;
2006 Ok(Interval::new(addr, bytes))
2007 }
2008 TyKind::RawPtr(..) => {
2009 let size = self.ptr_size();
2010 let value = valtree.inner().to_leaf().to_uint(Size::from_bytes(size));
2011 let addr = self.heap_allocate(size, size)?;
2012 self.write_memory(addr, &value.to_le_bytes()[..size])?;
2013 Ok(Interval::new(addr, size))
2014 }
2015 TyKind::Ref(_, inner_ty, _) => match inner_ty.kind() {
2016 TyKind::Str => {
2017 let bytes = valtree
2018 .inner()
2019 .to_branch()
2020 .iter()
2021 .map(|konst| match konst.kind() {
2022 ConstKind::Value(value) => Ok(value.value.inner().to_leaf().to_u8()),
2023 _ => not_supported!("unsupported const"),
2024 })
2025 .collect::<Result<'_, Vec<_>>>()?;
2026 let bytes_addr = self.heap_allocate(bytes.len(), 1)?;
2027 self.write_memory(bytes_addr, &bytes)?;
2028 let ref_addr = self.heap_allocate(self.ptr_size() * 2, self.ptr_size())?;
2029 self.write_memory(ref_addr, &bytes_addr.to_bytes())?;
2030 let mut len = [0; 16];
2031 len[..size_of::<usize>()].copy_from_slice(&bytes.len().to_le_bytes());
2032 self.write_memory(ref_addr.offset(self.ptr_size()), &len[..self.ptr_size()])?;
2033 Ok(Interval::new(ref_addr, self.ptr_size() * 2))
2034 }
2035 TyKind::Slice(inner_ty) => {
2036 let item_layout = self.layout(inner_ty)?;
2037 let items = valtree
2038 .inner()
2039 .to_branch()
2040 .iter()
2041 .map(|konst| match konst.kind() {
2042 ConstKind::Value(value) => {
2043 self.allocate_valtree_in_heap(value.ty, value.value)
2044 }
2045 _ => not_supported!("unsupported const"),
2046 })
2047 .collect::<Result<'_, Vec<_>>>()?;
2048 let item_size = item_layout.size.bytes_usize();
2049 let items_addr = self.heap_allocate(
2050 items.len() * item_size,
2051 item_layout.align.bytes() as usize,
2052 )?;
2053 for (i, item) in items.iter().enumerate() {
2054 self.copy_from_interval(items_addr.offset(i * item_size), *item)?;
2055 }
2056 let ref_addr = self.heap_allocate(self.ptr_size() * 2, self.ptr_size())?;
2057 self.write_memory(ref_addr, &items_addr.to_bytes())?;
2058 let mut len = [0; 16];
2059 len[..size_of::<usize>()].copy_from_slice(&items.len().to_le_bytes());
2060 self.write_memory(ref_addr.offset(self.ptr_size()), &len[..self.ptr_size()])?;
2061 Ok(Interval::new(ref_addr, self.ptr_size() * 2))
2062 }
2063 TyKind::Dynamic(..) => not_supported!("`dyn Trait` consts not supported yet"),
2064 _ => {
2065 let inner_addr = self.allocate_valtree_in_heap(inner_ty, valtree)?;
2066 let ref_addr = self.heap_allocate(self.ptr_size(), self.ptr_size())?;
2067 self.write_memory(ref_addr, &inner_addr.addr.to_bytes())?;
2068 Ok(Interval::new(ref_addr, self.ptr_size()))
2069 }
2070 },
2071 TyKind::Adt(_, _) | TyKind::Array(_, _) | TyKind::Tuple(_) => {
2072 not_supported!(
2073 "ADTs, arrays and tuples are unsupported in consts currently (requires `adt_const_params`)"
2074 )
2075 }
2076 TyKind::Pat(_, _)
2077 | TyKind::Slice(_)
2078 | TyKind::FnDef(_, _)
2079 | TyKind::Foreign(_)
2080 | TyKind::Dynamic(_, _)
2081 | TyKind::UnsafeBinder(..)
2082 | TyKind::FnPtr(..)
2083 | TyKind::Closure(_, _)
2084 | TyKind::CoroutineClosure(_, _)
2085 | TyKind::Coroutine(_, _)
2086 | TyKind::CoroutineWitness(_, _)
2087 | TyKind::Never
2088 | TyKind::Alias(..)
2089 | TyKind::Param(_)
2090 | TyKind::Bound(..)
2091 | TyKind::Placeholder(_)
2092 | TyKind::Infer(_)
2093 | TyKind::Str
2094 | TyKind::Error(_) => not_supported!("unsupported const"),
2095 }
2096 }
2097
2098 fn allocate_const_in_heap(
2099 &mut self,
2100 locals: &Locals<'a, 'db>,
2101 konst: Const<'db>,
2102 ) -> Result<'db, Interval> {
2103 match konst.kind() {
2104 ConstKind::Value(value) => self.allocate_valtree_in_heap(value.ty, value.value),
2105 ConstKind::Unevaluated(UnevaluatedConst { def: const_id, args: subst }) => {
2106 let mut id = const_id.0;
2107 let mut subst = subst;
2108 if let GeneralConstId::ConstId(c) = id {
2109 let (c, s) = lookup_impl_const(&self.infcx, self.param_env.param_env, c, subst);
2110 id = GeneralConstId::ConstId(c);
2111 subst = s;
2112 }
2113 let allocation = match id {
2114 GeneralConstId::ConstId(const_id) => {
2115 self.db.const_eval(const_id, subst, Some(self.param_env)).map_err(|e| {
2116 let name = id.name(self.db);
2117 MirEvalError::ConstEvalError(name, Box::new(e))
2118 })?
2119 }
2120 GeneralConstId::StaticId(static_id) => {
2121 self.db.const_eval_static(static_id).map_err(|e| {
2122 let name = id.name(self.db);
2123 MirEvalError::ConstEvalError(name, Box::new(e))
2124 })?
2125 }
2126 GeneralConstId::AnonConstId(anon_const_id) => self
2127 .db
2128 .anon_const_eval(anon_const_id, subst, Some(self.param_env))
2129 .map_err(|e| {
2130 let name = id.name(self.db);
2131 MirEvalError::ConstEvalError(name, Box::new(e))
2132 })?,
2133 };
2134 self.allocate_allocation_in_heap(locals, allocation)
2135 }
2136 _ => not_supported!("evaluating unknown const"),
2137 }
2138 }
2139
2140 fn allocate_allocation_in_heap(
2141 &mut self,
2142 locals: &Locals<'a, 'db>,
2143 allocation: Allocation<'db>,
2144 ) -> Result<'db, Interval> {
2145 let AllocationData { ty, memory: ref v, ref memory_map } = *allocation;
2146 let patch_map = memory_map.transform_addresses(|b, align| {
2147 let addr = self.heap_allocate(b.len(), align)?;
2148 self.write_memory(addr, b)?;
2149 Ok(addr.to_usize())
2150 })?;
2151 let (size, align) = self.size_align_of(allocation.ty, locals)?.unwrap_or((v.len(), 1));
2152 let v: Cow<'_, [u8]> = if size != v.len() {
2153 if size == 16 && v.len() < 16 {
2155 Cow::Owned(pad16(v, IsSigned::No).to_vec())
2156 } else if size < 16 && v.len() == 16 {
2157 Cow::Borrowed(&v[0..size])
2158 } else {
2159 return Err(MirEvalError::InvalidConst);
2160 }
2161 } else {
2162 Cow::Borrowed(v)
2163 };
2164 let addr = self.heap_allocate(size, align)?;
2165 self.write_memory(addr, &v)?;
2166 self.patch_addresses(
2167 &patch_map,
2168 |bytes| match memory_map {
2169 MemoryMap::Empty | MemoryMap::Simple(_) => {
2170 Err(MirEvalError::InvalidVTableId(from_bytes!(usize, bytes)))
2171 }
2172 MemoryMap::Complex(cm) => cm.vtable.ty_of_bytes(bytes),
2173 },
2174 addr,
2175 ty,
2176 locals,
2177 )?;
2178 Ok(Interval::new(addr, size))
2179 }
2180
2181 fn eval_place(&mut self, p: &StoredPlace, locals: &Locals<'a, 'db>) -> Result<'db, Interval> {
2182 let addr = self.place_addr(p, locals)?;
2183 Ok(Interval::new(
2184 addr,
2185 self.size_of_sized(self.place_ty(p, locals)?, locals, "type of this place")?,
2186 ))
2187 }
2188
2189 fn read_memory(&self, addr: Address, size: usize) -> Result<'db, &[u8]> {
2190 if size == 0 {
2191 return Ok(&[]);
2192 }
2193 let (mem, pos) = match addr {
2194 Stack(it) => (&self.stack, it),
2195 Heap(it) => (&self.heap, it),
2196 Invalid(it) => {
2197 return Err(MirEvalError::UndefinedBehavior(format!(
2198 "read invalid memory address {it} with size {size}"
2199 )));
2200 }
2201 };
2202 mem.get(pos..pos + size)
2203 .ok_or_else(|| MirEvalError::UndefinedBehavior("out of bound memory read".to_owned()))
2204 }
2205
2206 fn write_memory_using_ref(&mut self, addr: Address, size: usize) -> Result<'db, &mut [u8]> {
2207 let (mem, pos) = match addr {
2208 Stack(it) => (&mut self.stack, it),
2209 Heap(it) => (&mut self.heap, it),
2210 Invalid(it) => {
2211 return Err(MirEvalError::UndefinedBehavior(format!(
2212 "write invalid memory address {it} with size {size}"
2213 )));
2214 }
2215 };
2216 mem.get_mut(pos..pos + size)
2217 .ok_or_else(|| MirEvalError::UndefinedBehavior("out of bound memory write".to_owned()))
2218 }
2219
2220 fn write_memory(&mut self, addr: Address, r: &[u8]) -> Result<'db, ()> {
2221 if r.is_empty() {
2222 return Ok(());
2223 }
2224 self.write_memory_using_ref(addr, r.len())?.copy_from_slice(r);
2225 Ok(())
2226 }
2227
2228 fn copy_from_interval_or_owned(
2229 &mut self,
2230 addr: Address,
2231 r: IntervalOrOwned,
2232 ) -> Result<'db, ()> {
2233 match r {
2234 IntervalOrOwned::Borrowed(r) => self.copy_from_interval(addr, r),
2235 IntervalOrOwned::Owned(r) => self.write_memory(addr, &r),
2236 }
2237 }
2238
2239 fn copy_from_interval(&mut self, addr: Address, r: Interval) -> Result<'db, ()> {
2240 if r.size == 0 {
2241 return Ok(());
2242 }
2243
2244 let oob = || MirEvalError::UndefinedBehavior("out of bounds memory write".to_owned());
2245
2246 match (addr, r.addr) {
2247 (Stack(dst), Stack(src)) => {
2248 if self.stack.len() < src + r.size || self.stack.len() < dst + r.size {
2249 return Err(oob());
2250 }
2251 self.stack.copy_within(src..src + r.size, dst)
2252 }
2253 (Heap(dst), Heap(src)) => {
2254 if self.stack.len() < src + r.size || self.stack.len() < dst + r.size {
2255 return Err(oob());
2256 }
2257 self.heap.copy_within(src..src + r.size, dst)
2258 }
2259 (Stack(dst), Heap(src)) => {
2260 self.stack
2261 .get_mut(dst..dst + r.size)
2262 .ok_or_else(oob)?
2263 .copy_from_slice(self.heap.get(src..src + r.size).ok_or_else(oob)?);
2264 }
2265 (Heap(dst), Stack(src)) => {
2266 self.heap
2267 .get_mut(dst..dst + r.size)
2268 .ok_or_else(oob)?
2269 .copy_from_slice(self.stack.get(src..src + r.size).ok_or_else(oob)?);
2270 }
2271 _ => {
2272 return Err(MirEvalError::UndefinedBehavior(format!(
2273 "invalid memory write at address {addr:?}"
2274 )));
2275 }
2276 }
2277
2278 Ok(())
2279 }
2280
2281 fn size_align_of(
2282 &self,
2283 ty: Ty<'db>,
2284 locals: &Locals<'a, 'db>,
2285 ) -> Result<'db, Option<(usize, usize)>> {
2286 if let Some(layout) = self.layout_cache.borrow().get(&ty) {
2287 return Ok(layout
2288 .is_sized()
2289 .then(|| (layout.size.bytes_usize(), layout.align.bytes() as usize)));
2290 }
2291 if let Some(f) = locals.body.owner.as_variant()
2292 && let Some((AdtId::EnumId(e), _)) = ty.as_adt()
2293 && f.lookup(self.db).parent == e
2294 {
2295 return Ok(Some((16, 16)));
2298 }
2299 let layout = self.layout(ty);
2300 if self.assert_placeholder_ty_is_unused
2301 && matches!(layout, Err(MirEvalError::LayoutError(LayoutError::HasPlaceholder, _)))
2302 {
2303 return Ok(Some((0, 1)));
2304 }
2305 let layout = layout?;
2306 Ok(layout.is_sized().then(|| (layout.size.bytes_usize(), layout.align.bytes() as usize)))
2307 }
2308
2309 fn size_of_sized(
2312 &self,
2313 ty: Ty<'db>,
2314 locals: &Locals<'a, 'db>,
2315 what: &'static str,
2316 ) -> Result<'db, usize> {
2317 match self.size_align_of(ty, locals)? {
2318 Some(it) => Ok(it.0),
2319 None => Err(MirEvalError::TypeIsUnsized(ty.store(), what)),
2320 }
2321 }
2322
2323 fn size_align_of_sized(
2326 &self,
2327 ty: Ty<'db>,
2328 locals: &Locals<'a, 'db>,
2329 what: &'static str,
2330 ) -> Result<'db, (usize, usize)> {
2331 match self.size_align_of(ty, locals)? {
2332 Some(it) => Ok(it),
2333 None => Err(MirEvalError::TypeIsUnsized(ty.store(), what)),
2334 }
2335 }
2336
2337 fn heap_allocate(&mut self, size: usize, align: usize) -> Result<'db, Address> {
2338 if !align.is_power_of_two() || align > 10000 {
2339 return Err(MirEvalError::UndefinedBehavior(format!("Alignment {align} is invalid")));
2340 }
2341 while !self.heap.len().is_multiple_of(align) {
2342 self.heap.push(0);
2343 }
2344 if size.checked_add(self.heap.len()).is_none_or(|x| x > self.memory_limit) {
2345 return Err(MirEvalError::Panic(format!("Memory allocation of {size} bytes failed")));
2346 }
2347 let pos = self.heap.len();
2348 self.heap.extend(std::iter::repeat_n(0, size));
2349 Ok(Address::Heap(pos))
2350 }
2351
2352 fn detect_fn_trait(&self, def: FunctionId) -> Option<FnTrait> {
2353 let def = Some(def);
2354 if def == self.cached_fn_trait_func {
2355 Some(FnTrait::Fn)
2356 } else if def == self.cached_fn_mut_trait_func {
2357 Some(FnTrait::FnMut)
2358 } else if def == self.cached_fn_once_trait_func {
2359 Some(FnTrait::FnOnce)
2360 } else {
2361 None
2362 }
2363 }
2364
2365 fn create_memory_map(
2366 &self,
2367 bytes: &[u8],
2368 ty: Ty<'db>,
2369 locals: &Locals<'a, 'db>,
2370 ) -> Result<'db, ComplexMemoryMap<'db>> {
2371 fn rec<'a, 'db>(
2372 this: &Evaluator<'a, 'db>,
2373 bytes: &[u8],
2374 ty: Ty<'db>,
2375 locals: &Locals<'a, 'db>,
2376 mm: &mut ComplexMemoryMap<'db>,
2377 stack_depth_limit: usize,
2378 ) -> Result<'db, ()> {
2379 if stack_depth_limit.checked_sub(1).is_none() {
2380 return Err(MirEvalError::StackOverflow);
2381 }
2382 match ty.kind() {
2383 TyKind::Ref(_, t, _) => {
2384 let size = this.size_align_of(t, locals)?;
2385 match size {
2386 Some((size, _)) => {
2387 let addr_usize = from_bytes!(usize, bytes);
2388 let bytes =
2389 this.read_memory(Address::from_usize(addr_usize), size)?.to_vec();
2390 mm.insert(addr_usize, bytes.clone().into());
2391 rec(this, &bytes, t, locals, mm, stack_depth_limit - 1)?;
2392 }
2393 None => {
2394 let mut check_inner = None;
2395 let (addr, meta) = bytes.split_at(bytes.len() / 2);
2396 let element_size = match t.kind() {
2397 TyKind::Str => 1,
2398 TyKind::Slice(t) => {
2399 check_inner = Some(t);
2400 this.size_of_sized(t, locals, "slice inner type")?
2401 }
2402 TyKind::Dynamic(..) => {
2403 let t = this.vtable_map.ty_of_bytes(meta)?;
2404 check_inner = Some(t);
2405 this.size_of_sized(t, locals, "dyn concrete type")?
2406 }
2407 _ => return Ok(()),
2408 };
2409 let count = match t.kind() {
2410 TyKind::Dynamic(..) => 1,
2411 _ => from_bytes!(usize, meta),
2412 };
2413 let size = element_size * count;
2414 let addr = Address::from_bytes(addr)?;
2415 let b = this.read_memory(addr, size)?;
2416 mm.insert(addr.to_usize(), b.into());
2417 if let Some(ty) = check_inner {
2418 for i in 0..count {
2419 let offset = element_size * i;
2420 rec(
2421 this,
2422 &b[offset..offset + element_size],
2423 ty,
2424 locals,
2425 mm,
2426 stack_depth_limit - 1,
2427 )?;
2428 }
2429 }
2430 }
2431 }
2432 }
2433 TyKind::Array(inner, len) => {
2434 let len = match try_const_usize(this.db, len) {
2435 Some(it) => it as usize,
2436 None => not_supported!("non evaluatable array len in patching addresses"),
2437 };
2438 let size = this.size_of_sized(inner, locals, "inner of array")?;
2439 for i in 0..len {
2440 let offset = i * size;
2441 rec(
2442 this,
2443 &bytes[offset..offset + size],
2444 inner,
2445 locals,
2446 mm,
2447 stack_depth_limit - 1,
2448 )?;
2449 }
2450 }
2451 TyKind::Tuple(subst) => {
2452 let layout = this.layout(ty)?;
2453 for (id, ty) in subst.iter().enumerate() {
2454 let offset = layout.fields.offset(id).bytes_usize();
2455 let size = this.layout(ty)?.size.bytes_usize();
2456 rec(
2457 this,
2458 &bytes[offset..offset + size],
2459 ty,
2460 locals,
2461 mm,
2462 stack_depth_limit - 1,
2463 )?;
2464 }
2465 }
2466 TyKind::Adt(adt, subst) => match adt.def_id() {
2467 AdtId::StructId(s) => {
2468 let data = s.fields(this.db);
2469 let layout = this.layout(ty)?;
2470 let field_types = this.db.field_types(s.into());
2471 for (f, _) in data.fields().iter() {
2472 let offset = layout
2473 .fields
2474 .offset(u32::from(f.into_raw()) as usize)
2475 .bytes_usize();
2476 let ty = field_types[f]
2477 .ty()
2478 .instantiate(this.interner(), subst)
2479 .skip_norm_wip();
2480 let size = this.layout(ty)?.size.bytes_usize();
2481 rec(
2482 this,
2483 &bytes[offset..offset + size],
2484 ty,
2485 locals,
2486 mm,
2487 stack_depth_limit - 1,
2488 )?;
2489 }
2490 }
2491 AdtId::EnumId(e) => {
2492 let layout = this.layout(ty)?;
2493 if let Some((v, l)) = detect_variant_from_bytes(
2494 &layout,
2495 this.db,
2496 this.target_data_layout,
2497 bytes,
2498 e,
2499 ) {
2500 let data = v.fields(this.db);
2501 let field_types = this.db.field_types(v.into());
2502 for (f, _) in data.fields().iter() {
2503 let offset =
2504 l.fields.offset(u32::from(f.into_raw()) as usize).bytes_usize();
2505 let ty = field_types[f]
2506 .ty()
2507 .instantiate(this.interner(), subst)
2508 .skip_norm_wip();
2509 let size = this.layout(ty)?.size.bytes_usize();
2510 rec(
2511 this,
2512 &bytes[offset..offset + size],
2513 ty,
2514 locals,
2515 mm,
2516 stack_depth_limit - 1,
2517 )?;
2518 }
2519 }
2520 }
2521 AdtId::UnionId(_) => (),
2522 },
2523 TyKind::Alias(AliasTy { kind: AliasTyKind::Projection { .. }, .. }) => {
2524 let mut ocx = ObligationCtxt::new(&this.infcx);
2525 let ty = ocx
2526 .structurally_normalize_ty(
2527 &ObligationCause::dummy(),
2528 this.param_env.param_env,
2529 ty,
2530 )
2531 .map_err(|_| MirEvalError::NotSupported("couldn't normalize".to_owned()))?;
2532
2533 rec(this, bytes, ty, locals, mm, stack_depth_limit - 1)?;
2534 }
2535 _ => (),
2536 }
2537 Ok(())
2538 }
2539 let mut mm = ComplexMemoryMap::default();
2540 rec(self, bytes, ty, locals, &mut mm, self.stack_depth_limit - 1)?;
2541 Ok(mm)
2542 }
2543
2544 fn patch_addresses(
2545 &mut self,
2546 patch_map: &FxHashMap<usize, usize>,
2547 ty_of_bytes: impl Fn(&[u8]) -> Result<'db, Ty<'db>> + Copy,
2548 addr: Address,
2549 ty: Ty<'db>,
2550 locals: &Locals<'a, 'db>,
2551 ) -> Result<'db, ()> {
2552 let layout = self.layout(ty)?;
2553 let my_size = self.size_of_sized(ty, locals, "value to patch address")?;
2554 use rustc_type_ir::TyKind;
2555 match ty.kind() {
2556 TyKind::Ref(_, t, _) => {
2557 let size = self.size_align_of(t, locals)?;
2558 match size {
2559 Some(_) => {
2560 let current = from_bytes!(usize, self.read_memory(addr, my_size)?);
2561 let patched = match patch_map.get(¤t) {
2562 Some(it) => {
2563 self.write_memory(addr, &it.to_le_bytes())?;
2564 *it
2565 }
2566 None => current,
2567 };
2568 self.patch_addresses(
2569 patch_map,
2570 ty_of_bytes,
2571 Address::from_usize(patched),
2572 t,
2573 locals,
2574 )?;
2575 }
2576 None => {
2577 let bytes = self.read_memory(addr, my_size)?;
2578 let (current, metadata) = bytes.split_at(my_size / 2);
2579 let metadata = metadata.to_vec();
2580 let current = from_bytes!(usize, current);
2581 let patched = match patch_map.get(¤t) {
2582 Some(it) => {
2583 self.write_memory(addr, &it.to_le_bytes())?;
2584 *it
2585 }
2586 None => current,
2587 };
2588 let patched = Address::from_usize(patched);
2589 if let TyKind::Slice(inner) = t.kind() {
2590 let len = from_bytes!(usize, metadata);
2591 let size = self.size_of_sized(inner, locals, "slice item to patch")?;
2592 for i in 0..len {
2593 self.patch_addresses(
2594 patch_map,
2595 ty_of_bytes,
2596 patched.offset(i * size),
2597 inner,
2598 locals,
2599 )?;
2600 }
2601 }
2602 }
2603 }
2604 }
2605 TyKind::FnPtr(_, _) => {
2606 let ty = ty_of_bytes(self.read_memory(addr, my_size)?)?;
2607 let new_id = self.vtable_map.id(ty);
2608 self.write_memory(addr, &new_id.to_le_bytes())?;
2609 }
2610 TyKind::Adt(id, args) => match id.def_id() {
2611 AdtId::StructId(s) => {
2612 for (i, (_, field)) in self.db.field_types(s.into()).iter().enumerate() {
2613 let offset = layout.fields.offset(i).bytes_usize();
2614 let ty = field.ty().instantiate(self.interner(), args).skip_norm_wip();
2615 self.patch_addresses(
2616 patch_map,
2617 ty_of_bytes,
2618 addr.offset(offset),
2619 ty,
2620 locals,
2621 )?;
2622 }
2623 }
2624 AdtId::UnionId(_) => (),
2625 AdtId::EnumId(e) => {
2626 if let Some((ev, layout)) = detect_variant_from_bytes(
2627 &layout,
2628 self.db,
2629 self.target_data_layout,
2630 self.read_memory(addr, layout.size.bytes_usize())?,
2631 e,
2632 ) {
2633 for (i, (_, field)) in self.db.field_types(ev.into()).iter().enumerate() {
2634 let offset = layout.fields.offset(i).bytes_usize();
2635 let ty = field.ty().instantiate(self.interner(), args).skip_norm_wip();
2636 self.patch_addresses(
2637 patch_map,
2638 ty_of_bytes,
2639 addr.offset(offset),
2640 ty,
2641 locals,
2642 )?;
2643 }
2644 }
2645 }
2646 },
2647 TyKind::Tuple(tys) => {
2648 for (id, ty) in tys.iter().enumerate() {
2649 let offset = layout.fields.offset(id).bytes_usize();
2650 self.patch_addresses(patch_map, ty_of_bytes, addr.offset(offset), ty, locals)?;
2651 }
2652 }
2653 TyKind::Array(inner, len) => {
2654 let len = match consteval::try_const_usize(self.db, len) {
2655 Some(it) => it as usize,
2656 None => not_supported!("non evaluatable array len in patching addresses"),
2657 };
2658 let size = self.size_of_sized(inner, locals, "inner of array")?;
2659 for i in 0..len {
2660 self.patch_addresses(
2661 patch_map,
2662 ty_of_bytes,
2663 addr.offset(i * size),
2664 inner,
2665 locals,
2666 )?;
2667 }
2668 }
2669 TyKind::Bool
2670 | TyKind::Char
2671 | TyKind::Int(_)
2672 | TyKind::Uint(_)
2673 | TyKind::Float(_)
2674 | TyKind::Slice(_)
2675 | TyKind::RawPtr(_, _)
2676 | TyKind::FnDef(_, _)
2677 | TyKind::Str
2678 | TyKind::Never
2679 | TyKind::Closure(_, _)
2680 | TyKind::Coroutine(_, _)
2681 | TyKind::CoroutineWitness(_, _)
2682 | TyKind::Foreign(_)
2683 | TyKind::Error(_)
2684 | TyKind::Placeholder(_)
2685 | TyKind::Dynamic(_, _)
2686 | TyKind::Alias(..)
2687 | TyKind::Bound(_, _)
2688 | TyKind::Infer(_)
2689 | TyKind::Pat(_, _)
2690 | TyKind::Param(_)
2691 | TyKind::UnsafeBinder(_)
2692 | TyKind::CoroutineClosure(_, _) => (),
2693 }
2694 Ok(())
2695 }
2696
2697 fn exec_fn_pointer(
2698 &mut self,
2699 bytes: Interval,
2700 destination: Interval,
2701 args: &[IntervalAndTy<'db>],
2702 locals: &Locals<'a, 'db>,
2703 target_bb: Option<BasicBlockId>,
2704 span: MirSpan,
2705 ) -> Result<'db, Option<StackFrame<'a, 'db>>> {
2706 let id = from_bytes!(usize, bytes.get(self)?);
2707 let next_ty = self.vtable_map.ty(id)?;
2708 use rustc_type_ir::TyKind;
2709 match next_ty.kind() {
2710 TyKind::FnDef(def, generic_args) => {
2711 self.exec_fn_def(def.0, generic_args, destination, args, locals, target_bb, span)
2712 }
2713 TyKind::Closure(id, generic_args) => self.exec_closure(
2714 id.0,
2715 bytes.slice(0..0),
2716 generic_args,
2717 destination,
2718 args,
2719 locals,
2720 span,
2721 ),
2722 _ => Err(MirEvalError::InternalError("function pointer to non function".into())),
2723 }
2724 }
2725
2726 fn exec_closure(
2727 &mut self,
2728 closure: InternedClosureId<'db>,
2729 closure_data: Interval,
2730 generic_args: GenericArgs<'db>,
2731 destination: Interval,
2732 args: &[IntervalAndTy<'db>],
2733 locals: &Locals<'a, 'db>,
2734 span: MirSpan,
2735 ) -> Result<'db, Option<StackFrame<'a, 'db>>> {
2736 let mir_body = self
2737 .db
2738 .monomorphized_mir_body_for_closure(
2739 closure,
2740 generic_args.store(),
2741 self.param_env.store(),
2742 )
2743 .map_err(|it| MirEvalError::MirLowerErrorForClosure(closure, it))?;
2744 let closure_data =
2745 if mir_body.locals[mir_body.param_locals[0]].ty.as_ref().as_reference().is_some() {
2746 closure_data.addr.to_bytes().to_vec()
2747 } else {
2748 closure_data.get(self)?.to_owned()
2749 };
2750 let arg_bytes = iter::once(Ok(closure_data))
2751 .chain(args.iter().map(|it| Ok(it.get(self)?.to_owned())))
2752 .collect::<Result<'db, Vec<_>>>()?;
2753 let interval = self
2754 .interpret_mir(mir_body, arg_bytes.into_iter().map(IntervalOrOwned::Owned))
2755 .map_err(|e| {
2756 MirEvalError::InFunction(
2757 Box::new(e),
2758 vec![(Either::Right(closure), span, locals.body.owner)],
2759 )
2760 })?;
2761 destination.write_from_interval(self, interval)?;
2762 Ok(None)
2763 }
2764
2765 fn exec_fn_def(
2766 &mut self,
2767 def: CallableDefId,
2768 generic_args: GenericArgs<'db>,
2769 destination: Interval,
2770 args: &[IntervalAndTy<'db>],
2771 locals: &Locals<'a, 'db>,
2772 target_bb: Option<BasicBlockId>,
2773 span: MirSpan,
2774 ) -> Result<'db, Option<StackFrame<'a, 'db>>> {
2775 match def {
2776 CallableDefId::FunctionId(def) => {
2777 if self.detect_fn_trait(def).is_some() {
2778 return self.exec_fn_trait(
2779 def,
2780 args,
2781 generic_args,
2782 locals,
2783 destination,
2784 target_bb,
2785 span,
2786 );
2787 }
2788 self.exec_fn_with_args(
2789 def,
2790 args,
2791 generic_args,
2792 locals,
2793 destination,
2794 target_bb,
2795 span,
2796 )
2797 }
2798 CallableDefId::StructId(id) => {
2799 let (size, variant_layout, tag) =
2800 self.layout_of_variant(id.into(), generic_args, locals)?;
2801 let result = self.construct_with_layout(
2802 size,
2803 &variant_layout,
2804 tag,
2805 args.iter().map(|it| it.interval.into()),
2806 )?;
2807 destination.write_from_bytes(self, &result)?;
2808 Ok(None)
2809 }
2810 CallableDefId::EnumVariantId(id) => {
2811 let (size, variant_layout, tag) =
2812 self.layout_of_variant(id.into(), generic_args, locals)?;
2813 let result = self.construct_with_layout(
2814 size,
2815 &variant_layout,
2816 tag,
2817 args.iter().map(|it| it.interval.into()),
2818 )?;
2819 destination.write_from_bytes(self, &result)?;
2820 Ok(None)
2821 }
2822 }
2823 }
2824
2825 fn get_mir_or_dyn_index(
2826 &self,
2827 def: FunctionId,
2828 generic_args: GenericArgs<'db>,
2829 locals: &Locals<'a, 'db>,
2830 span: MirSpan,
2831 ) -> Result<'db, MirOrDynIndex<'db>> {
2832 let pair = (def, generic_args);
2833 if let Some(r) = self.mir_or_dyn_index_cache.borrow().get(&pair) {
2834 return Ok(r.clone());
2835 }
2836 let (def, generic_args) = pair;
2837 let r = if let Some(self_ty_idx) =
2838 is_dyn_method(self.interner(), self.param_env.param_env, def, generic_args)
2839 {
2840 MirOrDynIndex::Dyn(self_ty_idx)
2841 } else {
2842 let (imp, generic_args) = self.db.lookup_impl_method(
2843 ParamEnvAndCrate { param_env: self.param_env.param_env, krate: self.crate_id },
2844 def,
2845 generic_args,
2846 );
2847 let Either::Left(imp) = imp else {
2848 not_supported!("evaluating builtin derive impls is not supported")
2849 };
2850
2851 let mir_body = self
2852 .db
2853 .monomorphized_mir_body(imp.into(), generic_args.store(), self.param_env.store())
2854 .map_err(|e| {
2855 MirEvalError::InFunction(
2856 Box::new(MirEvalError::MirLowerError(imp, e)),
2857 vec![(Either::Left(imp), span, locals.body.owner)],
2858 )
2859 })?;
2860 MirOrDynIndex::Mir(mir_body)
2861 };
2862 self.mir_or_dyn_index_cache.borrow_mut().insert((def, generic_args), r.clone());
2863 Ok(r)
2864 }
2865
2866 fn exec_fn_with_args(
2867 &mut self,
2868 mut def: FunctionId,
2869 args: &[IntervalAndTy<'db>],
2870 generic_args: GenericArgs<'db>,
2871 locals: &Locals<'a, 'db>,
2872 destination: Interval,
2873 target_bb: Option<BasicBlockId>,
2874 span: MirSpan,
2875 ) -> Result<'db, Option<StackFrame<'a, 'db>>> {
2876 if self.detect_and_exec_special_function(
2877 def,
2878 args,
2879 generic_args,
2880 locals,
2881 destination,
2882 span,
2883 )? {
2884 return Ok(None);
2885 }
2886 if let Some(redirect_def) = self.detect_and_redirect_special_function(def)? {
2887 def = redirect_def;
2888 }
2889 let arg_bytes = args.iter().map(|it| IntervalOrOwned::Borrowed(it.interval));
2890 match self.get_mir_or_dyn_index(def, generic_args, locals, span)? {
2891 MirOrDynIndex::Dyn(self_ty_idx) => {
2892 let first_arg = arg_bytes.clone().next().unwrap();
2897 let first_arg = first_arg.get(self)?;
2898 let ty = self
2899 .vtable_map
2900 .ty_of_bytes(&first_arg[self.ptr_size()..self.ptr_size() * 2])?;
2901 let mut args_for_target = args.to_vec();
2902 args_for_target[0] = IntervalAndTy {
2903 interval: args_for_target[0].interval.slice(0..self.ptr_size()),
2904 ty,
2905 };
2906 let generics_for_target = GenericArgs::new_from_iter(
2907 self.interner(),
2908 generic_args
2909 .iter()
2910 .enumerate()
2911 .map(|(i, it)| if i == self_ty_idx { ty.into() } else { it }),
2912 );
2913 self.exec_fn_with_args(
2914 def,
2915 &args_for_target,
2916 generics_for_target,
2917 locals,
2918 destination,
2919 target_bb,
2920 span,
2921 )
2922 }
2923 MirOrDynIndex::Mir(body) => self.exec_looked_up_function(
2924 body,
2925 locals,
2926 def,
2927 arg_bytes,
2928 span,
2929 destination,
2930 target_bb,
2931 ),
2932 }
2933 }
2934
2935 fn exec_looked_up_function(
2936 &mut self,
2937 mir_body: &'db MirBody<'db>,
2938 locals: &Locals<'a, 'db>,
2939 def: FunctionId,
2940 arg_bytes: impl Iterator<Item = IntervalOrOwned>,
2941 span: MirSpan,
2942 destination: Interval,
2943 target_bb: Option<BasicBlockId>,
2944 ) -> Result<'db, Option<StackFrame<'a, 'db>>> {
2945 if let Some(target_bb) = target_bb {
2946 let (mut locals, prev_stack_ptr) =
2947 self.create_locals_for_body(mir_body, Some(destination))?;
2948 self.fill_locals_for_body(mir_body, &mut locals, arg_bytes.into_iter())?;
2949 let span = (span, locals.body.owner);
2950 Ok(Some(StackFrame { locals, destination: Some(target_bb), prev_stack_ptr, span }))
2951 } else {
2952 let result = self.interpret_mir(mir_body, arg_bytes).map_err(|e| {
2953 MirEvalError::InFunction(
2954 Box::new(e),
2955 vec![(Either::Left(def), span, locals.body.owner)],
2956 )
2957 })?;
2958 destination.write_from_interval(self, result)?;
2959 Ok(None)
2960 }
2961 }
2962
2963 fn exec_fn_trait(
2964 &mut self,
2965 def: FunctionId,
2966 args: &[IntervalAndTy<'db>],
2967 generic_args: GenericArgs<'db>,
2968 locals: &Locals<'a, 'db>,
2969 destination: Interval,
2970 target_bb: Option<BasicBlockId>,
2971 span: MirSpan,
2972 ) -> Result<'db, Option<StackFrame<'a, 'db>>> {
2973 let func = args
2974 .first()
2975 .ok_or_else(|| MirEvalError::InternalError("fn trait with no arg".into()))?;
2976 let mut func_ty = func.ty;
2977 let mut func_data = func.interval;
2978 while let TyKind::Ref(_, z, _) = func_ty.kind() {
2979 func_ty = z;
2980 if matches!(func_ty.kind(), TyKind::Dynamic(..)) {
2981 let id =
2982 from_bytes!(usize, &func_data.get(self)?[self.ptr_size()..self.ptr_size() * 2]);
2983 func_data = func_data.slice(0..self.ptr_size());
2984 func_ty = self.vtable_map.ty(id)?;
2985 }
2986 let size = self.size_of_sized(func_ty, locals, "self type of fn trait")?;
2987 func_data = Interval { addr: Address::from_bytes(func_data.get(self)?)?, size };
2988 }
2989 match func_ty.kind() {
2990 TyKind::FnDef(def, subst) => {
2991 self.exec_fn_def(def.0, subst, destination, &args[1..], locals, target_bb, span)
2992 }
2993 TyKind::FnPtr(..) => {
2994 self.exec_fn_pointer(func_data, destination, &args[1..], locals, target_bb, span)
2995 }
2996 TyKind::Closure(closure, subst) => self.exec_closure(
2997 closure.0,
2998 func_data,
2999 GenericArgs::new_from_slice(subst.as_closure().parent_args()),
3000 destination,
3001 &args[1..],
3002 locals,
3003 span,
3004 ),
3005 _ => {
3006 let arg0 = func;
3008 let args = &args[1..];
3009 let arg1 = {
3010 let ty = Ty::new_tup_from_iter(self.interner(), args.iter().map(|it| it.ty));
3011 let layout = self.layout(ty)?;
3012 let result = self.construct_with_layout(
3013 layout.size.bytes_usize(),
3014 &layout,
3015 None,
3016 args.iter().map(|it| IntervalOrOwned::Borrowed(it.interval)),
3017 )?;
3018 let size = layout.size.bytes_usize();
3020 let addr = self.heap_allocate(size, layout.align.bytes() as usize)?;
3021 self.write_memory(addr, &result)?;
3022 IntervalAndTy { interval: Interval { addr, size }, ty }
3023 };
3024 self.exec_fn_with_args(
3025 def,
3026 &[arg0.clone(), arg1],
3027 generic_args,
3028 locals,
3029 destination,
3030 target_bb,
3031 span,
3032 )
3033 }
3034 }
3035 }
3036
3037 fn eval_static(&mut self, st: StaticId, locals: &Locals<'a, 'db>) -> Result<'db, Address> {
3038 if let Some(o) = self.static_locations.get(&st) {
3039 return Ok(*o);
3040 };
3041 let static_data = StaticSignature::of(self.db, st);
3042 let result = if !static_data.flags.contains(StaticFlags::EXTERN) {
3043 let allocation = self.db.const_eval_static(st).map_err(|e| {
3044 MirEvalError::ConstEvalError(static_data.name.as_str().to_owned(), Box::new(e))
3045 })?;
3046 self.allocate_allocation_in_heap(locals, allocation)?
3047 } else {
3048 let ty = InferenceResult::of(self.db, DefWithBodyId::from(st))
3049 .expr_ty(Body::of(self.db, st.into()).root_expr());
3050 let Some((size, align)) = self.size_align_of(ty, locals)? else {
3051 not_supported!("unsized extern static");
3052 };
3053 let addr = self.heap_allocate(size, align)?;
3054 Interval::new(addr, size)
3055 };
3056 let addr = self.heap_allocate(self.ptr_size(), self.ptr_size())?;
3057 self.write_memory(addr, &result.addr.to_bytes())?;
3058 self.static_locations.insert(st, addr);
3059 Ok(addr)
3060 }
3061
3062 fn const_eval_discriminant(&self, variant: EnumVariantId) -> Result<'db, i128> {
3063 let r = self.db.const_eval_discriminant(variant);
3064 match r {
3065 Ok(r) => Ok(r),
3066 Err(e) => {
3067 let db = self.db;
3068 let loc = variant.lookup(db);
3069 let edition = self.crate_id.data(self.db).edition;
3070 let name = format!(
3071 "{}::{}",
3072 EnumSignature::of(self.db, loc.parent).name.display(db, edition),
3073 loc.parent
3074 .enum_variants(self.db)
3075 .variant_name_by_id(variant)
3076 .unwrap()
3077 .display(db, edition),
3078 );
3079 Err(MirEvalError::ConstEvalError(name, Box::new(e)))
3080 }
3081 }
3082 }
3083
3084 fn drop_place(
3085 &mut self,
3086 place: &StoredPlace,
3087 locals: &mut Locals<'a, 'db>,
3088 span: MirSpan,
3089 ) -> Result<'db, ()> {
3090 let (addr, ty, metadata) = self.place_addr_and_ty_and_metadata(place, locals)?;
3091 if !locals.drop_flags.remove_place(place.as_ref()) {
3092 return Ok(());
3093 }
3094 let metadata = match metadata {
3095 Some(it) => it.get(self)?.to_vec(),
3096 None => vec![],
3097 };
3098 self.run_drop_glue_deep(ty, locals, addr, &metadata, span)
3099 }
3100
3101 fn run_drop_glue_deep(
3102 &mut self,
3103 ty: Ty<'db>,
3104 locals: &Locals<'a, 'db>,
3105 addr: Address,
3106 metadata: &[u8],
3107 span: MirSpan,
3108 ) -> Result<'db, ()> {
3109 let Some(drop_fn) = self.lang_items().Drop_drop else {
3110 return Ok(());
3113 };
3114
3115 let generic_args = GenericArgs::new_from_slice(&[ty.into()]);
3116 let (drop_impl, drop_args) = self.db.lookup_impl_method(
3117 ParamEnvAndCrate { param_env: self.param_env.param_env, krate: self.crate_id },
3118 drop_fn,
3119 generic_args,
3120 );
3121 if let Either::Left(drop_impl) = drop_impl
3122 && matches!(drop_impl.lookup(self.db).container, ItemContainerId::ImplId(_))
3123 && let Ok(body) = self.db.monomorphized_mir_body(
3124 drop_impl.into(),
3125 drop_args.store(),
3126 self.param_env.store(),
3127 )
3128 {
3129 self.exec_looked_up_function(
3130 body,
3131 locals,
3132 drop_impl,
3133 iter::once(IntervalOrOwned::Owned(addr.to_bytes().to_vec())),
3134 span,
3135 Interval { addr: Address::Invalid(0), size: 0 },
3136 None,
3137 )?;
3138 }
3139 match ty.kind() {
3140 TyKind::Adt(adt_def, subst) => {
3141 let id = adt_def.def_id();
3142 match id {
3143 AdtId::StructId(s) => {
3144 let data = StructSignature::of(self.db, s);
3145 if data.flags.contains(StructFlags::IS_MANUALLY_DROP) {
3146 return Ok(());
3147 }
3148 let layout = self.layout_adt(id, subst)?;
3149 let variant_fields = s.fields(self.db);
3150 match variant_fields.shape {
3151 FieldsShape::Record | FieldsShape::Tuple => {
3152 let field_types = self.db.field_types(s.into());
3153 for (field, _) in variant_fields.fields().iter() {
3154 let offset = layout
3155 .fields
3156 .offset(u32::from(field.into_raw()) as usize)
3157 .bytes_usize();
3158 let addr = addr.offset(offset);
3159 let ty = field_types[field]
3160 .ty()
3161 .instantiate(self.interner(), subst)
3162 .skip_norm_wip();
3163 self.run_drop_glue_deep(ty, locals, addr, &[], span)?;
3164 }
3165 }
3166 FieldsShape::Unit => (),
3167 }
3168 }
3169 AdtId::UnionId(_) => (), AdtId::EnumId(_) => (),
3171 }
3172 }
3173 TyKind::Dynamic(..) => {
3174 if !metadata.is_empty() {
3175 let concrete_ty = self.vtable_map.ty_of_bytes(metadata)?;
3176 self.run_drop_glue_deep(concrete_ty, locals, addr, &[], span)?;
3177 }
3178 }
3179 TyKind::Bool
3180 | TyKind::Char
3181 | TyKind::Int(_)
3182 | TyKind::Uint(_)
3183 | TyKind::Float(_)
3184 | TyKind::Tuple(_)
3185 | TyKind::Array(_, _)
3186 | TyKind::Slice(_)
3187 | TyKind::RawPtr(_, _)
3188 | TyKind::Ref(_, _, _)
3189 | TyKind::Alias(..)
3190 | TyKind::FnDef(_, _)
3191 | TyKind::Str
3192 | TyKind::Never
3193 | TyKind::Closure(_, _)
3194 | TyKind::Coroutine(_, _)
3195 | TyKind::CoroutineClosure(..)
3196 | TyKind::CoroutineWitness(_, _)
3197 | TyKind::Foreign(_)
3198 | TyKind::Error(_)
3199 | TyKind::Param(_)
3200 | TyKind::Placeholder(_)
3201 | TyKind::FnPtr(..)
3202 | TyKind::Bound(..)
3203 | TyKind::Infer(..)
3204 | TyKind::Pat(..)
3205 | TyKind::UnsafeBinder(..) => (),
3206 };
3207 Ok(())
3208 }
3209
3210 fn write_to_stdout(&mut self, interval: Interval) -> Result<'db, ()> {
3211 self.stdout.extend(interval.get(self)?.to_vec());
3212 Ok(())
3213 }
3214
3215 fn write_to_stderr(&mut self, interval: Interval) -> Result<'db, ()> {
3216 self.stderr.extend(interval.get(self)?.to_vec());
3217 Ok(())
3218 }
3219}
3220
3221pub fn render_const_using_debug_impl<'db>(
3222 db: &'db dyn HirDatabase,
3223 owner: InferBodyId<'db>,
3224 c: Allocation<'db>,
3225 ty: Ty<'db>,
3226) -> Result<'db, String> {
3227 let mut evaluator = Evaluator::new(db, owner, false, None)?;
3228 let locals = &Locals {
3229 ptr: ArenaMap::new(),
3230 body: db
3231 .mir_body(owner)
3232 .map_err(|_| MirEvalError::NotSupported("unreachable".to_owned()))?,
3233 drop_flags: DropFlags::default(),
3234 };
3235 let data = evaluator.allocate_allocation_in_heap(locals, c)?;
3236 let lang_items = evaluator.interner().lang_items();
3237 let resolver = owner.resolver(db);
3238 let Some(debug_fmt_fn) = lang_items.Debug_fmt else {
3239 not_supported!("core::fmt::Debug::fmt not found");
3240 };
3241 let ptr_size = evaluator.ptr_size();
3242 let argument = evaluator.heap_allocate(ptr_size * 2, ptr_size)?;
3249 evaluator.write_memory(argument, &data.addr.to_bytes())?;
3250 let debug_fmt_fn_ptr = evaluator.vtable_map.id(Ty::new_fn_def(
3251 evaluator.interner(),
3252 CallableDefId::FunctionId(debug_fmt_fn).into(),
3253 GenericArgs::new_from_slice(&[ty.into()]),
3254 ));
3255 evaluator.write_memory(argument.offset(ptr_size), &debug_fmt_fn_ptr.to_le_bytes())?;
3256 let template = evaluator.heap_allocate(2, 1)?;
3264 evaluator.write_memory(template, &[0xC0, 0x00])?;
3265 let arguments = evaluator.heap_allocate(ptr_size * 2, ptr_size)?;
3266 evaluator.write_memory(arguments, &template.to_bytes())?;
3267 evaluator.write_memory(arguments.offset(ptr_size), &argument.to_bytes())?;
3268 let Some(ValueNs::FunctionId(format_fn)) = resolver.resolve_path_in_value_ns_fully(
3269 db,
3270 &hir_def::expr_store::path::Path::from_known_path_with_no_generic(path![std::fmt::format]),
3271 HygieneId::ROOT,
3272 ) else {
3273 not_supported!("std::fmt::format not found");
3274 };
3275 let interval = evaluator.interpret_mir(
3276 db.mir_body(format_fn.into()).map_err(|e| MirEvalError::MirLowerError(format_fn, e))?,
3277 [IntervalOrOwned::Borrowed(Interval { addr: arguments, size: ptr_size * 2 })].into_iter(),
3278 )?;
3279 let message_string = interval.get(&evaluator)?;
3280 let words = [
3281 from_bytes!(usize, message_string[0..ptr_size]),
3282 from_bytes!(usize, message_string[ptr_size..2 * ptr_size]),
3283 from_bytes!(usize, message_string[2 * ptr_size..3 * ptr_size]),
3284 ];
3285 let Some(addr) = words.into_iter().map(Address::from_usize).find(|it| matches!(it, Heap(_)))
3286 else {
3287 return Ok(String::new());
3289 };
3290 let size = words
3291 .into_iter()
3292 .filter(|&it| !matches!(Address::from_usize(it), Heap(_)))
3293 .min()
3294 .unwrap_or(0);
3295 Ok(std::string::String::from_utf8_lossy(evaluator.read_memory(addr, size)?).into_owned())
3296}
3297
3298#[derive(PartialEq, Eq)]
3299pub enum IsSigned {
3300 Yes,
3301 No,
3302}
3303
3304impl From<bool> for IsSigned {
3305 fn from(value: bool) -> Self {
3306 if value { Self::Yes } else { Self::No }
3307 }
3308}
3309
3310pub fn pad16(it: &[u8], is_signed: IsSigned) -> [u8; 16] {
3311 let is_negative = is_signed == IsSigned::Yes && it.last().unwrap_or(&0) > &127;
3312 let mut res = [if is_negative { 255 } else { 0 }; 16];
3313 res[..it.len()].copy_from_slice(it);
3314 res
3315}
3316
3317macro_rules! for_each_int_type {
3318 ($call_macro:path, $args:tt) => {
3319 $call_macro! {
3320 $args
3321 I8
3322 U8
3323 I16
3324 U16
3325 I32
3326 U32
3327 I64
3328 U64
3329 I128
3330 U128
3331 }
3332 };
3333}
3334
3335#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
3336enum IntValue {
3337 I8(i8),
3338 U8(u8),
3339 I16(i16),
3340 U16(u16),
3341 I32(i32),
3342 U32(u32),
3343 I64(i64),
3344 U64(u64),
3345 I128(i128),
3346 U128(u128),
3347}
3348
3349macro_rules! checked_int_op {
3350 ( [ $op:ident ] $( $int_ty:ident )+ ) => {
3351 fn $op(self, other: Self) -> Option<Self> {
3352 match (self, other) {
3353 $( (Self::$int_ty(a), Self::$int_ty(b)) => a.$op(b).map(Self::$int_ty), )+
3354 _ => panic!("incompatible integer types"),
3355 }
3356 }
3357 };
3358}
3359
3360macro_rules! int_bit_shifts {
3361 ( [ $op:ident ] $( $int_ty:ident )+ ) => {
3362 fn $op(self, amount: u32) -> Option<Self> {
3363 match self {
3364 $( Self::$int_ty(this) => this.$op(amount).map(Self::$int_ty), )+
3365 }
3366 }
3367 };
3368}
3369
3370macro_rules! unchecked_int_op {
3371 ( [ $name:ident, $op:tt ] $( $int_ty:ident )+ ) => {
3372 fn $name(self, other: Self) -> Self {
3373 match (self, other) {
3374 $( (Self::$int_ty(a), Self::$int_ty(b)) => Self::$int_ty(a $op b), )+
3375 _ => panic!("incompatible integer types"),
3376 }
3377 }
3378 };
3379}
3380
3381impl IntValue {
3382 fn from_bytes(bytes: &[u8], is_signed: bool) -> Self {
3383 match (bytes.len(), is_signed) {
3384 (1, false) => Self::U8(u8::from_le_bytes(bytes.try_into().unwrap())),
3385 (1, true) => Self::I8(i8::from_le_bytes(bytes.try_into().unwrap())),
3386 (2, false) => Self::U16(u16::from_le_bytes(bytes.try_into().unwrap())),
3387 (2, true) => Self::I16(i16::from_le_bytes(bytes.try_into().unwrap())),
3388 (4, false) => Self::U32(u32::from_le_bytes(bytes.try_into().unwrap())),
3389 (4, true) => Self::I32(i32::from_le_bytes(bytes.try_into().unwrap())),
3390 (8, false) => Self::U64(u64::from_le_bytes(bytes.try_into().unwrap())),
3391 (8, true) => Self::I64(i64::from_le_bytes(bytes.try_into().unwrap())),
3392 (16, false) => Self::U128(u128::from_le_bytes(bytes.try_into().unwrap())),
3393 (16, true) => Self::I128(i128::from_le_bytes(bytes.try_into().unwrap())),
3394 (len, is_signed) => {
3395 never!("invalid integer size: {len}, signed: {is_signed}");
3396 Self::I32(0)
3397 }
3398 }
3399 }
3400
3401 fn to_bytes(self) -> Vec<u8> {
3402 macro_rules! m {
3403 ( [] $( $int_ty:ident )+ ) => {
3404 match self {
3405 $( Self::$int_ty(v) => v.to_le_bytes().to_vec() ),+
3406 }
3407 };
3408 }
3409 for_each_int_type! { m, [] }
3410 }
3411
3412 fn as_u32(self) -> Option<u32> {
3413 macro_rules! m {
3414 ( [] $( $int_ty:ident )+ ) => {
3415 match self {
3416 $( Self::$int_ty(v) => v.try_into().ok() ),+
3417 }
3418 };
3419 }
3420 for_each_int_type! { m, [] }
3421 }
3422
3423 for_each_int_type!(checked_int_op, [checked_add]);
3424 for_each_int_type!(checked_int_op, [checked_sub]);
3425 for_each_int_type!(checked_int_op, [checked_div]);
3426 for_each_int_type!(checked_int_op, [checked_rem]);
3427 for_each_int_type!(checked_int_op, [checked_mul]);
3428
3429 for_each_int_type!(int_bit_shifts, [checked_shl]);
3430 for_each_int_type!(int_bit_shifts, [checked_shr]);
3431}
3432
3433impl std::ops::BitAnd for IntValue {
3434 type Output = Self;
3435 for_each_int_type!(unchecked_int_op, [bitand, &]);
3436}
3437impl std::ops::BitOr for IntValue {
3438 type Output = Self;
3439 for_each_int_type!(unchecked_int_op, [bitor, |]);
3440}
3441impl std::ops::BitXor for IntValue {
3442 type Output = Self;
3443 for_each_int_type!(unchecked_int_op, [bitxor, ^]);
3444}