Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/mir/eval.rs')
| -rw-r--r-- | crates/hir-ty/src/mir/eval.rs | 963 |
1 files changed, 609 insertions, 354 deletions
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index bb33da97e3..01d27f2672 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -1,39 +1,49 @@ //! This module provides a MIR interpreter, which is used in const eval. -use std::{borrow::Cow, collections::HashMap, iter, ops::Range, sync::Arc}; +use std::{borrow::Cow, collections::HashMap, fmt::Write, iter, ops::Range, sync::Arc}; -use base_db::CrateId; +use base_db::{CrateId, FileId}; use chalk_ir::{ fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable}, - DebruijnIndex, TyKind, + DebruijnIndex, Mutability, }; +use either::Either; use hir_def::{ builtin_type::BuiltinType, lang_item::{lang_attr, LangItem}, layout::{TagEncoding, Variants}, - AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, VariantId, + AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, StaticId, + TypeOrConstParamId, VariantId, }; +use hir_expand::{name::Name, InFile}; use intern::Interned; use la_arena::ArenaMap; +use rustc_hash::FxHashMap; +use syntax::{SyntaxNodePtr, TextRange}; use crate::{ consteval::{intern_const_scalar, ConstEvalError}, db::HirDatabase, + display::{ClosureStyle, HirDisplay}, from_placeholder_idx, infer::{normalize, PointerCast}, layout::{layout_of_ty, Layout, LayoutError, RustcEnumVariantIdx}, mapping::from_chalk, - method_resolution::{is_dyn_method, lookup_impl_method}, + method_resolution::{is_dyn_method, lookup_impl_const, lookup_impl_method}, + static_lifetime, traits::FnTrait, + utils::{generics, ClosureSubst, Generics}, CallableDefId, ClosureId, Const, ConstScalar, FnDefId, GenericArgData, Interner, MemoryMap, - Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, + Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, }; use super::{ const_as_usize, return_slot, AggregateKind, BinOp, CastKind, LocalId, MirBody, MirLowerError, - Operand, Place, ProjectionElem, Rvalue, StatementKind, Terminator, UnOp, + MirSpan, Operand, Place, ProjectionElem, Rvalue, StatementKind, TerminatorKind, UnOp, }; +mod shim; + macro_rules! from_bytes { ($ty:tt, $value:expr) => { ($ty::from_le_bytes(match ($value).try_into() { @@ -43,9 +53,15 @@ macro_rules! from_bytes { }; } -#[derive(Debug, Default)] -struct VTableMap { - ty_to_id: HashMap<Ty, usize>, +macro_rules! not_supported { + ($x: expr) => { + return Err(MirEvalError::NotSupported(format!($x))) + }; +} + +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub struct VTableMap { + ty_to_id: FxHashMap<Ty, usize>, id_to_ty: Vec<Ty>, } @@ -75,6 +91,9 @@ pub struct Evaluator<'a> { trait_env: Arc<TraitEnvironment>, stack: Vec<u8>, heap: Vec<u8>, + /// Stores the global location of the statics. We const evaluate every static first time we need it + /// and see it's missing, then we add it to this to reuse. + static_locations: FxHashMap<StaticId, Address>, /// We don't really have function pointers, i.e. pointers to some assembly instructions that we can run. Instead, we /// store the type as an interned id in place of function and vtable pointers, and we recover back the type at the /// time of use. @@ -88,7 +107,7 @@ pub struct Evaluator<'a> { stack_depth_limit: usize, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] enum Address { Stack(usize), Heap(usize), @@ -153,6 +172,13 @@ enum IntervalOrOwned { Owned(Vec<u8>), Borrowed(Interval), } + +impl From<Interval> for IntervalOrOwned { + fn from(it: Interval) -> IntervalOrOwned { + IntervalOrOwned::Borrowed(it) + } +} + impl IntervalOrOwned { pub(crate) fn to_vec(self, memory: &Evaluator<'_>) -> Result<Vec<u8>> { Ok(match self { @@ -160,6 +186,13 @@ impl IntervalOrOwned { IntervalOrOwned::Borrowed(b) => b.get(memory)?.to_vec(), }) } + + fn get<'a>(&'a self, memory: &'a Evaluator<'a>) -> Result<&'a [u8]> { + Ok(match self { + IntervalOrOwned::Owned(o) => o, + IntervalOrOwned::Borrowed(b) => b.get(memory)?, + }) + } } impl Address { @@ -205,30 +238,129 @@ impl Address { #[derive(Clone, PartialEq, Eq)] pub enum MirEvalError { - ConstEvalError(Box<ConstEvalError>), + ConstEvalError(String, Box<ConstEvalError>), LayoutError(LayoutError, Ty), /// Means that code had type errors (or mismatched args) and we shouldn't generate mir in first place. TypeError(&'static str), /// Means that code had undefined behavior. We don't try to actively detect UB, but if it was detected /// then use this type of error. - UndefinedBehavior(&'static str), + UndefinedBehavior(String), + GenericArgNotProvided(TypeOrConstParamId, Substitution), Panic(String), MirLowerError(FunctionId, MirLowerError), MirLowerErrorForClosure(ClosureId, MirLowerError), TypeIsUnsized(Ty, &'static str), NotSupported(String), InvalidConst(Const), - InFunction(FunctionId, Box<MirEvalError>), + InFunction(Either<FunctionId, ClosureId>, Box<MirEvalError>, MirSpan, DefWithBodyId), ExecutionLimitExceeded, StackOverflow, TargetDataLayoutNotAvailable, InvalidVTableId(usize), + CoerceUnsizedError(Ty), +} + +impl MirEvalError { + pub fn pretty_print( + &self, + f: &mut String, + db: &dyn HirDatabase, + span_formatter: impl Fn(FileId, TextRange) -> String, + ) -> std::result::Result<(), std::fmt::Error> { + writeln!(f, "Mir eval error:")?; + let mut err = self; + while let MirEvalError::InFunction(func, e, span, def) = err { + err = e; + match func { + Either::Left(func) => { + let function_name = db.function_data(*func); + writeln!(f, "In function {} ({:?})", function_name.name, func)?; + } + Either::Right(clos) => { + writeln!(f, "In {:?}", clos)?; + } + } + let source_map = db.body_with_source_map(*def).1; + let span: InFile<SyntaxNodePtr> = match span { + MirSpan::ExprId(e) => match source_map.expr_syntax(*e) { + Ok(s) => s.map(|x| x.into()), + Err(_) => continue, + }, + MirSpan::PatId(p) => match source_map.pat_syntax(*p) { + Ok(s) => s.map(|x| match x { + Either::Left(e) => e.into(), + Either::Right(e) => e.into(), + }), + Err(_) => continue, + }, + MirSpan::Unknown => continue, + }; + let file_id = span.file_id.original_file(db.upcast()); + let text_range = span.value.text_range(); + writeln!(f, "{}", span_formatter(file_id, text_range))?; + } + match err { + MirEvalError::InFunction(..) => unreachable!(), + MirEvalError::LayoutError(err, ty) => { + write!( + f, + "Layout for type `{}` is not available due {err:?}", + ty.display(db).with_closure_style(ClosureStyle::ClosureWithId).to_string() + )?; + } + MirEvalError::GenericArgNotProvided(id, subst) => { + let parent = id.parent; + let param = &db.generic_params(parent).type_or_consts[id.local_id]; + writeln!( + f, + "Generic arg not provided for {}", + param.name().unwrap_or(&Name::missing()) + )?; + writeln!(f, "Provided args: [")?; + for g in subst.iter(Interner) { + write!(f, " {},", g.display(db).to_string())?; + } + writeln!(f, "]")?; + } + MirEvalError::MirLowerError(func, err) => { + let function_name = db.function_data(*func); + writeln!( + f, + "MIR lowering for function `{}` ({:?}) failed due:", + function_name.name, func + )?; + err.pretty_print(f, db, span_formatter)?; + } + MirEvalError::ConstEvalError(name, err) => { + MirLowerError::ConstEvalError(name.clone(), err.clone()).pretty_print( + f, + db, + span_formatter, + )?; + } + MirEvalError::TypeError(_) + | MirEvalError::UndefinedBehavior(_) + | MirEvalError::Panic(_) + | MirEvalError::MirLowerErrorForClosure(_, _) + | MirEvalError::TypeIsUnsized(_, _) + | MirEvalError::NotSupported(_) + | MirEvalError::InvalidConst(_) + | MirEvalError::ExecutionLimitExceeded + | MirEvalError::StackOverflow + | MirEvalError::TargetDataLayoutNotAvailable + | MirEvalError::CoerceUnsizedError(_) + | MirEvalError::InvalidVTableId(_) => writeln!(f, "{:?}", err)?, + } + Ok(()) + } } impl std::fmt::Debug for MirEvalError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::ConstEvalError(arg0) => f.debug_tuple("ConstEvalError").field(arg0).finish(), + Self::ConstEvalError(arg0, arg1) => { + f.debug_tuple("ConstEvalError").field(arg0).field(arg1).finish() + } Self::LayoutError(arg0, arg1) => { f.debug_tuple("LayoutError").field(arg0).field(arg1).finish() } @@ -241,24 +373,28 @@ impl std::fmt::Debug for MirEvalError { Self::TypeIsUnsized(ty, it) => write!(f, "{ty:?} is unsized. {it} should be sized."), Self::ExecutionLimitExceeded => write!(f, "execution limit exceeded"), Self::StackOverflow => write!(f, "stack overflow"), + Self::GenericArgNotProvided(..) => f.debug_tuple("GenericArgNotProvided").finish(), Self::MirLowerError(arg0, arg1) => { f.debug_tuple("MirLowerError").field(arg0).field(arg1).finish() } Self::MirLowerErrorForClosure(arg0, arg1) => { f.debug_tuple("MirLowerError").field(arg0).field(arg1).finish() } + Self::CoerceUnsizedError(arg0) => { + f.debug_tuple("CoerceUnsizedError").field(arg0).finish() + } Self::InvalidVTableId(arg0) => f.debug_tuple("InvalidVTableId").field(arg0).finish(), Self::NotSupported(arg0) => f.debug_tuple("NotSupported").field(arg0).finish(), Self::InvalidConst(arg0) => { let data = &arg0.data(Interner); f.debug_struct("InvalidConst").field("ty", &data.ty).field("value", &arg0).finish() } - Self::InFunction(func, e) => { + Self::InFunction(func, e, span, _) => { let mut e = &**e; - let mut stack = vec![*func]; - while let Self::InFunction(f, next_e) = e { + let mut stack = vec![(*func, *span)]; + while let Self::InFunction(f, next_e, span, _) = e { e = &next_e; - stack.push(*f); + stack.push((*f, *span)); } f.debug_struct("WithStack").field("error", e).field("stack", &stack).finish() } @@ -266,22 +402,9 @@ impl std::fmt::Debug for MirEvalError { } } -macro_rules! not_supported { - ($x: expr) => { - return Err(MirEvalError::NotSupported(format!($x))) - }; -} - -impl From<ConstEvalError> for MirEvalError { - fn from(value: ConstEvalError) -> Self { - match value { - _ => MirEvalError::ConstEvalError(Box::new(value)), - } - } -} - type Result<T> = std::result::Result<T, MirEvalError>; +#[derive(Debug)] struct Locals<'a> { ptr: &'a ArenaMap<LocalId, Address>, body: &'a MirBody, @@ -301,12 +424,14 @@ pub fn interpret_mir( ) -> Result<Const> { let ty = body.locals[return_slot()].ty.clone(); let mut evaluator = Evaluator::new(db, body, assert_placeholder_ty_is_unused); + let ty = evaluator.ty_filler(&ty, &subst, body.owner)?; let bytes = evaluator.interpret_mir(&body, None.into_iter(), subst.clone())?; - let memory_map = evaluator.create_memory_map( + let mut memory_map = evaluator.create_memory_map( &bytes, &ty, &Locals { ptr: &ArenaMap::new(), body: &body, subst: &subst }, )?; + memory_map.vtable = evaluator.vtable_map; return Ok(intern_const_scalar(ConstScalar::Bytes(bytes, memory_map), ty)); } @@ -322,6 +447,7 @@ impl Evaluator<'_> { stack: vec![0], heap: vec![0], vtable_map: VTableMap::default(), + static_locations: HashMap::default(), db, trait_env, crate_id, @@ -365,15 +491,21 @@ impl Evaluator<'_> { let mut metadata = None; // locals are always sized for proj in &p.projection { let prev_ty = ty.clone(); - ty = proj.projected_ty(ty, self.db, |c, f| { + ty = proj.projected_ty(ty, self.db, |c, subst, f| { let (def, _) = self.db.lookup_intern_closure(c.into()); let infer = self.db.infer(def); let (captures, _) = infer.closure_info(&c); - captures.get(f).expect("broken closure field").ty.clone() + let parent_subst = ClosureSubst(subst).parent_subst(); + captures + .get(f) + .expect("broken closure field") + .ty + .clone() + .substitute(Interner, parent_subst) }); match proj { ProjectionElem::Deref => { - metadata = if self.size_of(&ty, locals)?.is_none() { + metadata = if self.size_align_of(&ty, locals)?.is_none() { Some(Interval { addr: addr.offset(self.ptr_size()), size: self.ptr_size() }) } else { None @@ -435,6 +567,11 @@ impl Evaluator<'_> { .map_err(|e| MirEvalError::LayoutError(e, ty.clone())) } + fn layout_filled(&self, ty: &Ty, locals: &Locals<'_>) -> Result<Layout> { + let ty = &self.ty_filler(ty, locals.subst, locals.body.owner)?; + self.layout(ty) + } + fn layout_adt(&self, adt: AdtId, subst: Substitution) -> Result<Layout> { self.db.layout_of_adt(adt, subst.clone()).map_err(|e| { MirEvalError::LayoutError(e, TyKind::Adt(chalk_ir::AdtId(adt), subst).intern(Interner)) @@ -449,6 +586,10 @@ impl Evaluator<'_> { Ok(match o { Operand::Copy(p) | Operand::Move(p) => self.place_ty(p, locals)?, Operand::Constant(c) => c.data(Interner).ty.clone(), + &Operand::Static(s) => { + let ty = self.db.infer(s.into())[self.db.body(s.into()).body_expr].clone(); + TyKind::Ref(Mutability::Not, static_lifetime(), ty).intern(Interner) + } }) } @@ -524,11 +665,11 @@ impl Evaluator<'_> { let Some(terminator) = current_block.terminator.as_ref() else { not_supported!("block without terminator"); }; - match terminator { - Terminator::Goto { target } => { + match &terminator.kind { + TerminatorKind::Goto { target } => { current_block_idx = *target; } - Terminator::Call { + TerminatorKind::Call { func, args, destination, @@ -545,23 +686,36 @@ impl Evaluator<'_> { match &fn_ty.data(Interner).kind { TyKind::Function(_) => { let bytes = self.eval_operand(func, &locals)?; - self.exec_fn_pointer(bytes, destination, &args, &locals)?; + self.exec_fn_pointer( + bytes, + destination, + &args, + &locals, + terminator.span, + )?; } TyKind::FnDef(def, generic_args) => { - self.exec_fn_def(*def, generic_args, destination, &args, &locals)?; + self.exec_fn_def( + *def, + generic_args, + destination, + &args, + &locals, + terminator.span, + )?; } x => not_supported!("unknown function type {x:?}"), } current_block_idx = target.expect("broken mir, function without target"); } - Terminator::SwitchInt { discr, targets } => { + TerminatorKind::SwitchInt { discr, targets } => { let val = u128::from_le_bytes(pad16( self.eval_operand(discr, &locals)?.get(&self)?, false, )); current_block_idx = targets.target_for_value(val); } - Terminator::Return => { + TerminatorKind::Return => { let ty = body.locals[return_slot()].ty.clone(); self.stack_depth_limit += 1; return Ok(self @@ -571,8 +725,8 @@ impl Evaluator<'_> { )? .to_owned()); } - Terminator::Unreachable => { - return Err(MirEvalError::UndefinedBehavior("unreachable executed")); + TerminatorKind::Unreachable => { + return Err(MirEvalError::UndefinedBehavior("unreachable executed".to_owned())); } _ => not_supported!("unknown terminator"), } @@ -725,13 +879,13 @@ impl Evaluator<'_> { }; match layout.variants { Variants::Single { index } => { - let r = self.db.const_eval_discriminant(EnumVariantId { + let r = self.const_eval_discriminant(EnumVariantId { parent: enum_id, local_id: index.0, })?; Owned(r.to_le_bytes().to_vec()) } - Variants::Multiple { tag, tag_encoding, .. } => { + Variants::Multiple { tag, tag_encoding, variants, .. } => { let Some(target_data_layout) = self.db.target_data_layout(self.crate_id) else { not_supported!("missing target data layout"); }; @@ -744,25 +898,20 @@ impl Evaluator<'_> { } TagEncoding::Niche { untagged_variant, niche_start, .. } => { let tag = &bytes[offset..offset + size]; - let candidate_discriminant = i128::from_le_bytes(pad16(tag, false)) - .wrapping_sub(niche_start as i128); - let enum_data = self.db.enum_data(enum_id); - let result = 'b: { - for (local_id, _) in enum_data.variants.iter() { - if candidate_discriminant - == self.db.const_eval_discriminant(EnumVariantId { - parent: enum_id, - local_id, - })? - { - break 'b candidate_discriminant; - } - } - self.db.const_eval_discriminant(EnumVariantId { - parent: enum_id, - local_id: untagged_variant.0, - })? - }; + let candidate_tag = i128::from_le_bytes(pad16(tag, false)) + .wrapping_sub(niche_start as i128) + as usize; + let variant = variants + .iter_enumerated() + .map(|(x, _)| x) + .filter(|x| *x != untagged_variant) + .nth(candidate_tag) + .unwrap_or(untagged_variant) + .0; + let result = self.const_eval_discriminant(EnumVariantId { + parent: enum_id, + local_id: variant, + })?; Owned(result.to_le_bytes().to_vec()) } } @@ -771,6 +920,13 @@ impl Evaluator<'_> { } Rvalue::Repeat(_, _) => not_supported!("evaluating repeat rvalue"), Rvalue::ShallowInitBox(_, _) => not_supported!("shallow init box"), + Rvalue::ShallowInitBoxWithAlloc(ty) => { + let Some((size, align)) = self.size_align_of(ty, locals)? else { + not_supported!("unsized box initialization"); + }; + let addr = self.heap_allocate(size, align); + Owned(addr.to_bytes()) + } Rvalue::CopyForDeref(_) => not_supported!("copy for deref"), Rvalue::Aggregate(kind, values) => { let values = values @@ -787,12 +943,12 @@ impl Evaluator<'_> { Owned(r) } AggregateKind::Tuple(ty) => { - let layout = self.layout(&ty)?; + let layout = self.layout_filled(&ty, locals)?; Owned(self.make_by_layout( layout.size.bytes_usize(), &layout, None, - values.iter().copied(), + values.iter().map(|&x| x.into()), )?) } AggregateKind::Union(x, f) => { @@ -814,74 +970,44 @@ impl Evaluator<'_> { size, &variant_layout, tag, - values.iter().copied(), + values.iter().map(|&x| x.into()), )?) } AggregateKind::Closure(ty) => { - let layout = self.layout(&ty)?; + let layout = self.layout_filled(&ty, locals)?; Owned(self.make_by_layout( layout.size.bytes_usize(), &layout, None, - values.iter().copied(), + values.iter().map(|&x| x.into()), )?) } } } Rvalue::Cast(kind, operand, target_ty) => match kind { CastKind::Pointer(cast) => match cast { - PointerCast::ReifyFnPointer => { + PointerCast::ReifyFnPointer | PointerCast::ClosureFnPointer(_) => { let current_ty = self.operand_ty(operand, locals)?; - if let TyKind::FnDef(_, _) = ¤t_ty.data(Interner).kind { + if let TyKind::FnDef(_, _) | TyKind::Closure(_, _) = + ¤t_ty.data(Interner).kind + { let id = self.vtable_map.id(current_ty); let ptr_size = self.ptr_size(); Owned(id.to_le_bytes()[0..ptr_size].to_vec()) } else { - not_supported!("ReifyFnPointer cast of a non FnDef type"); + not_supported!( + "creating a fn pointer from a non FnDef or Closure type" + ); } } PointerCast::Unsize => { let current_ty = self.operand_ty(operand, locals)?; - match &target_ty.data(Interner).kind { - TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => { - match &ty.data(Interner).kind { - TyKind::Slice(_) => match ¤t_ty.data(Interner).kind { - TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => { - match &ty.data(Interner).kind { - TyKind::Array(_, size) => { - let addr = self - .eval_operand(operand, locals)? - .get(&self)?; - let len = const_as_usize(size); - let mut r = Vec::with_capacity(16); - r.extend(addr.iter().copied()); - r.extend(len.to_le_bytes().into_iter()); - Owned(r) - } - _ => { - not_supported!("slice unsizing from non arrays") - } - } - } - _ => not_supported!("slice unsizing from non pointers"), - }, - TyKind::Dyn(_) => match ¤t_ty.data(Interner).kind { - TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => { - let vtable = self.vtable_map.id(ty.clone()); - let addr = - self.eval_operand(operand, locals)?.get(&self)?; - let mut r = Vec::with_capacity(16); - r.extend(addr.iter().copied()); - r.extend(vtable.to_le_bytes().into_iter()); - Owned(r) - } - _ => not_supported!("dyn unsizing from non pointers"), - }, - _ => not_supported!("unknown unsized cast"), - } - } - _ => not_supported!("unsized cast on unknown pointer type"), - } + let addr = self.eval_operand(operand, locals)?; + self.coerce_unsized(addr, ¤t_ty, target_ty)? + } + PointerCast::MutToConstPointer | PointerCast::UnsafeFnPointer => { + // This is no-op + Borrowed(self.eval_operand(operand, locals)?) } x => not_supported!("pointer cast {x:?}"), }, @@ -909,6 +1035,77 @@ impl Evaluator<'_> { }) } + fn coerce_unsized_look_through_fields<T>( + &self, + ty: &Ty, + goal: impl Fn(&TyKind) -> Option<T>, + ) -> Result<T> { + let kind = ty.kind(Interner); + if let Some(x) = goal(kind) { + return Ok(x); + } + if let TyKind::Adt(id, subst) = kind { + if let AdtId::StructId(struct_id) = id.0 { + let field_types = self.db.field_types(struct_id.into()); + let mut field_types = field_types.iter(); + if let Some(ty) = + field_types.next().map(|x| x.1.clone().substitute(Interner, subst)) + { + return self.coerce_unsized_look_through_fields(&ty, goal); + } + } + } + Err(MirEvalError::CoerceUnsizedError(ty.clone())) + } + + fn coerce_unsized( + &mut self, + addr: Interval, + current_ty: &Ty, + target_ty: &Ty, + ) -> Result<IntervalOrOwned> { + use IntervalOrOwned::*; + fn for_ptr(x: &TyKind) -> Option<Ty> { + match x { + TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => Some(ty.clone()), + _ => None, + } + } + Ok(match self.coerce_unsized_look_through_fields(target_ty, for_ptr)? { + ty => match &ty.data(Interner).kind { + TyKind::Slice(_) => { + match self.coerce_unsized_look_through_fields(current_ty, for_ptr)? { + ty => match &ty.data(Interner).kind { + TyKind::Array(_, size) => { + let len = const_as_usize(size); + let mut r = Vec::with_capacity(16); + let addr = addr.get(self)?; + r.extend(addr.iter().copied()); + r.extend(len.to_le_bytes().into_iter()); + Owned(r) + } + _ => { + not_supported!("slice unsizing from non arrays") + } + }, + } + } + TyKind::Dyn(_) => match ¤t_ty.data(Interner).kind { + TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => { + let vtable = self.vtable_map.id(ty.clone()); + let mut r = Vec::with_capacity(16); + let addr = addr.get(self)?; + r.extend(addr.iter().copied()); + r.extend(vtable.to_le_bytes().into_iter()); + Owned(r) + } + _ => not_supported!("dyn unsizing from non pointers"), + }, + _ => not_supported!("unknown unsized cast"), + }, + }) + } + fn layout_of_variant( &mut self, x: VariantId, @@ -921,7 +1118,7 @@ impl Evaluator<'_> { if AdtId::from(f.parent) == adt { // Computing the exact size of enums require resolving the enum discriminants. In order to prevent loops (and // infinite sized type errors) we use a dummy layout - let i = self.db.const_eval_discriminant(x)?; + let i = self.const_eval_discriminant(x)?; return Ok((16, self.layout(&TyBuilder::unit())?, Some((0, 16, i)))); } } @@ -939,13 +1136,22 @@ impl Evaluator<'_> { _ => not_supported!("multi variant layout for non-enums"), }; let rustc_enum_variant_idx = RustcEnumVariantIdx(enum_variant_id.local_id); - let mut discriminant = self.db.const_eval_discriminant(enum_variant_id)?; + let mut discriminant = self.const_eval_discriminant(enum_variant_id)?; let variant_layout = variants[rustc_enum_variant_idx].clone(); let have_tag = match tag_encoding { TagEncoding::Direct => true, TagEncoding::Niche { untagged_variant, niche_variants: _, niche_start } => { - discriminant = discriminant.wrapping_add(niche_start as i128); - untagged_variant != rustc_enum_variant_idx + if untagged_variant == rustc_enum_variant_idx { + false + } else { + discriminant = (variants + .iter_enumerated() + .filter(|(x, _)| *x != untagged_variant) + .position(|(x, _)| x == rustc_enum_variant_idx) + .unwrap() as i128) + .wrapping_add(niche_start as i128); + true + } } }; ( @@ -970,7 +1176,7 @@ impl Evaluator<'_> { size: usize, // Not necessarily equal to variant_layout.size variant_layout: &Layout, tag: Option<(usize, usize, i128)>, - values: impl Iterator<Item = Interval>, + values: impl Iterator<Item = IntervalOrOwned>, ) -> Result<Vec<u8>> { let mut result = vec![0; size]; if let Some((offset, size, value)) = tag { @@ -987,6 +1193,10 @@ impl Evaluator<'_> { fn eval_operand(&mut self, x: &Operand, locals: &Locals<'_>) -> Result<Interval> { Ok(match x { Operand::Copy(p) | Operand::Move(p) => self.eval_place(p, locals)?, + Operand::Static(st) => { + let addr = self.eval_static(*st, locals)?; + Interval::new(addr, self.ptr_size()) + } Operand::Constant(konst) => { let data = &konst.data(Interner); match &data.value { @@ -1003,37 +1213,71 @@ impl Evaluator<'_> { not_supported!("inference var constant") } chalk_ir::ConstValue::Placeholder(_) => not_supported!("placeholder constant"), - chalk_ir::ConstValue::Concrete(c) => match &c.interned { - ConstScalar::Bytes(v, memory_map) => { - let mut v: Cow<'_, [u8]> = Cow::Borrowed(v); - let patch_map = memory_map.transform_addresses(|b| { - let addr = self.heap_allocate(b.len()); - self.write_memory(addr, b)?; - Ok(addr.to_usize()) - })?; - let size = self.size_of(&data.ty, locals)?.unwrap_or(v.len()); - if size != v.len() { - // Handle self enum - if size == 16 && v.len() < 16 { - v = Cow::Owned(pad16(&v, false).to_vec()); - } else if size < 16 && v.len() == 16 { - v = Cow::Owned(v[0..size].to_vec()); - } else { - return Err(MirEvalError::InvalidConst(konst.clone())); - } - } - let addr = self.heap_allocate(size); - self.write_memory(addr, &v)?; - self.patch_addresses(&patch_map, addr, &data.ty, locals)?; - Interval::new(addr, size) - } - ConstScalar::Unknown => not_supported!("evaluating unknown const"), - }, + chalk_ir::ConstValue::Concrete(c) => { + self.allocate_const_in_heap(c, &data.ty, locals, konst)? + } } } }) } + fn allocate_const_in_heap( + &mut self, + c: &chalk_ir::ConcreteConst<Interner>, + ty: &Ty, + locals: &Locals<'_>, + konst: &chalk_ir::Const<Interner>, + ) -> Result<Interval> { + Ok(match &c.interned { + ConstScalar::Bytes(v, memory_map) => { + let mut v: Cow<'_, [u8]> = Cow::Borrowed(v); + let patch_map = memory_map.transform_addresses(|b| { + let addr = self.heap_allocate(b.len(), 1); // FIXME: align is wrong + self.write_memory(addr, b)?; + Ok(addr.to_usize()) + })?; + let (size, align) = self.size_align_of(ty, locals)?.unwrap_or((v.len(), 1)); + if size != v.len() { + // Handle self enum + if size == 16 && v.len() < 16 { + v = Cow::Owned(pad16(&v, false).to_vec()); + } else if size < 16 && v.len() == 16 { + v = Cow::Owned(v[0..size].to_vec()); + } else { + return Err(MirEvalError::InvalidConst(konst.clone())); + } + } + let addr = self.heap_allocate(size, align); + self.write_memory(addr, &v)?; + self.patch_addresses(&patch_map, &memory_map.vtable, addr, ty, locals)?; + Interval::new(addr, size) + } + ConstScalar::UnevaluatedConst(const_id, subst) => { + let subst = self.subst_filler(subst, locals); + let (const_id, subst) = lookup_impl_const( + self.db, + self.db.trait_environment_for_body(locals.body.owner), + *const_id, + subst, + ); + let c = self.db.const_eval(const_id.into(), subst).map_err(|e| { + let const_data = self.db.const_data(const_id); + MirEvalError::ConstEvalError( + const_data.name.as_ref().and_then(|x| x.as_str()).unwrap_or("_").to_owned(), + Box::new(e), + ) + })?; + if let chalk_ir::ConstValue::Concrete(c) = &c.data(Interner).value { + if let ConstScalar::Bytes(_, _) = &c.interned { + return self.allocate_const_in_heap(&c, ty, locals, konst); + } + } + not_supported!("failing at evaluating unevaluated const"); + } + ConstScalar::Unknown => not_supported!("evaluating unknown const"), + }) + } + fn eval_place(&mut self, p: &Place, locals: &Locals<'_>) -> Result<Interval> { let addr = self.place_addr(p, locals)?; Ok(Interval::new( @@ -1046,53 +1290,61 @@ impl Evaluator<'_> { let (mem, pos) = match addr { Stack(x) => (&self.stack, x), Heap(x) => (&self.heap, x), - Invalid(_) => { - return Err(MirEvalError::UndefinedBehavior("read invalid memory address")) + Invalid(x) => { + return Err(MirEvalError::UndefinedBehavior(format!( + "read invalid memory address {x} with size {size}" + ))); } }; - mem.get(pos..pos + size).ok_or(MirEvalError::UndefinedBehavior("out of bound memory read")) + mem.get(pos..pos + size) + .ok_or_else(|| MirEvalError::UndefinedBehavior("out of bound memory read".to_string())) } fn write_memory(&mut self, addr: Address, r: &[u8]) -> Result<()> { let (mem, pos) = match addr { Stack(x) => (&mut self.stack, x), Heap(x) => (&mut self.heap, x), - Invalid(_) => { - return Err(MirEvalError::UndefinedBehavior("write invalid memory address")) + Invalid(x) => { + return Err(MirEvalError::UndefinedBehavior(format!( + "write invalid memory address {x} with content {r:?}" + ))); } }; mem.get_mut(pos..pos + r.len()) - .ok_or(MirEvalError::UndefinedBehavior("out of bound memory write"))? + .ok_or_else(|| { + MirEvalError::UndefinedBehavior("out of bound memory write".to_string()) + })? .copy_from_slice(r); Ok(()) } - fn size_of(&self, ty: &Ty, locals: &Locals<'_>) -> Result<Option<usize>> { + fn size_align_of(&self, ty: &Ty, locals: &Locals<'_>) -> Result<Option<(usize, usize)>> { if let DefWithBodyId::VariantId(f) = locals.body.owner { if let Some((adt, _)) = ty.as_adt() { if AdtId::from(f.parent) == adt { // Computing the exact size of enums require resolving the enum discriminants. In order to prevent loops (and // infinite sized type errors) we use a dummy size - return Ok(Some(16)); + return Ok(Some((16, 16))); } } } - let ty = &self.ty_filler(ty, locals.subst, locals.body.owner)?; - let layout = self.layout(ty); + let layout = self.layout_filled(ty, locals); if self.assert_placeholder_ty_is_unused { if matches!(layout, Err(MirEvalError::LayoutError(LayoutError::HasPlaceholder, _))) { - return Ok(Some(0)); + return Ok(Some((0, 1))); } } let layout = layout?; - Ok(layout.is_sized().then(|| layout.size.bytes_usize())) + Ok(layout + .is_sized() + .then(|| (layout.size.bytes_usize(), layout.align.abi.bytes() as usize))) } /// A version of `self.size_of` which returns error if the type is unsized. `what` argument should /// be something that complete this: `error: type {ty} was unsized. {what} should be sized` fn size_of_sized(&self, ty: &Ty, locals: &Locals<'_>, what: &'static str) -> Result<usize> { - match self.size_of(ty, locals)? { - Some(x) => Ok(x), + match self.size_align_of(ty, locals)? { + Some(x) => Ok(x.0), None => Err(MirEvalError::TypeIsUnsized(ty.clone(), what)), } } @@ -1120,7 +1372,7 @@ impl Evaluator<'_> { struct Filler<'a> { db: &'a dyn HirDatabase, subst: &'a Substitution, - skip_params: usize, + generics: Option<Generics>, } impl FallibleTypeFolder<Interner> for Filler<'_> { type Error = MirEvalError; @@ -1144,7 +1396,11 @@ impl Evaluator<'_> { match impl_trait_id { crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { let infer = self.db.infer(func.into()); - let filler = &mut Filler { db: self.db, subst, skip_params: 0 }; + let filler = &mut Filler { + db: self.db, + subst, + generics: Some(generics(self.db.upcast(), func.into())), + }; filler.try_fold_ty(infer.type_of_rpit[idx].clone(), outer_binder) } crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { @@ -1162,22 +1418,36 @@ impl Evaluator<'_> { _outer_binder: DebruijnIndex, ) -> std::result::Result<Ty, Self::Error> { let x = from_placeholder_idx(self.db, idx); + let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(x)) else { + not_supported!("missing idx in generics"); + }; Ok(self .subst .as_slice(Interner) - .get((u32::from(x.local_id.into_raw()) as usize) + self.skip_params) + .get(idx) .and_then(|x| x.ty(Interner)) - .ok_or(MirEvalError::TypeError("Generic arg not provided"))? + .ok_or_else(|| MirEvalError::GenericArgNotProvided(x, self.subst.clone()))? .clone()) } } - let filler = &mut Filler { db: self.db, subst, skip_params: 0 }; - Ok(normalize(self.db, owner, ty.clone().try_fold_with(filler, DebruijnIndex::INNERMOST)?)) + let g_def = match owner { + DefWithBodyId::FunctionId(f) => Some(f.into()), + DefWithBodyId::StaticId(_) => None, + DefWithBodyId::ConstId(f) => Some(f.into()), + DefWithBodyId::VariantId(f) => Some(f.into()), + }; + let generics = g_def.map(|g_def| generics(self.db.upcast(), g_def)); + let filler = &mut Filler { db: self.db, subst, generics }; + Ok(normalize( + self.db, + self.trait_env.clone(), + ty.clone().try_fold_with(filler, DebruijnIndex::INNERMOST)?, + )) } - fn heap_allocate(&mut self, s: usize) -> Address { + fn heap_allocate(&mut self, size: usize, _align: usize) -> Address { let pos = self.heap.len(); - self.heap.extend(iter::repeat(0).take(s)); + self.heap.extend(iter::repeat(0).take(size)); Address::Heap(pos) } @@ -1185,16 +1455,6 @@ impl Evaluator<'_> { self.interpret_mir(&body, vec![].into_iter(), Substitution::empty(Interner)) } - fn detect_lang_function(&self, def: FunctionId) -> Option<LangItem> { - use LangItem::*; - let candidate = lang_attr(self.db.upcast(), def)?; - // We want to execute these functions with special logic - if [PanicFmt, BeginPanic, SliceLen].contains(&candidate) { - return Some(candidate); - } - None - } - fn detect_fn_trait(&self, def: FunctionId) -> Option<FnTrait> { use LangItem::*; let ItemContainerId::TraitId(parent) = self.db.lookup_intern_function(def).container else { @@ -1214,9 +1474,9 @@ impl Evaluator<'_> { let mut mm = MemoryMap::default(); match ty.kind(Interner) { TyKind::Ref(_, _, t) => { - let size = self.size_of(t, locals)?; + let size = self.size_align_of(t, locals)?; match size { - Some(size) => { + Some((size, _)) => { let addr_usize = from_bytes!(usize, bytes); mm.insert( addr_usize, @@ -1246,15 +1506,17 @@ impl Evaluator<'_> { fn patch_addresses( &mut self, patch_map: &HashMap<usize, usize>, + old_vtable: &VTableMap, addr: Address, ty: &Ty, locals: &Locals<'_>, ) -> Result<()> { // FIXME: support indirect references + let layout = self.layout(ty)?; let my_size = self.size_of_sized(ty, locals, "value to patch address")?; match ty.kind(Interner) { TyKind::Ref(_, _, t) => { - let size = self.size_of(t, locals)?; + let size = self.size_align_of(t, locals)?; match size { Some(_) => { let current = from_bytes!(usize, self.read_memory(addr, my_size)?); @@ -1270,98 +1532,50 @@ impl Evaluator<'_> { } } } - _ => (), - } - Ok(()) - } - - fn exec_intrinsic( - &mut self, - as_str: &str, - args: &[IntervalAndTy], - generic_args: Substitution, - destination: Interval, - locals: &Locals<'_>, - ) -> Result<()> { - match as_str { - "size_of" => { - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { - return Err(MirEvalError::TypeError("size_of generic arg is not provided")); - }; - let size = self.size_of_sized(ty, locals, "size_of arg")?; - destination.write_from_bytes(self, &size.to_le_bytes()[0..destination.size]) - } - "wrapping_add" => { - let [lhs, rhs] = args else { - return Err(MirEvalError::TypeError("const_eval_select args are not provided")); - }; - let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false)); - let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false)); - let ans = lhs.wrapping_add(rhs); - destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]) - } - "copy" | "copy_nonoverlapping" => { - let [src, dst, offset] = args else { - return Err(MirEvalError::TypeError("copy_nonoverlapping args are not provided")); - }; - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { - return Err(MirEvalError::TypeError("copy_nonoverlapping generic arg is not provided")); - }; - let src = Address::from_bytes(src.get(self)?)?; - let dst = Address::from_bytes(dst.get(self)?)?; - let offset = from_bytes!(usize, offset.get(self)?); - let size = self.size_of_sized(ty, locals, "copy_nonoverlapping ptr type")?; - let size = offset * size; - let src = Interval { addr: src, size }; - let dst = Interval { addr: dst, size }; - dst.write_from_interval(self, src) - } - "offset" | "arith_offset" => { - let [ptr, offset] = args else { - return Err(MirEvalError::TypeError("offset args are not provided")); - }; - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { - return Err(MirEvalError::TypeError("offset generic arg is not provided")); - }; - let ptr = u128::from_le_bytes(pad16(ptr.get(self)?, false)); - let offset = u128::from_le_bytes(pad16(offset.get(self)?, false)); - let size = self.size_of_sized(ty, locals, "offset ptr type")? as u128; - let ans = ptr + offset * size; - destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]) - } - "assert_inhabited" | "assert_zero_valid" | "assert_uninit_valid" => { - // FIXME: We should actually implement these checks - Ok(()) - } - "forget" => { - // We don't call any drop glue yet, so there is nothing here - Ok(()) - } - "transmute" => { - let [arg] = args else { - return Err(MirEvalError::TypeError("trasmute arg is not provided")); - }; - destination.write_from_interval(self, arg.interval) + TyKind::Function(_) => { + let ty = old_vtable.ty_of_bytes(self.read_memory(addr, my_size)?)?.clone(); + let new_id = self.vtable_map.id(ty); + self.write_memory(addr, &new_id.to_le_bytes())?; } - "const_eval_select" => { - let [tuple, const_fn, _] = args else { - return Err(MirEvalError::TypeError("const_eval_select args are not provided")); - }; - let mut args = vec![const_fn.clone()]; - let TyKind::Tuple(_, fields) = tuple.ty.kind(Interner) else { - return Err(MirEvalError::TypeError("const_eval_select arg[0] is not a tuple")); - }; - let layout = self.layout(&tuple.ty)?; - for (i, field) in fields.iter(Interner).enumerate() { - let field = field.assert_ty_ref(Interner).clone(); - let offset = layout.fields.offset(i).bytes_usize(); - let addr = tuple.interval.addr.offset(offset); - args.push(IntervalAndTy::new(addr, field, self, locals)?); + TyKind::Adt(id, subst) => match id.0 { + AdtId::StructId(s) => { + for (i, (_, ty)) in self.db.field_types(s.into()).iter().enumerate() { + let offset = layout.fields.offset(i).bytes_usize(); + let ty = ty.clone().substitute(Interner, subst); + self.patch_addresses( + patch_map, + old_vtable, + addr.offset(offset), + &ty, + locals, + )?; + } } - self.exec_fn_trait(&args, destination, locals) - } - _ => not_supported!("unknown intrinsic {as_str}"), + AdtId::UnionId(_) => (), + AdtId::EnumId(_) => (), + }, + TyKind::AssociatedType(_, _) + | TyKind::Scalar(_) + | TyKind::Tuple(_, _) + | TyKind::Array(_, _) + | TyKind::Slice(_) + | TyKind::Raw(_, _) + | TyKind::OpaqueType(_, _) + | TyKind::FnDef(_, _) + | TyKind::Str + | TyKind::Never + | TyKind::Closure(_, _) + | TyKind::Generator(_, _) + | TyKind::GeneratorWitness(_, _) + | TyKind::Foreign(_) + | TyKind::Error + | TyKind::Placeholder(_) + | TyKind::Dyn(_) + | TyKind::Alias(_) + | TyKind::BoundVar(_) + | TyKind::InferenceVar(_, _) => (), } + Ok(()) } fn exec_fn_pointer( @@ -1370,13 +1584,18 @@ impl Evaluator<'_> { destination: Interval, args: &[IntervalAndTy], locals: &Locals<'_>, + span: MirSpan, ) -> Result<()> { let id = from_bytes!(usize, bytes.get(self)?); let next_ty = self.vtable_map.ty(id)?.clone(); - if let TyKind::FnDef(def, generic_args) = &next_ty.data(Interner).kind { - self.exec_fn_def(*def, generic_args, destination, args, &locals)?; - } else { - return Err(MirEvalError::TypeError("function pointer to non function")); + match &next_ty.data(Interner).kind { + TyKind::FnDef(def, generic_args) => { + self.exec_fn_def(*def, generic_args, destination, args, &locals, span)?; + } + TyKind::Closure(id, subst) => { + self.exec_closure(*id, bytes.slice(0..0), subst, destination, args, locals, span)?; + } + _ => return Err(MirEvalError::TypeError("function pointer to non function")), } Ok(()) } @@ -1388,6 +1607,8 @@ impl Evaluator<'_> { generic_args: &Substitution, destination: Interval, args: &[IntervalAndTy], + locals: &Locals<'_>, + span: MirSpan, ) -> Result<()> { let mir_body = self .db @@ -1396,7 +1617,16 @@ impl Evaluator<'_> { let arg_bytes = iter::once(Ok(closure_data.get(self)?.to_owned())) .chain(args.iter().map(|x| Ok(x.get(&self)?.to_owned()))) .collect::<Result<Vec<_>>>()?; - let bytes = self.interpret_mir(&mir_body, arg_bytes.into_iter(), generic_args.clone())?; + let bytes = self + .interpret_mir(&mir_body, arg_bytes.into_iter(), generic_args.clone()) + .map_err(|e| { + MirEvalError::InFunction( + Either::Right(closure), + Box::new(e), + span, + locals.body.owner, + ) + })?; destination.write_from_bytes(self, &bytes) } @@ -1407,16 +1637,17 @@ impl Evaluator<'_> { destination: Interval, args: &[IntervalAndTy], locals: &Locals<'_>, + span: MirSpan, ) -> Result<()> { let def: CallableDefId = from_chalk(self.db, def); let generic_args = self.subst_filler(generic_args, &locals); match def { CallableDefId::FunctionId(def) => { if let Some(_) = self.detect_fn_trait(def) { - self.exec_fn_trait(&args, destination, locals)?; + self.exec_fn_trait(&args, destination, locals, span)?; return Ok(()); } - self.exec_fn_with_args(def, args, generic_args, locals, destination)?; + self.exec_fn_with_args(def, args, generic_args, locals, destination, span)?; } CallableDefId::StructId(id) => { let (size, variant_layout, tag) = @@ -1425,7 +1656,7 @@ impl Evaluator<'_> { size, &variant_layout, tag, - args.iter().map(|x| x.interval), + args.iter().map(|x| x.interval.into()), )?; destination.write_from_bytes(self, &result)?; } @@ -1436,7 +1667,7 @@ impl Evaluator<'_> { size, &variant_layout, tag, - args.iter().map(|x| x.interval), + args.iter().map(|x| x.interval.into()), )?; destination.write_from_bytes(self, &result)?; } @@ -1451,50 +1682,37 @@ impl Evaluator<'_> { generic_args: Substitution, locals: &Locals<'_>, destination: Interval, + span: MirSpan, ) -> Result<()> { - let function_data = self.db.function_data(def); - let is_intrinsic = match &function_data.abi { - Some(abi) => *abi == Interned::new_str("rust-intrinsic"), - None => match def.lookup(self.db.upcast()).container { - hir_def::ItemContainerId::ExternBlockId(block) => { - let id = block.lookup(self.db.upcast()).id; - id.item_tree(self.db.upcast())[id.value].abi.as_deref() - == Some("rust-intrinsic") - } - _ => false, - }, - }; - if is_intrinsic { - return self.exec_intrinsic( - function_data.name.as_text().unwrap_or_default().as_str(), - args, - generic_args, - destination, - &locals, - ); + if self.detect_and_exec_special_function( + def, + args, + &generic_args, + locals, + destination, + span, + )? { + return Ok(()); } let arg_bytes = args.iter().map(|x| Ok(x.get(&self)?.to_owned())).collect::<Result<Vec<_>>>()?; - let result = if let Some(x) = self.detect_lang_function(def) { - self.exec_lang_item(x, &arg_bytes)? - } else { - if let Some(self_ty_idx) = - is_dyn_method(self.db, self.trait_env.clone(), def, generic_args.clone()) - { - // In the layout of current possible receiver, which at the moment of writing this code is one of - // `&T`, `&mut T`, `Box<T>`, `Rc<T>`, `Arc<T>`, and `Pin<P>` where `P` is one of possible receivers, - // the vtable is exactly in the `[ptr_size..2*ptr_size]` bytes. So we can use it without branching on - // the type. - let ty = self - .vtable_map - .ty_of_bytes(&arg_bytes[0][self.ptr_size()..self.ptr_size() * 2])?; - let mut args_for_target = args.to_vec(); - args_for_target[0] = IntervalAndTy { - interval: args_for_target[0].interval.slice(0..self.ptr_size()), - ty: ty.clone(), - }; - let ty = GenericArgData::Ty(ty.clone()).intern(Interner); - let generics_for_target = Substitution::from_iter( + if let Some(self_ty_idx) = + is_dyn_method(self.db, self.trait_env.clone(), def, generic_args.clone()) + { + // In the layout of current possible receiver, which at the moment of writing this code is one of + // `&T`, `&mut T`, `Box<T>`, `Rc<T>`, `Arc<T>`, and `Pin<P>` where `P` is one of possible recievers, + // the vtable is exactly in the `[ptr_size..2*ptr_size]` bytes. So we can use it without branching on + // the type. + let ty = + self.vtable_map.ty_of_bytes(&arg_bytes[0][self.ptr_size()..self.ptr_size() * 2])?; + let mut args_for_target = args.to_vec(); + args_for_target[0] = IntervalAndTy { + interval: args_for_target[0].interval.slice(0..self.ptr_size()), + ty: ty.clone(), + }; + let ty = GenericArgData::Ty(ty.clone()).intern(Interner); + let generics_for_target = + Substitution::from_iter( Interner, generic_args.iter(Interner).enumerate().map(|(i, x)| { if i == self_ty_idx { @@ -1504,23 +1722,25 @@ impl Evaluator<'_> { } }), ); - return self.exec_fn_with_args( - def, - &args_for_target, - generics_for_target, - locals, - destination, - ); - } - let (imp, generic_args) = - lookup_impl_method(self.db, self.trait_env.clone(), def, generic_args); - let generic_args = self.subst_filler(&generic_args, &locals); - let def = imp.into(); - let mir_body = - self.db.mir_body(def).map_err(|e| MirEvalError::MirLowerError(imp, e))?; - self.interpret_mir(&mir_body, arg_bytes.iter().cloned(), generic_args) - .map_err(|e| MirEvalError::InFunction(imp, Box::new(e)))? - }; + return self.exec_fn_with_args( + def, + &args_for_target, + generics_for_target, + locals, + destination, + span, + ); + } + let (imp, generic_args) = + lookup_impl_method(self.db, self.trait_env.clone(), def, generic_args); + let generic_args = self.subst_filler(&generic_args, &locals); + let def = imp.into(); + let mir_body = self.db.mir_body(def).map_err(|e| MirEvalError::MirLowerError(imp, e))?; + let result = self + .interpret_mir(&mir_body, arg_bytes.iter().cloned(), generic_args) + .map_err(|e| { + MirEvalError::InFunction(Either::Left(imp), Box::new(e), span, locals.body.owner) + })?; destination.write_from_bytes(self, &result)?; Ok(()) } @@ -1530,6 +1750,7 @@ impl Evaluator<'_> { args: &[IntervalAndTy], destination: Interval, locals: &Locals<'_>, + span: MirSpan, ) -> Result<()> { let func = args.get(0).ok_or(MirEvalError::TypeError("fn trait with no arg"))?; let mut func_ty = func.ty.clone(); @@ -1547,35 +1768,69 @@ impl Evaluator<'_> { } match &func_ty.data(Interner).kind { TyKind::FnDef(def, subst) => { - self.exec_fn_def(*def, subst, destination, &args[1..], locals)?; + self.exec_fn_def(*def, subst, destination, &args[1..], locals, span)?; } TyKind::Function(_) => { - self.exec_fn_pointer(func_data, destination, &args[1..], locals)?; + self.exec_fn_pointer(func_data, destination, &args[1..], locals, span)?; } TyKind::Closure(closure, subst) => { - self.exec_closure(*closure, func_data, subst, destination, &args[1..])?; + self.exec_closure( + *closure, + func_data, + &Substitution::from_iter(Interner, ClosureSubst(subst).parent_subst()), + destination, + &args[1..], + locals, + span, + )?; } x => not_supported!("Call FnTrait methods with type {x:?}"), } Ok(()) } - fn exec_lang_item(&self, x: LangItem, args: &[Vec<u8>]) -> Result<Vec<u8>> { - use LangItem::*; - let mut args = args.iter(); - match x { - // FIXME: we want to find the panic message from arguments, but it wouldn't work - // currently even if we do that, since macro expansion of panic related macros - // is dummy. - PanicFmt | BeginPanic => Err(MirEvalError::Panic("<format-args>".to_string())), - SliceLen => { - let arg = args - .next() - .ok_or(MirEvalError::TypeError("argument of <[T]>::len() is not provided"))?; - let ptr_size = arg.len() / 2; - Ok(arg[ptr_size..].into()) + fn eval_static(&mut self, st: StaticId, locals: &Locals<'_>) -> Result<Address> { + if let Some(o) = self.static_locations.get(&st) { + return Ok(*o); + }; + let static_data = self.db.static_data(st); + let result = if !static_data.is_extern { + let konst = + self.db.const_eval(st.into(), Substitution::empty(Interner)).map_err(|e| { + MirEvalError::ConstEvalError( + static_data.name.as_str().unwrap_or("_").to_owned(), + Box::new(e), + ) + })?; + let data = &konst.data(Interner); + if let chalk_ir::ConstValue::Concrete(c) = &data.value { + self.allocate_const_in_heap(&c, &data.ty, locals, &konst)? + } else { + not_supported!("unevaluatable static"); + } + } else { + let ty = &self.db.infer(st.into())[self.db.body(st.into()).body_expr]; + let Some((size, align)) = self.size_align_of(&ty, locals)? else { + not_supported!("unsized extern static"); + }; + let addr = self.heap_allocate(size, align); + Interval::new(addr, size) + }; + let addr = self.heap_allocate(self.ptr_size(), self.ptr_size()); + self.write_memory(addr, &result.addr.to_bytes())?; + self.static_locations.insert(st, addr); + Ok(addr) + } + + fn const_eval_discriminant(&self, variant: EnumVariantId) -> Result<i128> { + let r = self.db.const_eval_discriminant(variant); + match r { + Ok(r) => Ok(r), + Err(e) => { + let data = self.db.enum_data(variant.parent); + let name = format!("{}::{}", data.name, data.variants[variant.local_id].name); + Err(MirEvalError::ConstEvalError(name, Box::new(e))) } - x => not_supported!("Executing lang item {x:?}"), } } } |