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.rs318
1 files changed, 75 insertions, 243 deletions
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index a1f69b1f28..28e7759db3 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -3,20 +3,17 @@
use std::{borrow::Cow, collections::HashMap, fmt::Write, iter, ops::Range};
use base_db::{CrateId, FileId};
-use chalk_ir::{
- fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable},
- DebruijnIndex, Mutability, ProjectionTy,
-};
+use chalk_ir::Mutability;
use either::Either;
use hir_def::{
builtin_type::BuiltinType,
data::adt::{StructFlags, VariantData},
lang_item::{lang_attr, LangItem},
layout::{TagEncoding, Variants},
- AdtId, DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, HasModule, ItemContainerId,
- Lookup, StaticId, TypeOrConstParamId, VariantId,
+ AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, StaticId,
+ VariantId,
};
-use hir_expand::{name::Name, InFile};
+use hir_expand::InFile;
use intern::Interned;
use la_arena::ArenaMap;
use rustc_hash::{FxHashMap, FxHashSet};
@@ -27,14 +24,13 @@ use crate::{
consteval::{intern_const_scalar, try_const_usize, ConstEvalError},
db::HirDatabase,
display::{ClosureStyle, HirDisplay},
- from_placeholder_idx,
- infer::{normalize, PointerCast},
- layout::{layout_of_ty, Layout, LayoutError, RustcEnumVariantIdx},
+ infer::PointerCast,
+ layout::{Layout, LayoutError, RustcEnumVariantIdx},
mapping::from_chalk,
- method_resolution::{is_dyn_method, lookup_impl_const, lookup_impl_method},
+ method_resolution::{is_dyn_method, lookup_impl_method},
name, static_lifetime,
traits::FnTrait,
- utils::{generics, ClosureSubst, Generics},
+ utils::ClosureSubst,
CallableDefId, ClosureId, Const, ConstScalar, FnDefId, GenericArgData, Interner, MemoryMap,
Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind,
};
@@ -279,7 +275,6 @@ pub enum MirEvalError {
/// 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(String),
- GenericArgNotProvided(TypeOrConstParamId, Substitution),
Panic(String),
MirLowerError(FunctionId, MirLowerError),
MirLowerErrorForClosure(ClosureId, MirLowerError),
@@ -348,20 +343,6 @@ impl MirEvalError {
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()).display(db.upcast())
- )?;
- 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!(
@@ -416,7 +397,6 @@ 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()
}
@@ -471,14 +451,12 @@ impl DropFlags {
struct Locals<'a> {
ptr: &'a ArenaMap<LocalId, Interval>,
body: &'a MirBody,
- subst: &'a Substitution,
drop_flags: DropFlags,
}
pub fn interpret_mir(
db: &dyn HirDatabase,
body: &MirBody,
- subst: Substitution,
// FIXME: This is workaround. Ideally, const generics should have a separate body (issue #7434), but now
// they share their body with their parent, so in MIR lowering we have locals of the parent body, which
// might have placeholders. With this argument, we (wrongly) assume that every placeholder type has
@@ -489,17 +467,11 @@ pub fn interpret_mir(
let ty = body.locals[return_slot()].ty.clone();
let mut evaluator = Evaluator::new(db, body, assert_placeholder_ty_is_unused);
let x: Result<Const> = (|| {
- let ty = evaluator.ty_filler(&ty, &subst, body.owner)?;
- let bytes = evaluator.interpret_mir(&body, None.into_iter(), subst.clone())?;
+ let bytes = evaluator.interpret_mir(&body, None.into_iter())?;
let mut memory_map = evaluator.create_memory_map(
&bytes,
&ty,
- &Locals {
- ptr: &ArenaMap::new(),
- body: &body,
- subst: &subst,
- drop_flags: DropFlags::default(),
- },
+ &Locals { ptr: &ArenaMap::new(), body: &body, drop_flags: DropFlags::default() },
)?;
memory_map.vtable = evaluator.vtable_map.clone();
return Ok(intern_const_scalar(ConstScalar::Bytes(bytes, memory_map), ty));
@@ -565,8 +537,7 @@ impl Evaluator<'_> {
locals: &'a Locals<'a>,
) -> Result<(Address, Ty, Option<IntervalOrOwned>)> {
let mut addr = locals.ptr[p.local].addr;
- let mut ty: Ty =
- self.ty_filler(&locals.body.locals[p.local].ty, locals.subst, locals.body.owner)?;
+ let mut ty: Ty = locals.body.locals[p.local].ty.clone();
let mut metadata: Option<IntervalOrOwned> = None; // locals are always sized
for proj in &*p.projection {
let prev_ty = ty.clone();
@@ -689,17 +660,13 @@ impl Evaluator<'_> {
Ok((addr, ty, metadata))
}
- fn layout(&self, ty: &Ty) -> Result<Layout> {
- layout_of_ty(self.db, ty, self.crate_id)
+ fn layout(&self, ty: &Ty) -> Result<Arc<Layout>> {
+ self.db
+ .layout_of_ty(ty.clone(), self.crate_id)
.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> {
+ fn layout_adt(&self, adt: AdtId, subst: Substitution) -> Result<Arc<Layout>> {
self.db.layout_of_adt(adt, subst.clone(), self.crate_id).map_err(|e| {
MirEvalError::LayoutError(e, TyKind::Adt(chalk_ir::AdtId(adt), subst).intern(Interner))
})
@@ -735,7 +702,6 @@ impl Evaluator<'_> {
&mut self,
body: &MirBody,
args: impl Iterator<Item = Vec<u8>>,
- subst: Substitution,
) -> Result<Vec<u8>> {
if let Some(x) = self.stack_depth_limit.checked_sub(1) {
self.stack_depth_limit = x;
@@ -743,12 +709,8 @@ impl Evaluator<'_> {
return Err(MirEvalError::StackOverflow);
}
let mut current_block_idx = body.start_block;
- let mut locals = Locals {
- ptr: &ArenaMap::new(),
- body: &body,
- subst: &subst,
- drop_flags: DropFlags::default(),
- };
+ let mut locals =
+ Locals { ptr: &ArenaMap::new(), body: &body, drop_flags: DropFlags::default() };
let (locals_ptr, stack_size) = {
let mut stack_ptr = self.stack.len();
let addr = body
@@ -882,7 +844,17 @@ impl Evaluator<'_> {
}
Owned(r)
}
- Rvalue::Len(_) => not_supported!("rvalue len"),
+ Rvalue::Len(p) => {
+ let (_, _, metadata) = self.place_addr_and_ty_and_metadata(p, locals)?;
+ match metadata {
+ Some(m) => m,
+ None => {
+ return Err(MirEvalError::TypeError(
+ "type without metadata is used for Rvalue::Len",
+ ));
+ }
+ }
+ }
Rvalue::UnaryOp(op, val) => {
let mut c = self.eval_operand(val, locals)?.get(&self)?;
let mut ty = self.operand_ty(val, locals)?;
@@ -1080,7 +1052,7 @@ impl Evaluator<'_> {
}
return Ok(Owned(0u128.to_le_bytes().to_vec()));
};
- match layout.variants {
+ match &layout.variants {
Variants::Single { index } => {
let r = self.const_eval_discriminant(EnumVariantId {
parent: enum_id,
@@ -1102,14 +1074,14 @@ impl Evaluator<'_> {
TagEncoding::Niche { untagged_variant, niche_start, .. } => {
let tag = &bytes[offset..offset + size];
let candidate_tag = i128::from_le_bytes(pad16(tag, false))
- .wrapping_sub(niche_start as i128)
+ .wrapping_sub(*niche_start as i128)
as usize;
let variant = variants
.iter_enumerated()
.map(|(x, _)| x)
- .filter(|x| *x != untagged_variant)
+ .filter(|x| x != untagged_variant)
.nth(candidate_tag)
- .unwrap_or(untagged_variant)
+ .unwrap_or(*untagged_variant)
.0;
let result = self.const_eval_discriminant(EnumVariantId {
parent: enum_id,
@@ -1122,7 +1094,7 @@ impl Evaluator<'_> {
}
}
Rvalue::Repeat(x, len) => {
- let len = match try_const_usize(self.db, len) {
+ let len = match try_const_usize(self.db, &len) {
Some(x) => x as usize,
None => not_supported!("non evaluatable array len in repeat Rvalue"),
};
@@ -1154,7 +1126,7 @@ impl Evaluator<'_> {
Owned(r)
}
AggregateKind::Tuple(ty) => {
- let layout = self.layout_filled(&ty, locals)?;
+ let layout = self.layout(&ty)?;
Owned(self.make_by_layout(
layout.size.bytes_usize(),
&layout,
@@ -1174,9 +1146,8 @@ impl Evaluator<'_> {
Owned(result)
}
AggregateKind::Adt(x, subst) => {
- let subst = self.subst_filler(subst, locals);
let (size, variant_layout, tag) =
- self.layout_of_variant(*x, subst, locals)?;
+ self.layout_of_variant(*x, subst.clone(), locals)?;
Owned(self.make_by_layout(
size,
&variant_layout,
@@ -1185,7 +1156,7 @@ impl Evaluator<'_> {
)?)
}
AggregateKind::Closure(ty) => {
- let layout = self.layout_filled(&ty, locals)?;
+ let layout = self.layout(&ty)?;
Owned(self.make_by_layout(
layout.size.bytes_usize(),
&layout,
@@ -1220,7 +1191,10 @@ impl Evaluator<'_> {
// This is no-op
Borrowed(self.eval_operand(operand, locals)?)
}
- x => not_supported!("pointer cast {x:?}"),
+ PointerCast::ArrayToPointer => {
+ // We should remove the metadata part if the current type is slice
+ Borrowed(self.eval_operand(operand, locals)?.slice(0..self.ptr_size()))
+ }
},
CastKind::DynStar => not_supported!("dyn star cast"),
CastKind::IntToInt
@@ -1235,12 +1209,6 @@ impl Evaluator<'_> {
CastKind::FloatToInt => not_supported!("float to int cast"),
CastKind::FloatToFloat => not_supported!("float to float cast"),
CastKind::IntToFloat => not_supported!("float to int cast"),
- CastKind::PtrToPtr => {
- let current = pad16(self.eval_operand(operand, locals)?.get(&self)?, false);
- let dest_size =
- self.size_of_sized(target_ty, locals, "destination of ptr to ptr cast")?;
- Owned(current[0..dest_size].to_vec())
- }
CastKind::FnPtrToPtr => not_supported!("fn ptr to ptr cast"),
},
})
@@ -1300,8 +1268,8 @@ impl Evaluator<'_> {
r.extend(len.to_le_bytes().into_iter());
Owned(r)
}
- _ => {
- not_supported!("slice unsizing from non arrays")
+ t => {
+ not_supported!("slice unsizing from non array type {t:?}")
}
},
}
@@ -1327,7 +1295,7 @@ impl Evaluator<'_> {
x: VariantId,
subst: Substitution,
locals: &Locals<'_>,
- ) -> Result<(usize, Layout, Option<(usize, usize, i128)>)> {
+ ) -> Result<(usize, Arc<Layout>, Option<(usize, usize, i128)>)> {
let adt = x.adt_id();
if let DefWithBodyId::VariantId(f) = locals.body.owner {
if let VariantId::EnumVariantId(x) = x {
@@ -1340,7 +1308,7 @@ impl Evaluator<'_> {
}
}
let layout = self.layout_adt(adt, subst)?;
- Ok(match layout.variants {
+ Ok(match &layout.variants {
Variants::Single { .. } => (layout.size.bytes_usize(), layout, None),
Variants::Multiple { variants, tag, tag_encoding, .. } => {
let cx = self
@@ -1357,22 +1325,22 @@ impl Evaluator<'_> {
let have_tag = match tag_encoding {
TagEncoding::Direct => true,
TagEncoding::Niche { untagged_variant, niche_variants: _, niche_start } => {
- if untagged_variant == rustc_enum_variant_idx {
+ if *untagged_variant == rustc_enum_variant_idx {
false
} else {
discriminant = (variants
.iter_enumerated()
- .filter(|(x, _)| *x != untagged_variant)
+ .filter(|(x, _)| x != untagged_variant)
.position(|(x, _)| x == rustc_enum_variant_idx)
.unwrap() as i128)
- .wrapping_add(niche_start as i128);
+ .wrapping_add(*niche_start as i128);
true
}
}
};
(
layout.size.bytes_usize(),
- variant_layout,
+ Arc::new(variant_layout),
if have_tag {
Some((
layout.fields.offset(0).bytes_usize(),
@@ -1419,15 +1387,7 @@ impl Evaluator<'_> {
Operand::Constant(konst) => {
let data = &konst.data(Interner);
match &data.value {
- chalk_ir::ConstValue::BoundVar(b) => {
- let c = locals
- .subst
- .as_slice(Interner)
- .get(b.index)
- .ok_or(MirEvalError::TypeError("missing generic arg"))?
- .assert_const_ref(Interner);
- self.eval_operand(&Operand::Constant(c.clone()), locals)?
- }
+ chalk_ir::ConstValue::BoundVar(_) => not_supported!("bound var constant"),
chalk_ir::ConstValue::InferenceVar(_) => {
not_supported!("inference var constant")
}
@@ -1471,29 +1431,8 @@ impl Evaluator<'_> {
self.patch_addresses(&patch_map, &memory_map.vtable, addr, ty, locals)?;
Interval::new(addr, size)
}
- ConstScalar::UnevaluatedConst(const_id, subst) => {
- let mut const_id = *const_id;
- let mut subst = self.subst_filler(subst, locals);
- if let GeneralConstId::ConstId(c) = const_id {
- let (c, s) = lookup_impl_const(
- self.db,
- self.db.trait_environment_for_body(locals.body.owner),
- c,
- subst,
- );
- const_id = GeneralConstId::ConstId(c);
- subst = s;
- }
- let c = self.db.const_eval(const_id.into(), subst).map_err(|e| {
- let name = const_id.name(self.db.upcast());
- MirEvalError::ConstEvalError(name, 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::UnevaluatedConst(..) => {
+ not_supported!("unevaluated const present in monomorphized mir");
}
ConstScalar::Unknown => not_supported!("evaluating unknown const"),
})
@@ -1555,7 +1494,7 @@ impl Evaluator<'_> {
}
}
}
- let layout = self.layout_filled(ty, locals);
+ let layout = self.layout(ty);
if self.assert_placeholder_ty_is_unused {
if matches!(layout, Err(MirEvalError::LayoutError(LayoutError::HasPlaceholder, _))) {
return Ok(Some((0, 1)));
@@ -1576,122 +1515,12 @@ impl Evaluator<'_> {
}
}
- /// Uses `ty_filler` to fill an entire subst
- fn subst_filler(&self, subst: &Substitution, locals: &Locals<'_>) -> Substitution {
- Substitution::from_iter(
- Interner,
- subst.iter(Interner).map(|x| match x.data(Interner) {
- chalk_ir::GenericArgData::Ty(ty) => {
- let Ok(ty) = self.ty_filler(ty, locals.subst, locals.body.owner) else {
- return x.clone();
- };
- chalk_ir::GenericArgData::Ty(ty).intern(Interner)
- }
- _ => x.clone(),
- }),
- )
- }
-
- /// This function substitutes placeholders of the body with the provided subst, effectively plays
- /// the rule of monomorphization. In addition to placeholders, it substitutes opaque types (return
- /// position impl traits) with their underlying type.
- fn ty_filler(&self, ty: &Ty, subst: &Substitution, owner: DefWithBodyId) -> Result<Ty> {
- struct Filler<'a> {
- db: &'a dyn HirDatabase,
- subst: &'a Substitution,
- generics: Option<Generics>,
- }
- impl FallibleTypeFolder<Interner> for Filler<'_> {
- type Error = MirEvalError;
-
- fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder<Interner, Error = Self::Error> {
- self
- }
-
- fn interner(&self) -> Interner {
- Interner
- }
-
- fn try_fold_ty(
- &mut self,
- ty: Ty,
- outer_binder: DebruijnIndex,
- ) -> std::result::Result<Ty, Self::Error> {
- match ty.kind(Interner) {
- TyKind::AssociatedType(id, subst) => {
- // I don't know exactly if and why this is needed, but it looks like `normalize_ty` likes
- // this kind of associated types.
- Ok(TyKind::Alias(chalk_ir::AliasTy::Projection(ProjectionTy {
- associated_ty_id: *id,
- substitution: subst.clone().try_fold_with(self, outer_binder)?,
- }))
- .intern(Interner))
- }
- TyKind::OpaqueType(id, subst) => {
- let impl_trait_id = self.db.lookup_intern_impl_trait_id((*id).into());
- let subst = subst.clone().try_fold_with(self.as_dyn(), outer_binder)?;
- match impl_trait_id {
- crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => {
- let infer = self.db.infer(func.into());
- let filler = &mut Filler {
- db: self.db,
- subst: &subst,
- generics: Some(generics(self.db.upcast(), func.into())),
- };
- filler.try_fold_ty(infer.type_of_rpit[idx].clone(), outer_binder)
- }
- crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {
- not_supported!("async block impl trait");
- }
- }
- }
- _ => ty.try_super_fold_with(self.as_dyn(), outer_binder),
- }
- }
-
- fn try_fold_free_placeholder_ty(
- &mut self,
- idx: chalk_ir::PlaceholderIndex,
- _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(idx)
- .and_then(|x| x.ty(Interner))
- .ok_or_else(|| MirEvalError::GenericArgNotProvided(x, self.subst.clone()))?
- .clone())
- }
- }
- 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, size: usize, _align: usize) -> Address {
let pos = self.heap.len();
self.heap.extend(iter::repeat(0).take(size));
Address::Heap(pos)
}
- pub fn interpret_mir_with_no_arg(&mut self, body: &MirBody) -> Result<Vec<u8>> {
- self.interpret_mir(&body, vec![].into_iter(), Substitution::empty(Interner))
- }
-
fn detect_fn_trait(&self, def: FunctionId) -> Option<FnTrait> {
use LangItem::*;
let ItemContainerId::TraitId(parent) = self.db.lookup_intern_function(def).container else {
@@ -1849,21 +1678,24 @@ impl Evaluator<'_> {
) -> Result<()> {
let mir_body = self
.db
- .mir_body_for_closure(closure)
+ .monomorphized_mir_body_for_closure(
+ closure,
+ generic_args.clone(),
+ self.trait_env.clone(),
+ )
.map_err(|x| MirEvalError::MirLowerErrorForClosure(closure, x))?;
- let arg_bytes = iter::once(Ok(closure_data.get(self)?.to_owned()))
+ let closure_data = if mir_body.locals[mir_body.param_locals[0]].ty.as_reference().is_some()
+ {
+ closure_data.addr.to_bytes()
+ } else {
+ closure_data.get(self)?.to_owned()
+ };
+ let arg_bytes = iter::once(Ok(closure_data))
.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())
- .map_err(|e| {
- MirEvalError::InFunction(
- Either::Right(closure),
- Box::new(e),
- span,
- locals.body.owner,
- )
- })?;
+ let bytes = self.interpret_mir(&mir_body, arg_bytes.into_iter()).map_err(|e| {
+ MirEvalError::InFunction(Either::Right(closure), Box::new(e), span, locals.body.owner)
+ })?;
destination.write_from_bytes(self, &bytes)
}
@@ -1877,7 +1709,7 @@ impl Evaluator<'_> {
span: MirSpan,
) -> Result<()> {
let def: CallableDefId = from_chalk(self.db, def);
- let generic_args = self.subst_filler(generic_args, &locals);
+ let generic_args = generic_args.clone();
match def {
CallableDefId::FunctionId(def) => {
if let Some(_) = self.detect_fn_trait(def) {
@@ -1982,14 +1814,14 @@ impl Evaluator<'_> {
span: MirSpan,
destination: Interval,
) -> Result<()> {
- 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)
- })?;
+ let mir_body = self
+ .db
+ .monomorphized_mir_body(def, generic_args, self.trait_env.clone())
+ .map_err(|e| MirEvalError::MirLowerError(imp, e))?;
+ let result = self.interpret_mir(&mir_body, arg_bytes.iter().cloned()).map_err(|e| {
+ MirEvalError::InFunction(Either::Left(imp), Box::new(e), span, locals.body.owner)
+ })?;
destination.write_from_bytes(self, &result)?;
Ok(())
}