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 | 243 |
1 files changed, 187 insertions, 56 deletions
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 505db1776f..80e429c4c8 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -21,7 +21,7 @@ use hir_expand::{InFile, mod_path::path, name::Name}; use intern::sym; use la_arena::ArenaMap; use macros::GenericTypeVisitable; -use rustc_abi::TargetDataLayout; +use rustc_abi::{Size, TargetDataLayout}; use rustc_apfloat::{ Float, ieee::{Half as f16, Quad as f128}, @@ -40,14 +40,14 @@ use triomphe::Arc; use crate::{ CallableDefId, ComplexMemoryMap, InferenceResult, MemoryMap, ParamEnvAndCrate, consteval::{self, ConstEvalError, try_const_usize}, - db::{HirDatabase, InternedClosure, InternedClosureId}, + db::{HirDatabase, InternedClosureId}, display::{ClosureStyle, DisplayTarget, HirDisplay}, infer::PointerCast, layout::{Layout, LayoutError, RustcEnumVariantIdx}, method_resolution::{is_dyn_method, lookup_impl_const}, next_solver::{ - Const, ConstBytes, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs, Region, - StoredConst, StoredTy, Ty, TyKind, TypingMode, UnevaluatedConst, ValueConst, + AliasTy, Allocation, AllocationData, Const, ConstKind, DbInterner, ErrorGuaranteed, + GenericArgs, Region, StoredTy, Ty, TyKind, TypingMode, UnevaluatedConst, ValTree, infer::{DbInternerInferExt, InferCtxt, traits::ObligationCause}, obligation_ctxt::ObligationCtxt, }, @@ -359,7 +359,7 @@ pub enum MirEvalError { MirLowerErrorForClosure(InternedClosureId, MirLowerError), TypeIsUnsized(StoredTy, &'static str), NotSupported(String), - InvalidConst(StoredConst), + InvalidConst, InFunction( Box<MirEvalError>, Vec<(Either<FunctionId, InternedClosureId>, MirSpan, DefWithBodyId)>, @@ -484,7 +484,7 @@ impl MirEvalError { | MirEvalError::MirLowerErrorForClosure(_, _) | MirEvalError::TypeIsUnsized(_, _) | MirEvalError::NotSupported(_) - | MirEvalError::InvalidConst(_) + | MirEvalError::InvalidConst | MirEvalError::ExecutionLimitExceeded | MirEvalError::StackOverflow | MirEvalError::CoerceUnsizedError(_) @@ -537,7 +537,7 @@ impl std::fmt::Debug for MirEvalError { Self::InternalError(arg0) => f.debug_tuple("InternalError").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) => f.debug_tuple("InvalidConst").field(&arg0).finish(), + Self::InvalidConst => f.write_str("InvalidConst"), Self::InFunction(e, stack) => { f.debug_struct("WithStack").field("error", e).field("stack", &stack).finish() } @@ -606,10 +606,10 @@ pub fn interpret_mir<'db>( // (and probably should) do better here, for example by excluding bindings outside of the target expression. assert_placeholder_ty_is_unused: bool, trait_env: Option<ParamEnvAndCrate<'db>>, -) -> Result<'db, (Result<'db, Const<'db>>, MirOutput)> { +) -> Result<'db, (Result<'db, Allocation<'db>>, MirOutput)> { let ty = body.locals[return_slot()].ty.as_ref(); let mut evaluator = Evaluator::new(db, body.owner, assert_placeholder_ty_is_unused, trait_env)?; - let it: Result<'db, Const<'db>> = (|| { + let it: Result<'db, Allocation<'db>> = (|| { if evaluator.ptr_size() != size_of::<usize>() { not_supported!("targets with different pointer size from host"); } @@ -620,7 +620,7 @@ pub fn interpret_mir<'db>( ty, &Locals { ptr: ArenaMap::new(), body, drop_flags: DropFlags::default() }, )?; - let bytes = bytes.into(); + let bytes = Box::from(bytes); let memory_map = if memory_map.memory.is_empty() && evaluator.vtable_map.is_empty() { MemoryMap::Empty } else { @@ -628,7 +628,7 @@ pub fn interpret_mir<'db>( memory_map.vtable.shrink_to_fit(); MemoryMap::Complex(Box::new(memory_map)) }; - Ok(Const::new_valtree(evaluator.interner(), ty, bytes, memory_map)) + Ok(Allocation::new(AllocationData { ty, memory: bytes, memory_map })) })(); Ok((it, MirOutput { stdout: evaluator.stdout, stderr: evaluator.stderr })) } @@ -731,24 +731,7 @@ impl<'db> Evaluator<'db> { return *r; } let (ty, proj) = pair; - let r = proj.projected_ty( - &self.infcx, - self.param_env.param_env, - ty, - |c, subst, f| { - let InternedClosure(owner, _) = self.db.lookup_intern_closure(c); - let infer = InferenceResult::of(self.db, owner); - let (captures, _) = infer.closure_info(c); - let parent_subst = subst.as_closure().parent_args(); - captures - .get(f) - .expect("broken closure field") - .ty - .get() - .instantiate(self.interner(), parent_subst) - }, - self.crate_id, - ); + let r = proj.projected_ty(&self.infcx, self.param_env.param_env, ty, self.crate_id); self.projected_ty_cache.borrow_mut().insert((ty, proj), r); r } @@ -898,6 +881,7 @@ impl<'db> Evaluator<'db> { Ok(match &o.kind { OperandKind::Copy(p) | OperandKind::Move(p) => self.place_ty(p, locals)?, OperandKind::Constant { konst: _, ty } => ty.as_ref(), + OperandKind::Allocation { allocation } => allocation.as_ref().ty, &OperandKind::Static(s) => { let ty = InferenceResult::of(self.db, DefWithBodyId::from(s)) .expr_ty(Body::of(self.db, s.into()).root_expr()); @@ -1927,19 +1911,152 @@ impl<'db> Evaluator<'db> { OperandKind::Constant { konst, .. } => { self.allocate_const_in_heap(locals, konst.as_ref())? } + OperandKind::Allocation { allocation } => { + self.allocate_allocation_in_heap(locals, allocation.as_ref())? + } }) } + fn allocate_valtree_in_heap( + &mut self, + ty: Ty<'db>, + valtree: ValTree<'db>, + ) -> Result<'db, Interval> { + match ty.kind() { + TyKind::Bool => { + let value = valtree.inner().to_leaf().try_to_bool().unwrap(); + let addr = self.heap_allocate(1, 1)?; + self.write_memory(addr, &[u8::from(value)])?; + Ok(Interval::new(addr, 1)) + } + TyKind::Char => { + let value = valtree.inner().to_leaf().to_u32(); + let addr = self.heap_allocate(4, 4)?; + self.write_memory(addr, &value.to_le_bytes())?; + Ok(Interval::new(addr, 4)) + } + TyKind::Int(int_ty) => { + let size = int_ty.bit_width().unwrap_or(self.ptr_size() as u64); + let value = valtree.inner().to_leaf().to_int(Size::from_bytes(size)); + let addr = self.heap_allocate(size as usize, size as usize)?; + self.write_memory(addr, &value.to_le_bytes()[..size as usize])?; + Ok(Interval::new(addr, size as usize)) + } + TyKind::Uint(uint_ty) => { + let size = uint_ty.bit_width().unwrap_or(self.ptr_size() as u64); + let value = valtree.inner().to_leaf().to_uint(Size::from_bytes(size)); + let addr = self.heap_allocate(size as usize, size as usize)?; + self.write_memory(addr, &value.to_le_bytes()[..size as usize])?; + Ok(Interval::new(addr, size as usize)) + } + TyKind::Float(float_ty) => { + let size = float_ty.bit_width(); + let value = valtree.inner().to_leaf().to_uint(Size::from_bytes(size)); + let addr = self.heap_allocate(size as usize, size as usize)?; + self.write_memory(addr, &value.to_le_bytes()[..size as usize])?; + Ok(Interval::new(addr, size as usize)) + } + TyKind::RawPtr(..) => { + let size = self.ptr_size(); + let value = valtree.inner().to_leaf().to_uint(Size::from_bytes(size)); + let addr = self.heap_allocate(size, size)?; + self.write_memory(addr, &value.to_le_bytes()[..size])?; + Ok(Interval::new(addr, size)) + } + TyKind::Ref(_, inner_ty, _) => match inner_ty.kind() { + TyKind::Str => { + let bytes = valtree + .inner() + .to_branch() + .iter() + .map(|konst| match konst.kind() { + ConstKind::Value(value) => Ok(value.value.inner().to_leaf().to_u8()), + _ => not_supported!("unsupported const"), + }) + .collect::<Result<'_, Vec<_>>>()?; + let bytes_addr = self.heap_allocate(bytes.len(), 1)?; + self.write_memory(bytes_addr, &bytes)?; + let ref_addr = self.heap_allocate(self.ptr_size() * 2, self.ptr_size())?; + self.write_memory(ref_addr, &bytes_addr.to_bytes())?; + let mut len = [0; 16]; + len[..size_of::<usize>()].copy_from_slice(&bytes.len().to_le_bytes()); + self.write_memory(ref_addr.offset(self.ptr_size()), &len[..self.ptr_size()])?; + Ok(Interval::new(ref_addr, self.ptr_size() * 2)) + } + TyKind::Slice(inner_ty) => { + let item_layout = self.layout(inner_ty)?; + let items = valtree + .inner() + .to_branch() + .iter() + .map(|konst| match konst.kind() { + ConstKind::Value(value) => { + self.allocate_valtree_in_heap(value.ty, value.value) + } + _ => not_supported!("unsupported const"), + }) + .collect::<Result<'_, Vec<_>>>()?; + let items_addr = self.heap_allocate( + items.len() * (item_layout.size.bits() as usize), + item_layout.align.bits_usize(), + )?; + for (i, item) in items.iter().enumerate() { + self.copy_from_interval( + items_addr.offset(i * (item_layout.size.bits() as usize)), + *item, + )?; + } + let ref_addr = self.heap_allocate(self.ptr_size() * 2, self.ptr_size())?; + self.write_memory(ref_addr, &items_addr.to_bytes())?; + let mut len = [0; 16]; + len[..size_of::<usize>()].copy_from_slice(&items.len().to_le_bytes()); + self.write_memory(ref_addr.offset(self.ptr_size()), &len[..self.ptr_size()])?; + Ok(Interval::new(ref_addr, self.ptr_size() * 2)) + } + TyKind::Dynamic(..) => not_supported!("`dyn Trait` consts not supported yet"), + _ => { + let inner_addr = self.allocate_valtree_in_heap(inner_ty, valtree)?; + let ref_addr = self.heap_allocate(self.ptr_size(), self.ptr_size())?; + self.write_memory(ref_addr, &inner_addr.addr.to_bytes())?; + Ok(Interval::new(ref_addr, self.ptr_size())) + } + }, + TyKind::Adt(_, _) | TyKind::Array(_, _) | TyKind::Tuple(_) => { + not_supported!( + "ADTs, arrays and tuples are unsupported in consts currently (requires `adt_const_params`)" + ) + } + TyKind::Pat(_, _) + | TyKind::Slice(_) + | TyKind::FnDef(_, _) + | TyKind::Foreign(_) + | TyKind::Dynamic(_, _) + | TyKind::UnsafeBinder(..) + | TyKind::FnPtr(..) + | TyKind::Closure(_, _) + | TyKind::CoroutineClosure(_, _) + | TyKind::Coroutine(_, _) + | TyKind::CoroutineWitness(_, _) + | TyKind::Never + | TyKind::Alias(..) + | TyKind::Param(_) + | TyKind::Bound(..) + | TyKind::Placeholder(_) + | TyKind::Infer(_) + | TyKind::Str + | TyKind::Error(_) => not_supported!("unsupported const"), + } + } + #[allow(clippy::double_parens)] fn allocate_const_in_heap( &mut self, locals: &Locals, konst: Const<'db>, ) -> Result<'db, Interval> { - let result_owner; - let value = match konst.kind() { - ConstKind::Value(value) => value, - ConstKind::Unevaluated(UnevaluatedConst { def: const_id, args: subst }) => 'b: { + match konst.kind() { + ConstKind::Value(value) => self.allocate_valtree_in_heap(value.ty, value.value), + ConstKind::Unevaluated(UnevaluatedConst { def: const_id, args: subst }) => { let mut id = const_id.0; let mut subst = subst; if let hir_def::GeneralConstId::ConstId(c) = id { @@ -1947,7 +2064,7 @@ impl<'db> Evaluator<'db> { id = hir_def::GeneralConstId::ConstId(c); subst = s; } - result_owner = match id { + let allocation = match id { GeneralConstId::ConstId(const_id) => { self.db.const_eval(const_id, subst, Some(self.param_env)).map_err(|e| { let name = id.name(self.db); @@ -1964,21 +2081,24 @@ impl<'db> Evaluator<'db> { not_supported!("anonymous const evaluation") } }; - if let ConstKind::Value(value) = result_owner.kind() { - break 'b value; - } - not_supported!("unevaluatable constant"); + self.allocate_allocation_in_heap(locals, allocation) } _ => not_supported!("evaluating unknown const"), - }; - let ValueConst { ty, value } = value; - let ConstBytes { memory: v, memory_map } = value.inner(); + } + } + + fn allocate_allocation_in_heap( + &mut self, + locals: &Locals, + allocation: Allocation<'db>, + ) -> Result<'db, Interval> { + let AllocationData { ty, memory: ref v, ref memory_map } = *allocation; let patch_map = memory_map.transform_addresses(|b, align| { let addr = self.heap_allocate(b.len(), align)?; self.write_memory(addr, b)?; Ok(addr.to_usize()) })?; - let (size, align) = self.size_align_of(ty, locals)?.unwrap_or((v.len(), 1)); + let (size, align) = self.size_align_of(allocation.ty, locals)?.unwrap_or((v.len(), 1)); let v: Cow<'_, [u8]> = if size != v.len() { // Handle self enum if size == 16 && v.len() < 16 { @@ -1986,7 +2106,7 @@ impl<'db> Evaluator<'db> { } else if size < 16 && v.len() == 16 { Cow::Borrowed(&v[0..size]) } else { - return Err(MirEvalError::InvalidConst(konst.store())); + return Err(MirEvalError::InvalidConst); } } else { Cow::Borrowed(v) @@ -2211,10 +2331,10 @@ impl<'db> Evaluator<'db> { match size { Some((size, _)) => { let addr_usize = from_bytes!(usize, bytes); - mm.insert( - addr_usize, - this.read_memory(Address::from_usize(addr_usize), size)?.into(), - ) + let bytes = + this.read_memory(Address::from_usize(addr_usize), size)?.to_vec(); + mm.insert(addr_usize, bytes.clone().into()); + rec(this, &bytes, t, locals, mm, stack_depth_limit - 1)?; } None => { let mut check_inner = None; @@ -2340,7 +2460,7 @@ impl<'db> Evaluator<'db> { } AdtId::UnionId(_) => (), }, - TyKind::Alias(AliasTyKind::Projection, _) => { + TyKind::Alias(AliasTy { kind: AliasTyKind::Projection { .. }, .. }) => { let mut ocx = ObligationCtxt::new(&this.infcx); let ty = ocx .structurally_normalize_ty( @@ -2379,9 +2499,20 @@ impl<'db> Evaluator<'db> { match size { Some(_) => { let current = from_bytes!(usize, self.read_memory(addr, my_size)?); - if let Some(it) = patch_map.get(¤t) { - self.write_memory(addr, &it.to_le_bytes())?; - } + let patched = match patch_map.get(¤t) { + Some(it) => { + self.write_memory(addr, &it.to_le_bytes())?; + *it + } + None => current, + }; + self.patch_addresses( + patch_map, + ty_of_bytes, + Address::from_usize(patched), + t, + locals, + )?; } None => { let current = from_bytes!(usize, self.read_memory(addr, my_size / 2)?); @@ -2472,7 +2603,7 @@ impl<'db> Evaluator<'db> { | TyKind::Error(_) | TyKind::Placeholder(_) | TyKind::Dynamic(_, _) - | TyKind::Alias(_, _) + | TyKind::Alias(..) | TyKind::Bound(_, _) | TyKind::Infer(_) | TyKind::Pat(_, _) @@ -2829,10 +2960,10 @@ impl<'db> Evaluator<'db> { }; let static_data = StaticSignature::of(self.db, st); let result = if !static_data.flags.contains(StaticFlags::EXTERN) { - let konst = self.db.const_eval_static(st).map_err(|e| { + let allocation = self.db.const_eval_static(st).map_err(|e| { MirEvalError::ConstEvalError(static_data.name.as_str().to_owned(), Box::new(e)) })?; - self.allocate_const_in_heap(locals, konst)? + self.allocate_allocation_in_heap(locals, allocation)? } else { let ty = InferenceResult::of(self.db, DefWithBodyId::from(st)) .expr_ty(Body::of(self.db, st.into()).root_expr()); @@ -2992,7 +3123,7 @@ impl<'db> Evaluator<'db> { pub fn render_const_using_debug_impl<'db>( db: &'db dyn HirDatabase, owner: DefWithBodyId, - c: Const<'db>, + c: Allocation<'db>, ty: Ty<'db>, ) -> Result<'db, String> { let mut evaluator = Evaluator::new(db, owner, false, None)?; @@ -3003,7 +3134,7 @@ pub fn render_const_using_debug_impl<'db>( .map_err(|_| MirEvalError::NotSupported("unreachable".to_owned()))?, drop_flags: DropFlags::default(), }; - let data = evaluator.allocate_const_in_heap(locals, c)?; + let data = evaluator.allocate_allocation_in_heap(locals, c)?; let resolver = owner.resolver(db); let Some(TypeNs::TraitId(debug_trait)) = resolver.resolve_path_in_type_ns_fully( db, |