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