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.rs963
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(_, _) = &current_ty.data(Interner).kind {
+ if let TyKind::FnDef(_, _) | TyKind::Closure(_, _) =
+ &current_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 &current_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 &current_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, &current_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 &current_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:?}"),
}
}
}