about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock24
-rw-r--r--crates/hir-def/Cargo.toml2
-rw-r--r--crates/hir-def/src/adt.rs95
-rw-r--r--crates/hir-def/src/layout.rs96
-rw-r--r--crates/hir-def/src/lib.rs1
-rw-r--r--crates/hir-expand/src/name.rs1
-rw-r--r--crates/hir-ty/Cargo.toml1
-rw-r--r--crates/hir-ty/src/db.rs16
-rw-r--r--crates/hir-ty/src/diagnostics/match_check.rs13
-rw-r--r--crates/hir-ty/src/infer.rs25
-rw-r--r--crates/hir-ty/src/lang_items.rs20
-rw-r--r--crates/hir-ty/src/layout.rs272
-rw-r--r--crates/hir-ty/src/layout/adt.rs133
-rw-r--r--crates/hir-ty/src/layout/target.rs46
-rw-r--r--crates/hir-ty/src/layout/tests.rs196
-rw-r--r--crates/hir-ty/src/lib.rs2
-rw-r--r--crates/hir/src/lib.rs49
-rw-r--r--crates/ide/src/hover/render.rs47
-rw-r--r--crates/ide/src/hover/tests.rs219
-rw-r--r--crates/test-utils/src/minicore.rs10
-rw-r--r--lib/la-arena/src/map.rs8
21 files changed, 1112 insertions, 164 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 948bf427d45..3d9f0d7cc5b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -510,6 +510,8 @@ dependencies = [
  "fst",
  "hashbrown",
  "hir-expand",
+ "hkalbasi-rustc-ap-rustc_abi",
+ "hkalbasi-rustc-ap-rustc_index",
  "indexmap",
  "itertools",
  "la-arena",
@@ -564,6 +566,7 @@ dependencies = [
  "expect-test",
  "hir-def",
  "hir-expand",
+ "hkalbasi-rustc-ap-rustc_index",
  "itertools",
  "la-arena",
  "limit",
@@ -582,6 +585,27 @@ dependencies = [
 ]
 
 [[package]]
+name = "hkalbasi-rustc-ap-rustc_abi"
+version = "0.0.20221125"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29c8368a30e518c0102d670d8515f7d424d875ee615ec7a7b6d29217b57a0371"
+dependencies = [
+ "bitflags",
+ "hkalbasi-rustc-ap-rustc_index",
+ "tracing",
+]
+
+[[package]]
+name = "hkalbasi-rustc-ap-rustc_index"
+version = "0.0.20221125"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c07bba80d7f6a8e1efb0f3e2115ef1eecbf97292dc8cad84e4982226b9aa12e2"
+dependencies = [
+ "arrayvec",
+ "smallvec",
+]
+
+[[package]]
 name = "home"
 version = "0.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/crates/hir-def/Cargo.toml b/crates/hir-def/Cargo.toml
index 22f98ea7cd4..9ecce46601b 100644
--- a/crates/hir-def/Cargo.toml
+++ b/crates/hir-def/Cargo.toml
@@ -33,6 +33,8 @@ base-db = { path = "../base-db", version = "0.0.0" }
 syntax = { path = "../syntax", version = "0.0.0" }
 profile = { path = "../profile", version = "0.0.0" }
 hir-expand = { path = "../hir-expand", version = "0.0.0" }
+rustc_abi = { version = "0.0.20221125", package = "hkalbasi-rustc-ap-rustc_abi", default-features = false }
+rustc_index = { version = "0.0.20221125", package = "hkalbasi-rustc-ap-rustc_index", default-features = false }
 mbe = { path = "../mbe", version = "0.0.0" }
 cfg = { path = "../cfg", version = "0.0.0" }
 tt = { path = "../tt", version = "0.0.0" }
diff --git a/crates/hir-def/src/adt.rs b/crates/hir-def/src/adt.rs
index e5ab7bf3f6d..db3b4194881 100644
--- a/crates/hir-def/src/adt.rs
+++ b/crates/hir-def/src/adt.rs
@@ -1,6 +1,6 @@
 //! Defines hir-level representation of structs, enums and unions
 
-use std::{num::NonZeroU32, sync::Arc};
+use std::sync::Arc;
 
 use base_db::CrateId;
 use either::Either;
@@ -9,6 +9,7 @@ use hir_expand::{
     HirFileId, InFile,
 };
 use la_arena::{Arena, ArenaMap};
+use rustc_abi::{Integer, IntegerType};
 use syntax::ast::{self, HasName, HasVisibility};
 use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree};
 
@@ -18,6 +19,7 @@ use crate::{
     db::DefDatabase,
     intern::Interned,
     item_tree::{AttrOwner, Field, FieldAstId, Fields, ItemTree, ModItem, RawVisibilityId},
+    layout::{Align, ReprFlags, ReprOptions},
     nameres::diagnostics::DefDiagnostic,
     src::HasChildSource,
     src::HasSource,
@@ -34,7 +36,7 @@ use cfg::CfgOptions;
 pub struct StructData {
     pub name: Name,
     pub variant_data: Arc<VariantData>,
-    pub repr: Option<ReprData>,
+    pub repr: Option<ReprOptions>,
     pub visibility: RawVisibility,
     pub rustc_has_incoherent_inherent_impls: bool,
 }
@@ -43,7 +45,7 @@ pub struct StructData {
 pub struct EnumData {
     pub name: Name,
     pub variants: Arena<EnumVariantData>,
-    pub repr: Option<ReprData>,
+    pub repr: Option<ReprOptions>,
     pub visibility: RawVisibility,
     pub rustc_has_incoherent_inherent_impls: bool,
 }
@@ -69,80 +71,91 @@ pub struct FieldData {
     pub visibility: RawVisibility,
 }
 
-#[derive(Copy, Debug, Clone, PartialEq, Eq)]
-pub enum ReprKind {
-    C,
-    BuiltinInt { builtin: Either<BuiltinInt, BuiltinUint>, is_c: bool },
-    Transparent,
-    Default,
-}
-
-#[derive(Copy, Debug, Clone, PartialEq, Eq)]
-pub struct ReprData {
-    pub kind: ReprKind,
-    pub packed: bool,
-    pub align: Option<NonZeroU32>,
-}
-
 fn repr_from_value(
     db: &dyn DefDatabase,
     krate: CrateId,
     item_tree: &ItemTree,
     of: AttrOwner,
-) -> Option<ReprData> {
+) -> Option<ReprOptions> {
     item_tree.attrs(db, krate, of).by_key("repr").tt_values().find_map(parse_repr_tt)
 }
 
-fn parse_repr_tt(tt: &Subtree) -> Option<ReprData> {
+fn parse_repr_tt(tt: &Subtree) -> Option<ReprOptions> {
     match tt.delimiter {
         Some(Delimiter { kind: DelimiterKind::Parenthesis, .. }) => {}
         _ => return None,
     }
 
-    let mut data = ReprData { kind: ReprKind::Default, packed: false, align: None };
+    let mut flags = ReprFlags::empty();
+    let mut int = None;
+    let mut max_align: Option<Align> = None;
+    let mut min_pack: Option<Align> = None;
 
     let mut tts = tt.token_trees.iter().peekable();
     while let Some(tt) = tts.next() {
         if let TokenTree::Leaf(Leaf::Ident(ident)) = tt {
-            match &*ident.text {
+            flags.insert(match &*ident.text {
                 "packed" => {
-                    data.packed = true;
-                    if let Some(TokenTree::Subtree(_)) = tts.peek() {
+                    let pack = if let Some(TokenTree::Subtree(tt)) = tts.peek() {
                         tts.next();
-                    }
+                        if let Some(TokenTree::Leaf(Leaf::Literal(lit))) = tt.token_trees.first() {
+                            lit.text.parse().unwrap_or_default()
+                        } else {
+                            0
+                        }
+                    } else {
+                        0
+                    };
+                    let pack = Align::from_bytes(pack).unwrap();
+                    min_pack =
+                        Some(if let Some(min_pack) = min_pack { min_pack.min(pack) } else { pack });
+                    ReprFlags::empty()
                 }
                 "align" => {
                     if let Some(TokenTree::Subtree(tt)) = tts.peek() {
                         tts.next();
                         if let Some(TokenTree::Leaf(Leaf::Literal(lit))) = tt.token_trees.first() {
                             if let Ok(align) = lit.text.parse() {
-                                data.align = Some(align);
+                                let align = Align::from_bytes(align).ok();
+                                max_align = max_align.max(align);
                             }
                         }
                     }
+                    ReprFlags::empty()
                 }
-                "C" => {
-                    if let ReprKind::BuiltinInt { is_c, .. } = &mut data.kind {
-                        *is_c = true;
-                    } else {
-                        data.kind = ReprKind::C;
-                    }
-                }
-                "transparent" => data.kind = ReprKind::Transparent,
+                "C" => ReprFlags::IS_C,
+                "transparent" => ReprFlags::IS_TRANSPARENT,
                 repr => {
-                    let is_c = matches!(data.kind, ReprKind::C);
                     if let Some(builtin) = BuiltinInt::from_suffix(repr)
                         .map(Either::Left)
                         .or_else(|| BuiltinUint::from_suffix(repr).map(Either::Right))
                     {
-                        data.kind = ReprKind::BuiltinInt { builtin, is_c };
+                        int = Some(match builtin {
+                            Either::Left(bi) => match bi {
+                                BuiltinInt::Isize => IntegerType::Pointer(true),
+                                BuiltinInt::I8 => IntegerType::Fixed(Integer::I8, true),
+                                BuiltinInt::I16 => IntegerType::Fixed(Integer::I16, true),
+                                BuiltinInt::I32 => IntegerType::Fixed(Integer::I32, true),
+                                BuiltinInt::I64 => IntegerType::Fixed(Integer::I64, true),
+                                BuiltinInt::I128 => IntegerType::Fixed(Integer::I128, true),
+                            },
+                            Either::Right(bu) => match bu {
+                                BuiltinUint::Usize => IntegerType::Pointer(false),
+                                BuiltinUint::U8 => IntegerType::Fixed(Integer::I8, false),
+                                BuiltinUint::U16 => IntegerType::Fixed(Integer::I16, false),
+                                BuiltinUint::U32 => IntegerType::Fixed(Integer::I32, false),
+                                BuiltinUint::U64 => IntegerType::Fixed(Integer::I64, false),
+                                BuiltinUint::U128 => IntegerType::Fixed(Integer::I128, false),
+                            },
+                        });
                     }
+                    ReprFlags::empty()
                 }
-            }
+            })
         }
     }
 
-    Some(data)
+    Some(ReprOptions { int, align: max_align, pack: min_pack, flags, field_shuffle_seed: 0 })
 }
 
 impl StructData {
@@ -299,10 +312,10 @@ impl EnumData {
         Some(id)
     }
 
-    pub fn variant_body_type(&self) -> Either<BuiltinInt, BuiltinUint> {
+    pub fn variant_body_type(&self) -> IntegerType {
         match self.repr {
-            Some(ReprData { kind: ReprKind::BuiltinInt { builtin, .. }, .. }) => builtin,
-            _ => Either::Left(BuiltinInt::Isize),
+            Some(ReprOptions { int: Some(builtin), .. }) => builtin,
+            _ => IntegerType::Pointer(true),
         }
     }
 }
diff --git a/crates/hir-def/src/layout.rs b/crates/hir-def/src/layout.rs
new file mode 100644
index 00000000000..a427c464bc0
--- /dev/null
+++ b/crates/hir-def/src/layout.rs
@@ -0,0 +1,96 @@
+//! Definitions needed for computing data layout of types.
+
+use std::cmp;
+
+use la_arena::{Idx, RawIdx};
+pub use rustc_abi::{
+    Abi, AbiAndPrefAlign, AddressSpace, Align, Endian, FieldsShape, Integer, IntegerType,
+    LayoutCalculator, Niche, Primitive, ReprFlags, ReprOptions, Scalar, Size, StructKind,
+    TargetDataLayout, WrappingRange,
+};
+
+use crate::LocalEnumVariantId;
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct RustcEnumVariantIdx(pub LocalEnumVariantId);
+
+impl rustc_index::vec::Idx for RustcEnumVariantIdx {
+    fn new(idx: usize) -> Self {
+        RustcEnumVariantIdx(Idx::from_raw(RawIdx::from(idx as u32)))
+    }
+
+    fn index(self) -> usize {
+        u32::from(self.0.into_raw()) as usize
+    }
+}
+
+pub type Layout = rustc_abi::LayoutS<RustcEnumVariantIdx>;
+pub type TagEncoding = rustc_abi::TagEncoding<RustcEnumVariantIdx>;
+pub type Variants = rustc_abi::Variants<RustcEnumVariantIdx>;
+
+pub trait IntegerExt {
+    fn repr_discr(
+        dl: &TargetDataLayout,
+        repr: &ReprOptions,
+        min: i128,
+        max: i128,
+    ) -> Result<(Integer, bool), LayoutError>;
+}
+
+impl IntegerExt for Integer {
+    /// Finds the appropriate Integer type and signedness for the given
+    /// signed discriminant range and `#[repr]` attribute.
+    /// N.B.: `u128` values above `i128::MAX` will be treated as signed, but
+    /// that shouldn't affect anything, other than maybe debuginfo.
+    fn repr_discr(
+        dl: &TargetDataLayout,
+        repr: &ReprOptions,
+        min: i128,
+        max: i128,
+    ) -> Result<(Integer, bool), LayoutError> {
+        // Theoretically, negative values could be larger in unsigned representation
+        // than the unsigned representation of the signed minimum. However, if there
+        // are any negative values, the only valid unsigned representation is u128
+        // which can fit all i128 values, so the result remains unaffected.
+        let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u128, max as u128));
+        let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max));
+
+        if let Some(ity) = repr.int {
+            let discr = Integer::from_attr(dl, ity);
+            let fit = if ity.is_signed() { signed_fit } else { unsigned_fit };
+            if discr < fit {
+                return Err(LayoutError::UserError(
+                    "Integer::repr_discr: `#[repr]` hint too small for \
+                      discriminant range of enum "
+                        .to_string(),
+                ));
+            }
+            return Ok((discr, ity.is_signed()));
+        }
+
+        let at_least = if repr.c() {
+            // This is usually I32, however it can be different on some platforms,
+            // notably hexagon and arm-none/thumb-none
+            dl.c_enum_min_size
+        } else {
+            // repr(Rust) enums try to be as small as possible
+            Integer::I8
+        };
+
+        // If there are no negative values, we can use the unsigned fit.
+        Ok(if min >= 0 {
+            (cmp::max(unsigned_fit, at_least), false)
+        } else {
+            (cmp::max(signed_fit, at_least), true)
+        })
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum LayoutError {
+    UserError(String),
+    SizeOverflow,
+    HasPlaceholder,
+    NotImplemented,
+    Unknown,
+}
diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs
index 5c7aa72349f..8267ef09cb0 100644
--- a/crates/hir-def/src/lib.rs
+++ b/crates/hir-def/src/lib.rs
@@ -34,6 +34,7 @@ pub mod adt;
 pub mod data;
 pub mod generics;
 pub mod lang_item;
+pub mod layout;
 
 pub mod expr;
 pub mod body;
diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs
index 259fe1327f8..beff3f6ad96 100644
--- a/crates/hir-expand/src/name.rs
+++ b/crates/hir-expand/src/name.rs
@@ -419,6 +419,7 @@ pub mod known {
         shr,
         sub_assign,
         sub,
+        unsafe_cell,
         va_list
     );
 
diff --git a/crates/hir-ty/Cargo.toml b/crates/hir-ty/Cargo.toml
index a8a74f3bf4f..e4dd244ab85 100644
--- a/crates/hir-ty/Cargo.toml
+++ b/crates/hir-ty/Cargo.toml
@@ -25,6 +25,7 @@ chalk-derive = "0.88.0"
 la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
 once_cell = "1.15.0"
 typed-arena = "2.0.1"
+rustc_index = { version = "0.0.20221125", package = "hkalbasi-rustc-ap-rustc_index", default-features = false }
 
 stdx = { path = "../stdx", version = "0.0.0" }
 hir-def = { path = "../hir-def", version = "0.0.0" }
diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs
index ae6bf786cf5..23ad335aac7 100644
--- a/crates/hir-ty/src/db.rs
+++ b/crates/hir-ty/src/db.rs
@@ -5,8 +5,11 @@ use std::sync::Arc;
 
 use base_db::{impl_intern_key, salsa, CrateId, Upcast};
 use hir_def::{
-    db::DefDatabase, expr::ExprId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId,
-    FunctionId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId,
+    db::DefDatabase,
+    expr::ExprId,
+    layout::{Layout, LayoutError, TargetDataLayout},
+    AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GenericDefId,
+    ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId,
 };
 use la_arena::ArenaMap;
 use smallvec::SmallVec;
@@ -16,7 +19,7 @@ use crate::{
     consteval::{ComputedExpr, ConstEvalError},
     method_resolution::{InherentImpls, TraitImpls, TyFingerprint},
     Binders, CallableDefId, FnDefId, GenericArg, ImplTraitId, InferenceResult, Interner, PolyFnSig,
-    QuantifiedWhereClause, ReturnTypeImplTraits, TraitRef, Ty, TyDefId, ValueTyDefId,
+    QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, TraitRef, Ty, TyDefId, ValueTyDefId,
 };
 use hir_expand::name::Name;
 
@@ -57,6 +60,13 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
     #[salsa::invoke(crate::lower::field_types_query)]
     fn field_types(&self, var: VariantId) -> Arc<ArenaMap<LocalFieldId, Binders<Ty>>>;
 
+    #[salsa::invoke(crate::layout::layout_of_adt_query)]
+    #[salsa::cycle(crate::layout::layout_of_adt_recover)]
+    fn layout_of_adt(&self, def: AdtId, subst: Substitution) -> Result<Layout, LayoutError>;
+
+    #[salsa::invoke(crate::layout::current_target_data_layout_query)]
+    fn current_target_data_layout(&self) -> Arc<TargetDataLayout>;
+
     #[salsa::invoke(crate::lower::callable_item_sig)]
     fn callable_item_signature(&self, def: CallableDefId) -> PolyFnSig;
 
diff --git a/crates/hir-ty/src/diagnostics/match_check.rs b/crates/hir-ty/src/diagnostics/match_check.rs
index d51ad72bd27..e0905e01b6a 100644
--- a/crates/hir-ty/src/diagnostics/match_check.rs
+++ b/crates/hir-ty/src/diagnostics/match_check.rs
@@ -12,16 +12,16 @@ pub(crate) mod usefulness;
 
 use chalk_ir::Mutability;
 use hir_def::{
-    adt::VariantData, body::Body, expr::PatId, AdtId, EnumVariantId, HasModule, LocalFieldId,
-    VariantId,
+    adt::VariantData, body::Body, expr::PatId, AdtId, EnumVariantId, LocalFieldId, VariantId,
 };
-use hir_expand::name::{name, Name};
+use hir_expand::name::Name;
 use stdx::{always, never};
 
 use crate::{
     db::HirDatabase,
     display::{HirDisplay, HirDisplayError, HirFormatter},
     infer::BindingMode,
+    lang_items::is_box,
     InferenceResult, Interner, Substitution, Ty, TyExt, TyKind,
 };
 
@@ -405,13 +405,6 @@ where
     }
 }
 
-fn is_box(adt: AdtId, db: &dyn HirDatabase) -> bool {
-    let owned_box = name![owned_box].to_smol_str();
-    let krate = adt.module(db.upcast()).krate();
-    let box_adt = db.lang_item(krate, owned_box).and_then(|it| it.as_struct()).map(AdtId::from);
-    Some(adt) == box_adt
-}
-
 pub(crate) trait PatternFoldable: Sized {
     fn fold_with<F: PatternFolder>(&self, folder: &mut F) -> Self {
         self.super_fold_with(folder)
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 112eb5bd84c..874a54fc3ee 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -19,10 +19,11 @@ use std::sync::Arc;
 use chalk_ir::{cast::Cast, ConstValue, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags};
 use hir_def::{
     body::Body,
-    builtin_type::BuiltinType,
+    builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
     data::{ConstData, StaticData},
     expr::{BindingAnnotation, ExprId, PatId},
     lang_item::LangItemTarget,
+    layout::Integer,
     path::{path, Path},
     resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs},
     type_ref::TypeRef,
@@ -70,8 +71,26 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
         DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_data(s)),
         DefWithBodyId::VariantId(v) => {
             ctx.return_ty = TyBuilder::builtin(match db.enum_data(v.parent).variant_body_type() {
-                Either::Left(builtin) => BuiltinType::Int(builtin),
-                Either::Right(builtin) => BuiltinType::Uint(builtin),
+                hir_def::layout::IntegerType::Pointer(signed) => match signed {
+                    true => BuiltinType::Int(BuiltinInt::Isize),
+                    false => BuiltinType::Uint(BuiltinUint::Usize),
+                },
+                hir_def::layout::IntegerType::Fixed(size, signed) => match signed {
+                    true => BuiltinType::Int(match size {
+                        Integer::I8 => BuiltinInt::I8,
+                        Integer::I16 => BuiltinInt::I16,
+                        Integer::I32 => BuiltinInt::I32,
+                        Integer::I64 => BuiltinInt::I64,
+                        Integer::I128 => BuiltinInt::I128,
+                    }),
+                    false => BuiltinType::Uint(match size {
+                        Integer::I8 => BuiltinUint::U8,
+                        Integer::I16 => BuiltinUint::U16,
+                        Integer::I32 => BuiltinUint::U32,
+                        Integer::I64 => BuiltinUint::U64,
+                        Integer::I128 => BuiltinUint::U128,
+                    }),
+                },
             });
         }
     }
diff --git a/crates/hir-ty/src/lang_items.rs b/crates/hir-ty/src/lang_items.rs
new file mode 100644
index 00000000000..afc54e729f9
--- /dev/null
+++ b/crates/hir-ty/src/lang_items.rs
@@ -0,0 +1,20 @@
+//! Functions to detect special lang items
+
+use hir_def::{AdtId, HasModule};
+use hir_expand::name;
+
+use crate::db::HirDatabase;
+
+pub fn is_box(adt: AdtId, db: &dyn HirDatabase) -> bool {
+    let owned_box = name![owned_box].to_smol_str();
+    let krate = adt.module(db.upcast()).krate();
+    let box_adt = db.lang_item(krate, owned_box).and_then(|it| it.as_struct()).map(AdtId::from);
+    Some(adt) == box_adt
+}
+
+pub fn is_unsafe_cell(adt: AdtId, db: &dyn HirDatabase) -> bool {
+    let owned_box = name![unsafe_cell].to_smol_str();
+    let krate = adt.module(db.upcast()).krate();
+    let box_adt = db.lang_item(krate, owned_box).and_then(|it| it.as_struct()).map(AdtId::from);
+    Some(adt) == box_adt
+}
diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs
new file mode 100644
index 00000000000..3c6489fa97b
--- /dev/null
+++ b/crates/hir-ty/src/layout.rs
@@ -0,0 +1,272 @@
+//! Compute the binary representation of a type
+
+use std::sync::Arc;
+
+use chalk_ir::{AdtId, TyKind};
+pub(self) use hir_def::layout::*;
+use hir_def::LocalFieldId;
+use stdx::never;
+
+use crate::{db::HirDatabase, Interner, Substitution, Ty};
+
+use self::adt::struct_variant_idx;
+pub use self::{
+    adt::{layout_of_adt_query, layout_of_adt_recover},
+    target::current_target_data_layout_query,
+};
+
+macro_rules! user_error {
+    ($x: expr) => {
+        return Err(LayoutError::UserError(format!($x)))
+    };
+}
+
+mod adt;
+mod target;
+
+struct LayoutCx<'a> {
+    db: &'a dyn HirDatabase,
+}
+
+impl LayoutCalculator for LayoutCx<'_> {
+    type TargetDataLayoutRef = Arc<TargetDataLayout>;
+
+    fn delay_bug(&self, txt: &str) {
+        never!("{}", txt);
+    }
+
+    fn current_data_layout(&self) -> Arc<TargetDataLayout> {
+        self.db.current_target_data_layout()
+    }
+}
+
+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) -> Result<Layout, LayoutError> {
+    let dl = &*db.current_target_data_layout();
+    let cx = LayoutCx { db };
+    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,
+                    },
+                    false,
+                ),
+            ),
+            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,
+                    },
+                    true,
+                ),
+            ),
+            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)))
+                .collect::<Result<Vec<_>, _>>()?;
+            let fields = fields.iter().collect::<Vec<_>>();
+            let fields = fields.iter().collect::<Vec<_>>();
+            cx.univariant(dl, &fields, &ReprOptions::default(), kind).ok_or(LayoutError::Unknown)?
+        }
+        TyKind::Array(element, count) => {
+            let count = match count.data(Interner).value {
+                chalk_ir::ConstValue::Concrete(c) => match c.interned {
+                    hir_def::type_ref::ConstScalar::Int(x) => x as u64,
+                    hir_def::type_ref::ConstScalar::UInt(x) => x as u64,
+                    hir_def::type_ref::ConstScalar::Unknown => {
+                        user_error!("unknown const generic parameter")
+                    }
+                    _ => user_error!("mismatched type of const generic parameter"),
+                },
+                _ => return Err(LayoutError::HasPlaceholder),
+            };
+            let element = layout_of_ty(db, element)?;
+            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)?;
+            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.intern_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::Closure(_, _)
+        | TyKind::OpaqueType(_, _)
+        | 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<Layout, LayoutError> {
+    cx.univariant::<RustcEnumVariantIdx, &&Layout>(
+        &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(adt), subst) => match adt {
+            &hir_def::AdtId::StructId(i) => {
+                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,
+        },
+        _ => 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;
diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs
new file mode 100644
index 00000000000..d9791a4b630
--- /dev/null
+++ b/crates/hir-ty/src/layout/adt.rs
@@ -0,0 +1,133 @@
+//! Compute the binary representation of structs, unions and enums
+
+use std::ops::Bound;
+
+use hir_def::{
+    adt::VariantData,
+    layout::{Integer, IntegerExt, Layout, LayoutCalculator, LayoutError, RustcEnumVariantIdx},
+    AdtId, EnumVariantId, LocalEnumVariantId, VariantId,
+};
+use la_arena::RawIdx;
+use rustc_index::vec::IndexVec;
+
+use crate::{db::HirDatabase, lang_items::is_unsafe_cell, layout::field_ty, Substitution};
+
+use super::{layout_of_ty, LayoutCx};
+
+pub(crate) fn struct_variant_idx() -> RustcEnumVariantIdx {
+    RustcEnumVariantIdx(LocalEnumVariantId::from_raw(RawIdx::from(0)))
+}
+
+pub fn layout_of_adt_query(
+    db: &dyn HirDatabase,
+    def: AdtId,
+    subst: Substitution,
+) -> Result<Layout, LayoutError> {
+    let dl = db.current_target_data_layout();
+    let cx = LayoutCx { db };
+    let handle_variant = |def: VariantId, var: &VariantData| {
+        var.fields()
+            .iter()
+            .map(|(fd, _)| layout_of_ty(db, &field_ty(db, def, fd, &subst)))
+            .collect::<Result<Vec<_>, _>>()
+    };
+    let (variants, is_enum, is_union, repr) = match def {
+        AdtId::StructId(s) => {
+            let data = db.struct_data(s);
+            let mut r = IndexVec::new();
+            r.push(handle_variant(s.into(), &data.variant_data)?);
+            (r, false, false, data.repr.unwrap_or_default())
+        }
+        AdtId::UnionId(id) => {
+            let data = db.union_data(id);
+            let mut r = IndexVec::new();
+            r.push(handle_variant(id.into(), &data.variant_data)?);
+            (r, false, true, data.repr.unwrap_or_default())
+        }
+        AdtId::EnumId(e) => {
+            let data = db.enum_data(e);
+            let r = data
+                .variants
+                .iter()
+                .map(|(idx, v)| {
+                    handle_variant(
+                        EnumVariantId { parent: e, local_id: idx }.into(),
+                        &v.variant_data,
+                    )
+                })
+                .collect::<Result<IndexVec<RustcEnumVariantIdx, _>, _>>()?;
+            (r, true, false, data.repr.unwrap_or_default())
+        }
+    };
+    let variants = variants.iter().map(|x| x.iter().collect::<Vec<_>>()).collect::<Vec<_>>();
+    let variants = variants.iter().map(|x| x.iter().collect()).collect();
+    if is_union {
+        cx.layout_of_union(&repr, &variants).ok_or(LayoutError::Unknown)
+    } else {
+        cx.layout_of_struct_or_enum(
+            &repr,
+            &variants,
+            is_enum,
+            is_unsafe_cell(def, db),
+            layout_scalar_valid_range(db, def),
+            |min, max| Integer::repr_discr(&dl, &repr, min, max).unwrap_or((Integer::I8, false)),
+            variants.iter_enumerated().filter_map(|(id, _)| {
+                let AdtId::EnumId(e) = def else { return None };
+                let d = match db
+                    .const_eval_variant(EnumVariantId { parent: e, local_id: id.0 })
+                    .ok()?
+                {
+                    crate::consteval::ComputedExpr::Literal(l) => match l {
+                        hir_def::expr::Literal::Int(i, _) => i,
+                        hir_def::expr::Literal::Uint(i, _) => i as i128,
+                        _ => return None,
+                    },
+                    _ => return None,
+                };
+                Some((id, d))
+            }),
+            // FIXME: The current code for niche-filling relies on variant indices
+            // instead of actual discriminants, so enums with
+            // explicit discriminants (RFC #2363) would misbehave and we should disable
+            // niche optimization for them.
+            // The code that do it in rustc:
+            // repr.inhibit_enum_layout_opt() || def
+            //     .variants()
+            //     .iter_enumerated()
+            //     .any(|(i, v)| v.discr != ty::VariantDiscr::Relative(i.as_u32()))
+            repr.inhibit_enum_layout_opt(),
+            !is_enum
+                && variants
+                    .iter()
+                    .next()
+                    .and_then(|x| x.last().map(|x| x.is_unsized()))
+                    .unwrap_or(true),
+        )
+        .ok_or(LayoutError::SizeOverflow)
+    }
+}
+
+fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound<u128>, Bound<u128>) {
+    let attrs = db.attrs(def.into());
+    let get = |name| {
+        let attr = attrs.by_key(name).tt_values();
+        for tree in attr {
+            if let Some(x) = tree.token_trees.first() {
+                if let Ok(x) = x.to_string().parse() {
+                    return Bound::Included(x);
+                }
+            }
+        }
+        Bound::Unbounded
+    };
+    (get("rustc_layout_scalar_valid_range_start"), get("rustc_layout_scalar_valid_range_end"))
+}
+
+pub fn layout_of_adt_recover(
+    _: &dyn HirDatabase,
+    _: &[String],
+    _: &AdtId,
+    _: &Substitution,
+) -> Result<Layout, LayoutError> {
+    user_error!("infinite sized recursive type");
+}
diff --git a/crates/hir-ty/src/layout/target.rs b/crates/hir-ty/src/layout/target.rs
new file mode 100644
index 00000000000..b76274bb854
--- /dev/null
+++ b/crates/hir-ty/src/layout/target.rs
@@ -0,0 +1,46 @@
+//! Target dependent parameters needed for layouts
+
+use std::sync::Arc;
+
+use hir_def::layout::TargetDataLayout;
+
+use crate::db::HirDatabase;
+
+use super::{AbiAndPrefAlign, AddressSpace, Align, Endian, Integer, Size};
+
+pub fn current_target_data_layout_query(db: &dyn HirDatabase) -> Arc<TargetDataLayout> {
+    let crate_graph = db.crate_graph();
+    let cfg_options = &crate_graph[crate_graph.iter().next().unwrap()].cfg_options;
+    let endian = match cfg_options.get_cfg_values("target_endian").next() {
+        Some(x) if x.as_str() == "big" => Endian::Big,
+        _ => Endian::Little,
+    };
+    let pointer_size =
+        Size::from_bytes(match cfg_options.get_cfg_values("target_pointer_width").next() {
+            Some(x) => match x.as_str() {
+                "16" => 2,
+                "32" => 4,
+                _ => 8,
+            },
+            _ => 8,
+        });
+    // FIXME: These values are incorrect for many architectures, at least for aarch64 and riscv64,
+    // use `rustc +nightly -Z unstable-options --print target-spec-json` or something similar instead.
+    Arc::new(TargetDataLayout {
+        endian,
+        i1_align: AbiAndPrefAlign::new(Align::from_bytes(1).unwrap()),
+        i8_align: AbiAndPrefAlign::new(Align::from_bytes(1).unwrap()),
+        i16_align: AbiAndPrefAlign::new(Align::from_bytes(2).unwrap()),
+        i32_align: AbiAndPrefAlign::new(Align::from_bytes(4).unwrap()),
+        i64_align: AbiAndPrefAlign::new(Align::from_bytes(8).unwrap()),
+        i128_align: AbiAndPrefAlign::new(Align::from_bytes(8).unwrap()),
+        f32_align: AbiAndPrefAlign::new(Align::from_bytes(4).unwrap()),
+        f64_align: AbiAndPrefAlign::new(Align::from_bytes(8).unwrap()),
+        pointer_size,
+        pointer_align: AbiAndPrefAlign::new(Align::from_bytes(pointer_size.bytes()).unwrap()),
+        aggregate_align: AbiAndPrefAlign::new(Align::from_bytes(1).unwrap()),
+        vector_align: vec![],
+        instruction_address_space: AddressSpace(0),
+        c_enum_min_size: Integer::I32,
+    })
+}
diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs
new file mode 100644
index 00000000000..5d97a69501f
--- /dev/null
+++ b/crates/hir-ty/src/layout/tests.rs
@@ -0,0 +1,196 @@
+use base_db::fixture::WithFixture;
+use chalk_ir::{AdtId, TyKind};
+use hir_def::{
+    db::DefDatabase,
+    layout::{Layout, LayoutError},
+};
+
+use crate::{test_db::TestDB, Interner, Substitution};
+
+use super::layout_of_ty;
+
+fn eval_goal(ra_fixture: &str) -> Result<Layout, LayoutError> {
+    let (db, file_id) = TestDB::with_single_file(ra_fixture);
+    let module_id = db.module_for_file(file_id);
+    let def_map = module_id.def_map(&db);
+    let scope = &def_map[module_id.local_id].scope;
+    let adt_id = scope
+        .declarations()
+        .into_iter()
+        .find_map(|x| match x {
+            hir_def::ModuleDefId::AdtId(x) => {
+                let name = match x {
+                    hir_def::AdtId::StructId(x) => db.struct_data(x).name.to_string(),
+                    hir_def::AdtId::UnionId(x) => db.union_data(x).name.to_string(),
+                    hir_def::AdtId::EnumId(x) => db.enum_data(x).name.to_string(),
+                };
+                if name == "Goal" {
+                    Some(x)
+                } else {
+                    None
+                }
+            }
+            _ => None,
+        })
+        .unwrap();
+    let goal_ty = TyKind::Adt(AdtId(adt_id), Substitution::empty(Interner)).intern(Interner);
+    layout_of_ty(&db, &goal_ty)
+}
+
+fn check_size_and_align(ra_fixture: &str, size: u64, align: u64) {
+    let l = eval_goal(ra_fixture).unwrap();
+    assert_eq!(l.size.bytes(), size);
+    assert_eq!(l.align.abi.bytes(), align);
+}
+
+fn check_fail(ra_fixture: &str, e: LayoutError) {
+    let r = eval_goal(ra_fixture);
+    assert_eq!(r, Err(e));
+}
+
+macro_rules! size_and_align {
+    (minicore: $($x:tt),*;$($t:tt)*) => {
+        {
+            #[allow(dead_code)]
+            $($t)*
+            check_size_and_align(
+                &format!("//- minicore: {}\n{}", stringify!($($x),*), stringify!($($t)*)),
+                ::std::mem::size_of::<Goal>() as u64,
+                ::std::mem::align_of::<Goal>() as u64,
+            );
+        }
+    };
+    ($($t:tt)*) => {
+        {
+            #[allow(dead_code)]
+            $($t)*
+            check_size_and_align(
+                stringify!($($t)*),
+                ::std::mem::size_of::<Goal>() as u64,
+                ::std::mem::align_of::<Goal>() as u64,
+            );
+        }
+    };
+}
+
+#[test]
+fn hello_world() {
+    size_and_align! {
+        struct Goal(i32);
+    }
+}
+
+#[test]
+fn field_order_optimization() {
+    size_and_align! {
+        struct Goal(u8, i32, u8);
+    }
+    size_and_align! {
+        #[repr(C)]
+        struct Goal(u8, i32, u8);
+    }
+}
+
+#[test]
+fn recursive() {
+    size_and_align! {
+        struct Goal {
+            left: &'static Goal,
+            right: &'static Goal,
+        }
+    }
+    size_and_align! {
+        struct BoxLike<T: ?Sized>(*mut T);
+        struct Goal(BoxLike<Goal>);
+    }
+    check_fail(
+        r#"struct Goal(Goal);"#,
+        LayoutError::UserError("infinite sized recursive type".to_string()),
+    );
+    check_fail(
+        r#"
+        struct Foo<T>(Foo<T>);
+        struct Goal(Foo<i32>);
+        "#,
+        LayoutError::UserError("infinite sized recursive type".to_string()),
+    );
+}
+
+#[test]
+fn generic() {
+    size_and_align! {
+        struct Pair<A, B>(A, B);
+        struct Goal(Pair<Pair<i32, u8>, i64>);
+    }
+    size_and_align! {
+        struct X<const N: usize> {
+            field1: [i32; N],
+            field2: [u8; N],
+        }
+        struct Goal(X<1000>);
+    }
+}
+
+#[test]
+fn enums() {
+    size_and_align! {
+        enum Goal {
+            Quit,
+            Move { x: i32, y: i32 },
+            ChangeColor(i32, i32, i32),
+        }
+    }
+}
+
+#[test]
+fn primitives() {
+    size_and_align! {
+        struct Goal(i32, i128, isize, usize, f32, f64, bool, char);
+    }
+}
+
+#[test]
+fn tuple() {
+    size_and_align! {
+        struct Goal((), (i32, u64, bool));
+    }
+}
+
+#[test]
+fn non_zero() {
+    size_and_align! {
+        minicore: non_zero, option;
+        use core::num::NonZeroU8;
+        struct Goal(Option<NonZeroU8>);
+    }
+}
+
+#[test]
+fn niche_optimization() {
+    size_and_align! {
+        minicore: option;
+        struct Goal(Option<&'static i32>);
+    }
+    size_and_align! {
+        minicore: option;
+        struct Goal(Option<Option<bool>>);
+    }
+}
+
+#[test]
+fn enums_with_discriminants() {
+    size_and_align! {
+        enum Goal {
+            A = 1000,
+            B = 2000,
+            C = 3000,
+        }
+    }
+    size_and_align! {
+        enum Goal {
+            A = 254,
+            B,
+            C, // implicitly becomes 256, so we need two bytes
+        }
+    }
+}
diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs
index 39514fc44e6..2a41cafba98 100644
--- a/crates/hir-ty/src/lib.rs
+++ b/crates/hir-ty/src/lib.rs
@@ -27,6 +27,8 @@ pub mod display;
 pub mod method_resolution;
 pub mod primitive;
 pub mod traits;
+pub mod layout;
+pub mod lang_items;
 
 #[cfg(test)]
 mod tests;
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 5f36ce62f87..316f3938c6c 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -39,12 +39,13 @@ use arrayvec::ArrayVec;
 use base_db::{CrateDisplayName, CrateId, CrateOrigin, Edition, FileId, ProcMacroKind};
 use either::Either;
 use hir_def::{
-    adt::{ReprData, VariantData},
+    adt::VariantData,
     body::{BodyDiagnostic, SyntheticSyntax},
     expr::{BindingAnnotation, LabelId, Pat, PatId},
     generics::{TypeOrConstParamData, TypeParamProvenance},
     item_tree::ItemTreeNode,
     lang_item::LangItemTarget,
+    layout::{Layout, LayoutError, ReprOptions},
     nameres::{self, diagnostics::DefDiagnostic},
     per_ns::PerNs,
     resolver::{HasResolver, Resolver},
@@ -59,6 +60,7 @@ use hir_ty::{
     all_super_traits, autoderef,
     consteval::{unknown_const_as_generic, ComputedExpr, ConstEvalError, ConstExt},
     diagnostics::BodyValidationDiagnostic,
+    layout::layout_of_ty,
     method_resolution::{self, TyFingerprint},
     primitive::UintTy,
     traits::FnTrait,
@@ -844,6 +846,10 @@ impl Field {
         self.parent.variant_data(db).fields()[self.id].name.clone()
     }
 
+    pub fn index(&self) -> usize {
+        u32::from(self.id.into_raw()) as usize
+    }
+
     /// Returns the type as in the signature of the struct (i.e., with
     /// placeholder types for type parameters). Only use this in the context of
     /// the field definition.
@@ -859,6 +865,10 @@ impl Field {
         Type::new(db, var_id, ty)
     }
 
+    pub fn layout(&self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
+        layout_of_ty(db, &self.ty(db).ty)
+    }
+
     pub fn parent_def(&self, _db: &dyn HirDatabase) -> VariantDef {
         self.parent
     }
@@ -900,7 +910,7 @@ impl Struct {
         Type::from_def(db, self.id)
     }
 
-    pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprData> {
+    pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprOptions> {
         db.struct_data(self.id).repr.clone()
     }
 
@@ -984,8 +994,30 @@ impl Enum {
         Type::new_for_crate(
             self.id.lookup(db.upcast()).container.krate(),
             TyBuilder::builtin(match db.enum_data(self.id).variant_body_type() {
-                Either::Left(builtin) => hir_def::builtin_type::BuiltinType::Int(builtin),
-                Either::Right(builtin) => hir_def::builtin_type::BuiltinType::Uint(builtin),
+                hir_def::layout::IntegerType::Pointer(sign) => match sign {
+                    true => hir_def::builtin_type::BuiltinType::Int(
+                        hir_def::builtin_type::BuiltinInt::Isize,
+                    ),
+                    false => hir_def::builtin_type::BuiltinType::Uint(
+                        hir_def::builtin_type::BuiltinUint::Usize,
+                    ),
+                },
+                hir_def::layout::IntegerType::Fixed(i, sign) => match sign {
+                    true => hir_def::builtin_type::BuiltinType::Int(match i {
+                        hir_def::layout::Integer::I8 => hir_def::builtin_type::BuiltinInt::I8,
+                        hir_def::layout::Integer::I16 => hir_def::builtin_type::BuiltinInt::I16,
+                        hir_def::layout::Integer::I32 => hir_def::builtin_type::BuiltinInt::I32,
+                        hir_def::layout::Integer::I64 => hir_def::builtin_type::BuiltinInt::I64,
+                        hir_def::layout::Integer::I128 => hir_def::builtin_type::BuiltinInt::I128,
+                    }),
+                    false => hir_def::builtin_type::BuiltinType::Uint(match i {
+                        hir_def::layout::Integer::I8 => hir_def::builtin_type::BuiltinUint::U8,
+                        hir_def::layout::Integer::I16 => hir_def::builtin_type::BuiltinUint::U16,
+                        hir_def::layout::Integer::I32 => hir_def::builtin_type::BuiltinUint::U32,
+                        hir_def::layout::Integer::I64 => hir_def::builtin_type::BuiltinUint::U64,
+                        hir_def::layout::Integer::I128 => hir_def::builtin_type::BuiltinUint::U128,
+                    }),
+                },
             }),
         )
     }
@@ -1076,6 +1108,13 @@ impl Adt {
         })
     }
 
+    pub fn layout(self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
+        if db.generic_params(self.into()).iter().count() != 0 {
+            return Err(LayoutError::HasPlaceholder);
+        }
+        db.layout_of_adt(self.into(), Substitution::empty(Interner))
+    }
+
     /// Turns this ADT into a type. Any type parameters of the ADT will be
     /// turned into unknown types, which is good for e.g. finding the most
     /// general set of completions, but will not look very nice when printed.
@@ -3033,7 +3072,7 @@ impl Type {
 
         let adt = adt_id.into();
         match adt {
-            Adt::Struct(s) => matches!(s.repr(db), Some(ReprData { packed: true, .. })),
+            Adt::Struct(s) => s.repr(db).unwrap_or_default().pack.is_some(),
             _ => false,
         }
     }
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index fb00a40f961..f37c9f4a6d5 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -2,7 +2,9 @@
 use std::fmt::Display;
 
 use either::Either;
-use hir::{AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo};
+use hir::{
+    Adt, AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo,
+};
 use ide_db::{
     base_db::SourceDatabase,
     defs::Definition,
@@ -388,10 +390,30 @@ pub(super) fn definition(
     let mod_path = definition_mod_path(db, &def);
     let (label, docs) = match def {
         Definition::Macro(it) => label_and_docs(db, it),
-        Definition::Field(it) => label_and_docs(db, it),
+        Definition::Field(it) => label_and_layout_info_and_docs(db, it, |&it| {
+            let var_def = it.parent_def(db);
+            let id = it.index();
+            let layout = it.layout(db).ok()?;
+            let offset = match var_def {
+                hir::VariantDef::Struct(s) => {
+                    let layout = Adt::from(s).layout(db).ok()?;
+                    layout.fields.offset(id)
+                }
+                _ => return None,
+            };
+            Some(format!(
+                "size = {}, align = {}, offset = {}",
+                layout.size.bytes(),
+                layout.align.abi.bytes(),
+                offset.bytes()
+            ))
+        }),
         Definition::Module(it) => label_and_docs(db, it),
         Definition::Function(it) => label_and_docs(db, it),
-        Definition::Adt(it) => label_and_docs(db, it),
+        Definition::Adt(it) => label_and_layout_info_and_docs(db, it, |&it| {
+            let layout = it.layout(db).ok()?;
+            Some(format!("size = {}, align = {}", layout.size.bytes(), layout.align.abi.bytes()))
+        }),
         Definition::Variant(it) => label_value_and_docs(db, it, |&it| {
             if !it.parent_enum(db).is_data_carrying(db) {
                 match it.eval(db) {
@@ -489,6 +511,25 @@ where
     (label, docs)
 }
 
+fn label_and_layout_info_and_docs<D, E, V>(
+    db: &RootDatabase,
+    def: D,
+    value_extractor: E,
+) -> (String, Option<hir::Documentation>)
+where
+    D: HasAttrs + HirDisplay,
+    E: Fn(&D) -> Option<V>,
+    V: Display,
+{
+    let label = if let Some(value) = value_extractor(&def) {
+        format!("{} // {}", def.display(db), value)
+    } else {
+        def.display(db).to_string()
+    };
+    let docs = def.attrs(db).docs();
+    (label, docs)
+}
+
 fn label_value_and_docs<D, E, V>(
     db: &RootDatabase,
     def: D,
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index f8be4cfb04c..f82fd6d0289 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -523,6 +523,27 @@ fn main() { }
 }
 
 #[test]
+fn hover_field_offset() {
+    // Hovering over the field when instantiating
+    check(
+        r#"
+struct Foo { fiel$0d_a: u8, field_b: i32, field_c: i16 }
+"#,
+        expect![[r#"
+            *field_a*
+
+            ```rust
+            test::Foo
+            ```
+
+            ```rust
+            field_a: u8 // size = 1, align = 1, offset = 4
+            ```
+        "#]],
+    );
+}
+
+#[test]
 fn hover_shows_struct_field_info() {
     // Hovering over the field when instantiating
     check(
@@ -534,16 +555,16 @@ fn main() {
 }
 "#,
         expect![[r#"
-                *field_a*
+            *field_a*
 
-                ```rust
-                test::Foo
-                ```
+            ```rust
+            test::Foo
+            ```
 
-                ```rust
-                field_a: u32
-                ```
-            "#]],
+            ```rust
+            field_a: u32 // size = 4, align = 4, offset = 0
+            ```
+        "#]],
     );
 
     // Hovering over the field in the definition
@@ -556,16 +577,16 @@ fn main() {
 }
 "#,
         expect![[r#"
-                *field_a*
+            *field_a*
 
-                ```rust
-                test::Foo
-                ```
+            ```rust
+            test::Foo
+            ```
 
-                ```rust
-                field_a: u32
-                ```
-            "#]],
+            ```rust
+            field_a: u32 // size = 4, align = 4, offset = 0
+            ```
+        "#]],
     );
 }
 
@@ -1508,30 +1529,30 @@ struct Bar;
 
 fn foo() { let bar = Ba$0r; }
 "#,
-        expect![[r##"
-                *Bar*
+        expect![[r#"
+            *Bar*
 
-                ```rust
-                test
-                ```
+            ```rust
+            test
+            ```
 
-                ```rust
-                struct Bar
-                ```
+            ```rust
+            struct Bar // size = 0, align = 1
+            ```
 
-                ---
+            ---
 
-                This is an example
-                multiline doc
+            This is an example
+            multiline doc
 
-                # Example
+            # Example
 
-                ```
-                let five = 5;
+            ```
+            let five = 5;
 
-                assert_eq!(6, my_crate::add_one(5));
-                ```
-            "##]],
+            assert_eq!(6, my_crate::add_one(5));
+            ```
+        "#]],
     );
 }
 
@@ -1545,20 +1566,20 @@ struct Bar;
 fn foo() { let bar = Ba$0r; }
 "#,
         expect![[r#"
-                *Bar*
+            *Bar*
 
-                ```rust
-                test
-                ```
+            ```rust
+            test
+            ```
 
-                ```rust
-                struct Bar
-                ```
+            ```rust
+            struct Bar // size = 0, align = 1
+            ```
 
-                ---
+            ---
 
-                bar docs
-            "#]],
+            bar docs
+        "#]],
     );
 }
 
@@ -1574,22 +1595,22 @@ struct Bar;
 fn foo() { let bar = Ba$0r; }
 "#,
         expect![[r#"
-                *Bar*
+            *Bar*
 
-                ```rust
-                test
-                ```
+            ```rust
+            test
+            ```
 
-                ```rust
-                struct Bar
-                ```
+            ```rust
+            struct Bar // size = 0, align = 1
+            ```
 
-                ---
+            ---
 
-                bar docs 0
-                bar docs 1
-                bar docs 2
-            "#]],
+            bar docs 0
+            bar docs 1
+            bar docs 2
+        "#]],
     );
 }
 
@@ -1602,20 +1623,20 @@ pub struct Foo;
 pub struct B$0ar
 "#,
         expect![[r#"
-                *Bar*
+            *Bar*
 
-                ```rust
-                test
-                ```
+            ```rust
+            test
+            ```
 
-                ```rust
-                pub struct Bar
-                ```
+            ```rust
+            pub struct Bar // size = 0, align = 1
+            ```
 
-                ---
+            ---
 
-                [external](https://www.google.com)
-            "#]],
+            [external](https://www.google.com)
+        "#]],
     );
 }
 
@@ -1629,20 +1650,20 @@ pub struct Foo;
 pub struct B$0ar
 "#,
         expect![[r#"
-                *Bar*
+            *Bar*
 
-                ```rust
-                test
-                ```
+            ```rust
+            test
+            ```
 
-                ```rust
-                pub struct Bar
-                ```
+            ```rust
+            pub struct Bar // size = 0, align = 1
+            ```
 
-                ---
+            ---
 
-                [baz](Baz)
-            "#]],
+            [baz](Baz)
+        "#]],
     );
 }
 
@@ -2960,7 +2981,7 @@ fn main() {
             ```
 
             ```rust
-            f: i32
+            f: i32 // size = 4, align = 4, offset = 0
             ```
         "#]],
     );
@@ -4203,20 +4224,20 @@ pub fn gimme() -> theitem::TheItem {
 }
 "#,
         expect![[r#"
-                *[`TheItem`]*
+            *[`TheItem`]*
 
-                ```rust
-                test::theitem
-                ```
+            ```rust
+            test::theitem
+            ```
 
-                ```rust
-                pub struct TheItem
-                ```
+            ```rust
+            pub struct TheItem // size = 0, align = 1
+            ```
 
-                ---
+            ---
 
-                This is the item. Cool!
-            "#]],
+            This is the item. Cool!
+        "#]],
     );
 }
 
@@ -4351,20 +4372,20 @@ mod string {
 }
 "#,
         expect![[r#"
-                *String*
+            *String*
 
-                ```rust
-                main
-                ```
+            ```rust
+            main
+            ```
 
-                ```rust
-                struct String
-                ```
+            ```rust
+            struct String // size = 0, align = 1
+            ```
 
-                ---
+            ---
 
-                Custom `String` type.
-            "#]],
+            Custom `String` type.
+        "#]],
     )
 }
 
@@ -5025,7 +5046,7 @@ foo_macro!(
             ```
 
             ```rust
-            pub struct Foo
+            pub struct Foo // size = 0, align = 1
             ```
 
             ---
@@ -5040,7 +5061,7 @@ fn hover_intra_in_attr() {
     check(
         r#"
 #[doc = "Doc comment for [`Foo$0`]"]
-pub struct Foo;
+pub struct Foo(i32);
 "#,
         expect![[r#"
             *[`Foo`]*
@@ -5050,7 +5071,7 @@ pub struct Foo;
             ```
 
             ```rust
-            pub struct Foo
+            pub struct Foo // size = 4, align = 4
             ```
 
             ---
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index 013fd6b4fdf..750b64fea6f 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -30,6 +30,7 @@
 //!     index: sized
 //!     iterator: option
 //!     iterators: iterator, fn
+//!     non_zero:
 //!     option:
 //!     ord: eq, option
 //!     pin:
@@ -704,6 +705,15 @@ mod macros {
 }
 // endregion:derive
 
+// region:non_zero
+pub mod num {
+    #[repr(transparent)]
+    #[rustc_layout_scalar_valid_range_start(1)]
+    #[rustc_nonnull_optimization_guaranteed]
+    pub struct NonZeroU8(u8);
+}
+// endregion:non_zero
+
 // region:bool_impl
 #[lang = "bool"]
 impl bool {
diff --git a/lib/la-arena/src/map.rs b/lib/la-arena/src/map.rs
index 5f347e27450..b9d491da3c0 100644
--- a/lib/la-arena/src/map.rs
+++ b/lib/la-arena/src/map.rs
@@ -86,6 +86,14 @@ impl<T, V> ArenaMap<Idx<T>, V> {
         self.v.iter().enumerate().filter_map(|(idx, o)| Some((Self::from_idx(idx), o.as_ref()?)))
     }
 
+    /// Returns an iterator over the arena indexes and values in the map.
+    pub fn iter_mut(&mut self) -> impl Iterator<Item = (Idx<T>, &mut V)> {
+        self.v
+            .iter_mut()
+            .enumerate()
+            .filter_map(|(idx, o)| Some((Self::from_idx(idx), o.as_mut()?)))
+    }
+
     /// Gets the given key's corresponding entry in the map for in-place manipulation.
     pub fn entry(&mut self, idx: Idx<T>) -> Entry<'_, Idx<T>, V> {
         let idx = Self::to_idx(idx);