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