//! Compute the binary representation of a type use base_db::CrateId; use chalk_ir::{AdtId, TyKind}; use hir_def::{ layout::{ Abi, FieldsShape, Integer, Layout, LayoutCalculator, LayoutError, Primitive, ReprOptions, RustcEnumVariantIdx, Scalar, Size, StructKind, TargetDataLayout, Variants, WrappingRange, }, LocalFieldId, }; use stdx::never; use crate::{consteval::try_const_usize, db::HirDatabase, Interner, Substitution, Ty}; use self::adt::struct_variant_idx; pub use self::{ adt::{layout_of_adt_query, layout_of_adt_recover}, target::target_data_layout_query, }; macro_rules! user_error { ($x: expr) => { return Err(LayoutError::UserError(format!($x))) }; } mod adt; mod target; struct LayoutCx<'a> { krate: CrateId, target: &'a TargetDataLayout, } impl<'a> LayoutCalculator for LayoutCx<'a> { type TargetDataLayoutRef = &'a TargetDataLayout; fn delay_bug(&self, txt: &str) { never!("{}", txt); } fn current_data_layout(&self) -> &'a TargetDataLayout { self.target } } fn scalar_unit(dl: &TargetDataLayout, value: Primitive) -> Scalar { Scalar::Initialized { value, valid_range: WrappingRange::full(value.size(dl)) } } fn scalar(dl: &TargetDataLayout, value: Primitive) -> Layout { Layout::scalar(dl, scalar_unit(dl, value)) } pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result { let Some(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable) }; let cx = LayoutCx { krate, target: &target }; let dl = &*cx.current_data_layout(); Ok(match ty.kind(Interner) { TyKind::Adt(AdtId(def), subst) => db.layout_of_adt(*def, subst.clone())?, TyKind::Scalar(s) => match s { chalk_ir::Scalar::Bool => Layout::scalar( dl, Scalar::Initialized { value: Primitive::Int(Integer::I8, false), valid_range: WrappingRange { start: 0, end: 1 }, }, ), chalk_ir::Scalar::Char => Layout::scalar( dl, Scalar::Initialized { value: Primitive::Int(Integer::I32, false), valid_range: WrappingRange { start: 0, end: 0x10FFFF }, }, ), chalk_ir::Scalar::Int(i) => scalar( dl, Primitive::Int( match i { chalk_ir::IntTy::Isize => dl.ptr_sized_integer(), chalk_ir::IntTy::I8 => Integer::I8, chalk_ir::IntTy::I16 => Integer::I16, chalk_ir::IntTy::I32 => Integer::I32, chalk_ir::IntTy::I64 => Integer::I64, chalk_ir::IntTy::I128 => Integer::I128, }, true, ), ), chalk_ir::Scalar::Uint(i) => scalar( dl, Primitive::Int( match i { chalk_ir::UintTy::Usize => dl.ptr_sized_integer(), chalk_ir::UintTy::U8 => Integer::I8, chalk_ir::UintTy::U16 => Integer::I16, chalk_ir::UintTy::U32 => Integer::I32, chalk_ir::UintTy::U64 => Integer::I64, chalk_ir::UintTy::U128 => Integer::I128, }, false, ), ), chalk_ir::Scalar::Float(f) => scalar( dl, match f { chalk_ir::FloatTy::F32 => Primitive::F32, chalk_ir::FloatTy::F64 => Primitive::F64, }, ), }, TyKind::Tuple(len, tys) => { let kind = if *len == 0 { StructKind::AlwaysSized } else { StructKind::MaybeUnsized }; let fields = tys .iter(Interner) .map(|k| layout_of_ty(db, k.assert_ty_ref(Interner), krate)) .collect::, _>>()?; let fields = fields.iter().collect::>(); let fields = fields.iter().collect::>(); cx.univariant(dl, &fields, &ReprOptions::default(), kind).ok_or(LayoutError::Unknown)? } TyKind::Array(element, count) => { let count = try_const_usize(&count).ok_or(LayoutError::UserError( "mismatched type of const generic parameter".to_string(), ))? as u64; let element = layout_of_ty(db, element, krate)?; let size = element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow)?; let abi = if count != 0 && matches!(element.abi, Abi::Uninhabited) { Abi::Uninhabited } else { Abi::Aggregate { sized: true } }; let largest_niche = if count != 0 { element.largest_niche } else { None }; Layout { variants: Variants::Single { index: struct_variant_idx() }, fields: FieldsShape::Array { stride: element.size, count }, abi, largest_niche, align: element.align, size, } } TyKind::Slice(element) => { let element = layout_of_ty(db, element, krate)?; Layout { variants: Variants::Single { index: struct_variant_idx() }, fields: FieldsShape::Array { stride: element.size, count: 0 }, abi: Abi::Aggregate { sized: false }, largest_niche: None, align: element.align, size: Size::ZERO, } } // Potentially-wide pointers. TyKind::Ref(_, _, pointee) | TyKind::Raw(_, pointee) => { let mut data_ptr = scalar_unit(dl, Primitive::Pointer); if matches!(ty.kind(Interner), TyKind::Ref(..)) { data_ptr.valid_range_mut().start = 1; } // let pointee = tcx.normalize_erasing_regions(param_env, pointee); // if pointee.is_sized(tcx.at(DUMMY_SP), param_env) { // return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr))); // } let unsized_part = struct_tail_erasing_lifetimes(db, pointee.clone()); let metadata = match unsized_part.kind(Interner) { TyKind::Slice(_) | TyKind::Str => { scalar_unit(dl, Primitive::Int(dl.ptr_sized_integer(), false)) } TyKind::Dyn(..) => { let mut vtable = scalar_unit(dl, Primitive::Pointer); vtable.valid_range_mut().start = 1; vtable } _ => { // pointee is sized return Ok(Layout::scalar(dl, data_ptr)); } }; // Effectively a (ptr, meta) tuple. cx.scalar_pair(data_ptr, metadata) } TyKind::FnDef(_, _) => layout_of_unit(&cx, dl)?, TyKind::Str => Layout { variants: Variants::Single { index: struct_variant_idx() }, fields: FieldsShape::Array { stride: Size::from_bytes(1), count: 0 }, abi: Abi::Aggregate { sized: false }, largest_niche: None, align: dl.i8_align, size: Size::ZERO, }, TyKind::Never => Layout { variants: Variants::Single { index: struct_variant_idx() }, fields: FieldsShape::Primitive, abi: Abi::Uninhabited, largest_niche: None, align: dl.i8_align, size: Size::ZERO, }, TyKind::Dyn(_) | TyKind::Foreign(_) => { let mut unit = layout_of_unit(&cx, dl)?; match unit.abi { Abi::Aggregate { ref mut sized } => *sized = false, _ => user_error!("bug"), } unit } TyKind::Function(_) => { let mut ptr = scalar_unit(dl, Primitive::Pointer); ptr.valid_range_mut().start = 1; Layout::scalar(dl, ptr) } TyKind::OpaqueType(opaque_ty_id, _) => { let impl_trait_id = db.lookup_intern_impl_trait_id((*opaque_ty_id).into()); match impl_trait_id { crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { let infer = db.infer(func.into()); layout_of_ty(db, &infer.type_of_rpit[idx], krate)? } crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { return Err(LayoutError::NotImplemented) } } } TyKind::Closure(_, _) | TyKind::Generator(_, _) | TyKind::GeneratorWitness(_, _) => { return Err(LayoutError::NotImplemented) } TyKind::AssociatedType(_, _) | TyKind::Error | TyKind::Alias(_) | TyKind::Placeholder(_) | TyKind::BoundVar(_) | TyKind::InferenceVar(_, _) => return Err(LayoutError::HasPlaceholder), }) } fn layout_of_unit(cx: &LayoutCx<'_>, dl: &TargetDataLayout) -> Result { cx.univariant::( dl, &[], &ReprOptions::default(), StructKind::AlwaysSized, ) .ok_or(LayoutError::Unknown) } fn struct_tail_erasing_lifetimes(db: &dyn HirDatabase, pointee: Ty) -> Ty { match pointee.kind(Interner) { TyKind::Adt(AdtId(hir_def::AdtId::StructId(i)), subst) => { let data = db.struct_data(*i); let mut it = data.variant_data.fields().iter().rev(); match it.next() { Some((f, _)) => field_ty(db, (*i).into(), f, subst), None => pointee, } } _ => pointee, } } fn field_ty( db: &dyn HirDatabase, def: hir_def::VariantId, fd: LocalFieldId, subst: &Substitution, ) -> Ty { db.field_types(def)[fd].clone().substitute(Interner, subst) } #[cfg(test)] mod tests;