Repository: rust-lang/rust-analyzer
Pull request #19036 (2025-01-26 09:35:30)

Split out `ExpressionStore` from `Body`
Simple refactor that can be split out for the https://github.com/rust-lang/rust-analyzer/issues/7434 work


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 2dfca7c480..f92668a6a9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -846,6 +846,7 @@ dependencies = [
  "dashmap",
  "hashbrown",
  "rustc-hash 2.0.0",
+ "sptr",
  "triomphe",
 ]
 
@@ -1926,6 +1927,12 @@ dependencies = [
  "vfs",
 ]
 
+[[package]]
+name = "sptr"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a"
+
 [[package]]
 name = "stdx"
 version = "0.0.0"
diff --git a/Cargo.toml b/Cargo.toml
index c42ae171d8..1029844cd3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,7 +4,7 @@ exclude = ["crates/proc-macro-srv/proc-macro-test/imp"]
 resolver = "2"
 
 [workspace.package]
-rust-version = "1.84"
+rust-version = "1.83"
 edition = "2021"
 license = "MIT OR Apache-2.0"
 authors = ["rust-analyzer team"]
diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs
index bf6cc1dcad..598a850898 100644
--- a/crates/hir-def/src/db.rs
+++ b/crates/hir-def/src/db.rs
@@ -10,12 +10,12 @@ use triomphe::Arc;
 
 use crate::{
     attr::{Attrs, AttrsWithOwner},
-    body::{scope::ExprScopes, Body, BodySourceMap},
     data::{
         adt::{EnumData, EnumVariantData, StructData, VariantData},
         ConstData, ExternCrateDeclData, FunctionData, ImplData, Macro2Data, MacroRulesData,
         ProcMacroData, StaticData, TraitAliasData, TraitData, TypeAliasData,
     },
+    expr_store::{scope::ExprScopes, Body, BodySourceMap},
     generics::GenericParams,
     import_map::ImportMap,
     item_tree::{AttrOwner, ItemTree, ItemTreeSourceMaps},
diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/expr_store.rs
similarity index 72%
rename from crates/hir-def/src/body.rs
rename to crates/hir-def/src/expr_store.rs
index de43924930..9df6eaade7 100644
--- a/crates/hir-def/src/body.rs
+++ b/crates/hir-def/src/expr_store.rs
@@ -1,18 +1,19 @@
-//! Defines `Body`: a lowered representation of bodies of functions, statics and
+//! Defines `ExpressionStore`: a lowered representation of functions, statics and
 //! consts.
+mod body;
 mod lower;
 mod pretty;
 pub mod scope;
+
 #[cfg(test)]
 mod tests;
 
 use std::ops::{Deref, Index};
 
-use base_db::CrateId;
 use cfg::{CfgExpr, CfgOptions};
 use either::Either;
 use hir_expand::{name::Name, ExpandError, InFile};
-use la_arena::{Arena, ArenaMap, Idx, RawIdx};
+use la_arena::{Arena, ArenaMap};
 use rustc_hash::FxHashMap;
 use smallvec::SmallVec;
 use span::{Edition, MacroFileId, SyntaxContextData};
@@ -22,19 +23,18 @@ use tt::TextRange;
 
 use crate::{
     db::DefDatabase,
-    expander::Expander,
     hir::{
-        dummy_expr_id, Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, Label,
-        LabelId, Pat, PatId, RecordFieldPat, Statement,
+        Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, Label, LabelId, Pat,
+        PatId, RecordFieldPat, Statement,
     },
-    item_tree::AttrOwner,
     nameres::DefMap,
     path::{ModPath, Path},
-    src::HasSource,
     type_ref::{TypeRef, TypeRefId, TypesMap, TypesSourceMap},
-    BlockId, DefWithBodyId, HasModule, Lookup, SyntheticSyntax,
+    BlockId, DefWithBodyId, Lookup, SyntheticSyntax,
 };
 
+pub use self::body::{Body, BodySourceMap};
+
 /// A wrapper around [`span::SyntaxContextId`] that is intended only for comparisons.
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct HygieneId(span::SyntaxContextId);
@@ -58,9 +58,29 @@ impl HygieneId {
     }
 }
 
-/// The body of an item (function, const etc.).
+pub type ExprPtr = AstPtr<ast::Expr>;
+pub type ExprSource = InFile<ExprPtr>;
+
+pub type PatPtr = AstPtr<ast::Pat>;
+pub type PatSource = InFile<PatPtr>;
+
+pub type LabelPtr = AstPtr<ast::Label>;
+pub type LabelSource = InFile<LabelPtr>;
+
+pub type FieldPtr = AstPtr<ast::RecordExprField>;
+pub type FieldSource = InFile<FieldPtr>;
+
+pub type PatFieldPtr = AstPtr<Either<ast::RecordExprField, ast::RecordPatField>>;
+pub type PatFieldSource = InFile<PatFieldPtr>;
+
+pub type ExprOrPatPtr = AstPtr<Either<ast::Expr, ast::Pat>>;
+pub type ExprOrPatSource = InFile<ExprOrPatPtr>;
+
+pub type SelfParamPtr = AstPtr<ast::SelfParam>;
+pub type MacroCallPtr = AstPtr<ast::MacroCall>;
+
 #[derive(Debug, Eq, PartialEq)]
-pub struct Body {
+pub struct ExpressionStore {
     pub exprs: Arena<Expr>,
     pub pats: Arena<Pat>,
     pub bindings: Arena<Binding>,
@@ -68,19 +88,9 @@ pub struct Body {
     /// Id of the closure/coroutine that owns the corresponding binding. If a binding is owned by the
     /// top level expression, it will not be listed in here.
     pub binding_owners: FxHashMap<BindingId, ExprId>,
-    /// The patterns for the function's parameters. While the parameter types are
-    /// part of the function signature, the patterns are not (they don't change
-    /// the external type of the function).
-    ///
-    /// If this `Body` is for the body of a constant, this will just be
-    /// empty.
-    pub params: Box<[PatId]>,
-    pub self_param: Option<BindingId>,
-    /// The `ExprId` of the actual body expression.
-    pub body_expr: ExprId,
     pub types: TypesMap,
-    /// Block expressions in this body that may contain inner items.
-    block_scopes: Vec<BlockId>,
+    /// Block expressions in this store that may contain inner items.
+    block_scopes: Box<[BlockId]>,
 
     /// A map from binding to its hygiene ID.
     ///
@@ -92,44 +102,13 @@ pub struct Body {
     binding_hygiene: FxHashMap<BindingId, HygieneId>,
     /// A map from an variable usages to their hygiene ID.
     ///
-    /// Expressions that can be recorded here are single segment path, although not all single segments path refer
+    /// Expressions (and destructuing patterns) that can be recorded here are single segment path, although not all single segments path refer
     /// to variables and have hygiene (some refer to items, we don't know at this stage).
-    expr_hygiene: FxHashMap<ExprId, HygieneId>,
-    /// A map from a destructuring assignment possible variable usages to their hygiene ID.
-    pat_hygiene: FxHashMap<PatId, HygieneId>,
+    ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>,
 }
 
-pub type ExprPtr = AstPtr<ast::Expr>;
-pub type ExprSource = InFile<ExprPtr>;
-
-pub type PatPtr = AstPtr<ast::Pat>;
-pub type PatSource = InFile<PatPtr>;
-
-pub type LabelPtr = AstPtr<ast::Label>;
-pub type LabelSource = InFile<LabelPtr>;
-
-pub type FieldPtr = AstPtr<ast::RecordExprField>;
-pub type FieldSource = InFile<FieldPtr>;
-
-pub type PatFieldPtr = AstPtr<Either<ast::RecordExprField, ast::RecordPatField>>;
-pub type PatFieldSource = InFile<PatFieldPtr>;
-
-pub type ExprOrPatPtr = AstPtr<Either<ast::Expr, ast::Pat>>;
-pub type ExprOrPatSource = InFile<ExprOrPatPtr>;
-
-/// An item body together with the mapping from syntax nodes to HIR expression
-/// IDs. This is needed to go from e.g. a position in a file to the HIR
-/// expression containing it; but for type inference etc., we want to operate on
-/// a structure that is agnostic to the actual positions of expressions in the
-/// file, so that we don't recompute types whenever some whitespace is typed.
-///
-/// One complication here is that, due to macro expansion, a single `Body` might
-/// be spread across several files. So, for each ExprId and PatId, we record
-/// both the HirFileId and the position inside the file. However, we only store
-/// AST -> ExprId mapping for non-macro files, as it is not clear how to handle
-/// this properly for macros.
-#[derive(Default, Debug, Eq, PartialEq)]
-pub struct BodySourceMap {
+#[derive(Debug, Eq, PartialEq, Default)]
+pub struct ExpressionStoreSourceMap {
     // AST expressions can create patterns in destructuring assignments. Therefore, `ExprSource` can also map
     // to `PatId`, and `PatId` can also map to `ExprSource` (the other way around is unaffected).
     expr_map: FxHashMap<ExprSource, ExprOrPatId>,
@@ -141,7 +120,6 @@ pub struct BodySourceMap {
     label_map: FxHashMap<LabelSource, LabelId>,
     label_map_back: ArenaMap<LabelId, LabelSource>,
 
-    self_param: Option<InFile<AstPtr<ast::SelfParam>>>,
     binding_definitions: FxHashMap<BindingId, SmallVec<[PatId; 4]>>,
 
     /// We don't create explicit nodes for record fields (`S { record_field: 92 }`).
@@ -153,11 +131,25 @@ pub struct BodySourceMap {
 
     template_map: Option<Box<FormatTemplate>>,
 
-    expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, MacroFileId>,
+    expansions: FxHashMap<InFile<MacroCallPtr>, MacroFileId>,
 
-    /// Diagnostics accumulated during body lowering. These contain `AstPtr`s and so are stored in
+    /// Diagnostics accumulated during lowering. These contain `AstPtr`s and so are stored in
     /// the source map (since they're just as volatile).
-    diagnostics: Vec<BodyDiagnostic>,
+    diagnostics: Vec<ExpressionStoreDiagnostics>,
+}
+
+/// The body of an item (function, const etc.).
+#[derive(Debug, Eq, PartialEq, Default)]
+pub struct ExpressionStoreBuilder {
+    pub exprs: Arena<Expr>,
+    pub pats: Arena<Pat>,
+    pub bindings: Arena<Binding>,
+    pub labels: Arena<Label>,
+    pub binding_owners: FxHashMap<BindingId, ExprId>,
+    pub types: TypesMap,
+    block_scopes: Vec<BlockId>,
+    binding_hygiene: FxHashMap<BindingId, HygieneId>,
+    ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>,
 }
 
 #[derive(Default, Debug, Eq, PartialEq)]
@@ -171,166 +163,62 @@ struct FormatTemplate {
     /// The value stored for each capture is its template literal and offset inside it. The template literal
     /// is from the `format_args[_nl]!()` macro and so needs to be mapped up once to go to the user-written
     /// template.
-    implicit_capture_to_source: FxHashMap<ExprId, InFile<(AstPtr<ast::Expr>, TextRange)>>,
+    implicit_capture_to_source: FxHashMap<ExprId, InFile<(ExprPtr, TextRange)>>,
 }
 
 #[derive(Debug, Eq, PartialEq)]
-pub enum BodyDiagnostic {
+pub enum ExpressionStoreDiagnostics {
     InactiveCode { node: InFile<SyntaxNodePtr>, cfg: CfgExpr, opts: CfgOptions },
-    MacroError { node: InFile<AstPtr<ast::MacroCall>>, err: ExpandError },
-    UnresolvedMacroCall { node: InFile<AstPtr<ast::MacroCall>>, path: ModPath },
+    MacroError { node: InFile<MacroCallPtr>, err: ExpandError },
+    UnresolvedMacroCall { node: InFile<MacroCallPtr>, path: ModPath },
     UnreachableLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
     AwaitOutsideOfAsync { node: InFile<AstPtr<ast::AwaitExpr>>, location: String },
     UndeclaredLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
 }
 
-impl Body {
-    pub(crate) fn body_with_source_map_query(
-        db: &dyn DefDatabase,
-        def: DefWithBodyId,
-    ) -> (Arc<Body>, Arc<BodySourceMap>) {
-        let _p = tracing::info_span!("body_with_source_map_query").entered();
-        let mut params = None;
-
-        let mut is_async_fn = false;
-        let InFile { file_id, value: body } = {
-            match def {
-                DefWithBodyId::FunctionId(f) => {
-                    let data = db.function_data(f);
-                    let f = f.lookup(db);
-                    let src = f.source(db);
-                    params = src.value.param_list().map(move |param_list| {
-                        let item_tree = f.id.item_tree(db);
-                        let func = &item_tree[f.id.value];
-                        let krate = f.container.module(db).krate;
-                        let crate_graph = db.crate_graph();
-                        (
-                            param_list,
-                            (0..func.params.len()).map(move |idx| {
-                                item_tree
-                                    .attrs(
-                                        db,
-                                        krate,
-                                        AttrOwner::Param(
-                                            f.id.value,
-                                            Idx::from_raw(RawIdx::from(idx as u32)),
-                                        ),
-                                    )
-                                    .is_cfg_enabled(&crate_graph[krate].cfg_options)
-                            }),
-                        )
-                    });
-                    is_async_fn = data.is_async();
-                    src.map(|it| it.body().map(ast::Expr::from))
-                }
-                DefWithBodyId::ConstId(c) => {
-                    let c = c.lookup(db);
-                    let src = c.source(db);
-                    src.map(|it| it.body())
-                }
-                DefWithBodyId::StaticId(s) => {
-                    let s = s.lookup(db);
-                    let src = s.source(db);
-                    src.map(|it| it.body())
-                }
-                DefWithBodyId::VariantId(v) => {
-                    let s = v.lookup(db);
-                    let src = s.source(db);
-                    src.map(|it| it.expr())
-                }
-                DefWithBodyId::InTypeConstId(c) => c.lookup(db).id.map(|_| c.source(db).expr()),
-            }
-        };
-        let module = def.module(db);
-        let expander = Expander::new(db, file_id, module);
-        let (mut body, mut source_map) =
-            Body::new(db, def, expander, params, body, module.krate, is_async_fn);
-        body.shrink_to_fit();
-        source_map.shrink_to_fit();
-
-        (Arc::new(body), Arc::new(source_map))
-    }
-
-    pub(crate) fn body_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<Body> {
-        db.body_with_source_map(def).0
-    }
-
-    /// Returns an iterator over all block expressions in this body that define inner items.
-    pub fn blocks<'a>(
-        &'a self,
-        db: &'a dyn DefDatabase,
-    ) -> impl Iterator<Item = (BlockId, Arc<DefMap>)> + 'a {
-        self.block_scopes.iter().map(move |&block| (block, db.block_def_map(block)))
-    }
-
-    pub fn pretty_print(
-        &self,
-        db: &dyn DefDatabase,
-        owner: DefWithBodyId,
-        edition: Edition,
-    ) -> String {
-        pretty::print_body_hir(db, self, owner, edition)
-    }
-
-    pub fn pretty_print_expr(
-        &self,
-        db: &dyn DefDatabase,
-        owner: DefWithBodyId,
-        expr: ExprId,
-        edition: Edition,
-    ) -> String {
-        pretty::print_expr_hir(db, self, owner, expr, edition)
-    }
-
-    pub fn pretty_print_pat(
-        &self,
-        db: &dyn DefDatabase,
-        owner: DefWithBodyId,
-        pat: PatId,
-        oneline: bool,
-        edition: Edition,
-    ) -> String {
-        pretty::print_pat_hir(db, self, owner, pat, oneline, edition)
-    }
-
-    fn new(
-        db: &dyn DefDatabase,
-        owner: DefWithBodyId,
-        expander: Expander,
-        params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
-        body: Option<ast::Expr>,
-        krate: CrateId,
-        is_async_fn: bool,
-    ) -> (Body, BodySourceMap) {
-        lower::lower(db, owner, expander, params, body, krate, is_async_fn)
-    }
-
-    fn shrink_to_fit(&mut self) {
+impl ExpressionStoreBuilder {
+    fn finish(self) -> ExpressionStore {
         let Self {
-            body_expr: _,
-            params: _,
-            self_param: _,
             block_scopes,
-            exprs,
-            labels,
-            pats,
-            bindings,
-            binding_owners,
-            binding_hygiene,
-            expr_hygiene,
-            pat_hygiene,
-            types,
+            mut exprs,
+            mut labels,
+            mut pats,
+            mut bindings,
+            mut binding_owners,
+            mut binding_hygiene,
+            mut ident_hygiene,
+            mut types,
         } = self;
-        block_scopes.shrink_to_fit();
         exprs.shrink_to_fit();
         labels.shrink_to_fit();
         pats.shrink_to_fit();
         bindings.shrink_to_fit();
         binding_owners.shrink_to_fit();
         binding_hygiene.shrink_to_fit();
-        expr_hygiene.shrink_to_fit();
-        pat_hygiene.shrink_to_fit();
+        ident_hygiene.shrink_to_fit();
         types.shrink_to_fit();
+
+        ExpressionStore {
+            exprs,
+            pats,
+            bindings,
+            labels,
+            binding_owners,
+            types,
+            block_scopes: block_scopes.into_boxed_slice(),
+            binding_hygiene,
+            ident_hygiene,
+        }
+    }
+}
+
+impl ExpressionStore {
+    /// Returns an iterator over all block expressions in this store that define inner items.
+    pub fn blocks<'a>(
+        &'a self,
+        db: &'a dyn DefDatabase,
+    ) -> impl Iterator<Item = (BlockId, Arc<DefMap>)> + 'a {
+        self.block_scopes.iter().map(move |&block| (block, db.block_def_map(block)))
     }
 
     pub fn walk_bindings_in_pat(&self, pat_id: PatId, mut f: impl FnMut(BindingId)) {
@@ -658,11 +546,11 @@ impl Body {
     }
 
     pub fn expr_path_hygiene(&self, expr: ExprId) -> HygieneId {
-        self.expr_hygiene.get(&expr).copied().unwrap_or(HygieneId::ROOT)
+        self.ident_hygiene.get(&expr.into()).copied().unwrap_or(HygieneId::ROOT)
     }
 
     pub fn pat_path_hygiene(&self, pat: PatId) -> HygieneId {
-        self.pat_hygiene.get(&pat).copied().unwrap_or(HygieneId::ROOT)
+        self.ident_hygiene.get(&pat.into()).copied().unwrap_or(HygieneId::ROOT)
     }
 
     pub fn expr_or_pat_path_hygiene(&self, id: ExprOrPatId) -> HygieneId {
@@ -673,27 +561,7 @@ impl Body {
     }
 }
 
-impl Default for Body {
-    fn default() -> Self {
-        Self {
-            body_expr: dummy_expr_id(),
-            exprs: Default::default(),
-            pats: Default::default(),
-            bindings: Default::default(),
-            labels: Default::default(),
-            params: Default::default(),
-            block_scopes: Default::default(),
-            binding_owners: Default::default(),
-            self_param: Default::default(),
-            binding_hygiene: Default::default(),
-            expr_hygiene: Default::default(),
-            pat_hygiene: Default::default(),
-            types: Default::default(),
-        }
-    }
-}
-
-impl Index<ExprId> for Body {
+impl Index<ExprId> for ExpressionStore {
     type Output = Expr;
 
     fn index(&self, expr: ExprId) -> &Expr {
@@ -701,7 +569,7 @@ impl Index<ExprId> for Body {
     }
 }
 
-impl Index<PatId> for Body {
+impl Index<PatId> for ExpressionStore {
     type Output = Pat;
 
     fn index(&self, pat: PatId) -> &Pat {
@@ -709,7 +577,7 @@ impl Index<PatId> for Body {
     }
 }
 
-impl Index<LabelId> for Body {
+impl Index<LabelId> for ExpressionStore {
     type Output = Label;
 
     fn index(&self, label: LabelId) -> &Label {
@@ -717,7 +585,7 @@ impl Index<LabelId> for Body {
     }
 }
 
-impl Index<BindingId> for Body {
+impl Index<BindingId> for ExpressionStore {
     type Output = Binding;
 
     fn index(&self, b: BindingId) -> &Binding {
@@ -725,7 +593,7 @@ impl Index<BindingId> for Body {
     }
 }
 
-impl Index<TypeRefId> for Body {
+impl Index<TypeRefId> for ExpressionStore {
     type Output = TypeRef;
 
     fn index(&self, b: TypeRefId) -> &TypeRef {
@@ -735,7 +603,7 @@ impl Index<TypeRefId> for Body {
 
 // FIXME: Change `node_` prefix to something more reasonable.
 // Perhaps `expr_syntax` and `expr_id`?
-impl BodySourceMap {
+impl ExpressionStoreSourceMap {
     pub fn expr_or_pat_syntax(&self, id: ExprOrPatId) -> Result<ExprOrPatSource, SyntheticSyntax> {
         match id {
             ExprOrPatId::ExprId(id) => self.expr_syntax(id).map(|it| it.map(AstPtr::wrap_left)),
@@ -757,9 +625,7 @@ impl BodySourceMap {
         self.expansions.get(&src).cloned()
     }
 
-    pub fn macro_calls(
-        &self,
-    ) -> impl Iterator<Item = (InFile<AstPtr<ast::MacroCall>>, MacroFileId)> + '_ {
+    pub fn macro_calls(&self) -> impl Iterator<Item = (InFile<MacroCallPtr>, MacroFileId)> + '_ {
         self.expansions.iter().map(|(&a, &b)| (a, b))
     }
 
@@ -767,10 +633,6 @@ impl BodySourceMap {
         self.pat_map_back.get(pat).cloned().ok_or(SyntheticSyntax)
     }
 
-    pub fn self_param_syntax(&self) -> Option<InFile<AstPtr<ast::SelfParam>>> {
-        self.self_param
-    }
-
     pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option<PatId> {
         self.pat_map.get(&node.map(AstPtr::new)).cloned()
     }
@@ -801,9 +663,7 @@ impl BodySourceMap {
         self.expr_map.get(&src).copied()
     }
 
-    pub fn expansions(
-        &self,
-    ) -> impl Iterator<Item = (&InFile<AstPtr<ast::MacroCall>>, &MacroFileId)> {
+    pub fn expansions(&self) -> impl Iterator<Item = (&InFile<MacroCallPtr>, &MacroFileId)> {
         self.expansions.iter()
     }
 
@@ -823,7 +683,7 @@ impl BodySourceMap {
     pub fn format_args_implicit_capture(
         &self,
         capture_expr: ExprId,
-    ) -> Option<InFile<(AstPtr<ast::Expr>, TextRange)>> {
+    ) -> Option<InFile<(ExprPtr, TextRange)>> {
         self.template_map.as_ref()?.implicit_capture_to_source.get(&capture_expr).copied()
     }
 
@@ -837,14 +697,13 @@ impl BodySourceMap {
             .zip(self.template_map.as_ref()?.asm_to_captures.get(&expr).map(std::ops::Deref::deref))
     }
 
-    /// Get a reference to the body source map's diagnostics.
-    pub fn diagnostics(&self) -> &[BodyDiagnostic] {
+    /// Get a reference to the source map's diagnostics.
+    pub fn diagnostics(&self) -> &[ExpressionStoreDiagnostics] {
         &self.diagnostics
     }
 
     fn shrink_to_fit(&mut self) {
         let Self {
-            self_param: _,
             expr_map,
             expr_map_back,
             pat_map,
diff --git a/crates/hir-def/src/expr_store/body.rs b/crates/hir-def/src/expr_store/body.rs
new file mode 100644
index 0000000000..a55fec4f8b
--- /dev/null
+++ b/crates/hir-def/src/expr_store/body.rs
@@ -0,0 +1,175 @@
+//! Defines `Body`: a lowered representation of functions, statics and
+//! consts.
+use std::ops;
+
+use hir_expand::{InFile, Lookup};
+use la_arena::{Idx, RawIdx};
+use span::Edition;
+use syntax::ast;
+use triomphe::Arc;
+
+use crate::{
+    db::DefDatabase,
+    expander::Expander,
+    expr_store::{lower, pretty, ExpressionStore, ExpressionStoreSourceMap, SelfParamPtr},
+    hir::{BindingId, ExprId, PatId},
+    item_tree::AttrOwner,
+    src::HasSource,
+    DefWithBodyId, HasModule,
+};
+
+/// The body of an item (function, const etc.).
+#[derive(Debug, Eq, PartialEq)]
+pub struct Body {
+    pub store: ExpressionStore,
+    /// The patterns for the function's parameters. While the parameter types are
+    /// part of the function signature, the patterns are not (they don't change
+    /// the external type of the function).
+    ///
+    /// If this `Body` is for the body of a constant, this will just be
+    /// empty.
+    pub params: Box<[PatId]>,
+    pub self_param: Option<BindingId>,
+    /// The `ExprId` of the actual body expression.
+    pub body_expr: ExprId,
+}
+
+impl ops::Deref for Body {
+    type Target = ExpressionStore;
+
+    fn deref(&self) -> &Self::Target {
+        &self.store
+    }
+}
+
+/// An item body together with the mapping from syntax nodes to HIR expression
+/// IDs. This is needed to go from e.g. a position in a file to the HIR
+/// expression containing it; but for type inference etc., we want to operate on
+/// a structure that is agnostic to the actual positions of expressions in the
+/// file, so that we don't recompute types whenever some whitespace is typed.
+///
+/// One complication here is that, due to macro expansion, a single `Body` might
+/// be spread across several files. So, for each ExprId and PatId, we record
+/// both the HirFileId and the position inside the file. However, we only store
+/// AST -> ExprId mapping for non-macro files, as it is not clear how to handle
+/// this properly for macros.
+#[derive(Default, Debug, Eq, PartialEq)]
+pub struct BodySourceMap {
+    pub self_param: Option<InFile<SelfParamPtr>>,
+    pub store: ExpressionStoreSourceMap,
+}
+
+impl ops::Deref for BodySourceMap {
+    type Target = ExpressionStoreSourceMap;
+
+    fn deref(&self) -> &Self::Target {
+        &self.store
+    }
+}
+
+impl Body {
+    pub(crate) fn body_with_source_map_query(
+        db: &dyn DefDatabase,
+        def: DefWithBodyId,
+    ) -> (Arc<Body>, Arc<BodySourceMap>) {
+        let _p = tracing::info_span!("body_with_source_map_query").entered();
+        let mut params = None;
+
+        let mut is_async_fn = false;
+        let InFile { file_id, value: body } = {
+            match def {
+                DefWithBodyId::FunctionId(f) => {
+                    let data = db.function_data(f);
+                    let f = f.lookup(db);
+                    let src = f.source(db);
+                    params = src.value.param_list().map(move |param_list| {
+                        let item_tree = f.id.item_tree(db);
+                        let func = &item_tree[f.id.value];
+                        let krate = f.container.module(db).krate;
+                        let crate_graph = db.crate_graph();
+                        (
+                            param_list,
+                            (0..func.params.len()).map(move |idx| {
+                                item_tree
+                                    .attrs(
+                                        db,
+                                        krate,
+                                        AttrOwner::Param(
+                                            f.id.value,
+                                            Idx::from_raw(RawIdx::from(idx as u32)),
+                                        ),
+                                    )
+                                    .is_cfg_enabled(&crate_graph[krate].cfg_options)
+                            }),
+                        )
+                    });
+                    is_async_fn = data.is_async();
+                    src.map(|it| it.body().map(ast::Expr::from))
+                }
+                DefWithBodyId::ConstId(c) => {
+                    let c = c.lookup(db);
+                    let src = c.source(db);
+                    src.map(|it| it.body())
+                }
+                DefWithBodyId::StaticId(s) => {
+                    let s = s.lookup(db);
+                    let src = s.source(db);
+                    src.map(|it| it.body())
+                }
+                DefWithBodyId::VariantId(v) => {
+                    let s = v.lookup(db);
+                    let src = s.source(db);
+                    src.map(|it| it.expr())
+                }
+                DefWithBodyId::InTypeConstId(c) => c.lookup(db).id.map(|_| c.source(db).expr()),
+            }
+        };
+        let module = def.module(db);
+        let expander = Expander::new(db, file_id, module);
+        let (body, mut source_map) =
+            lower::lower_body(db, def, expander, params, body, module.krate, is_async_fn);
+        source_map.store.shrink_to_fit();
+
+        (Arc::new(body), Arc::new(source_map))
+    }
+
+    pub(crate) fn body_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<Body> {
+        db.body_with_source_map(def).0
+    }
+
+    pub fn pretty_print(
+        &self,
+        db: &dyn DefDatabase,
+        owner: DefWithBodyId,
+        edition: Edition,
+    ) -> String {
+        pretty::print_body_hir(db, self, owner, edition)
+    }
+
+    pub fn pretty_print_expr(
+        &self,
+        db: &dyn DefDatabase,
+        owner: DefWithBodyId,
+        expr: ExprId,
+        edition: Edition,
+    ) -> String {
+        pretty::print_expr_hir(db, self, owner, expr, edition)
+    }
+
+    pub fn pretty_print_pat(
+        &self,
+        db: &dyn DefDatabase,
+        owner: DefWithBodyId,
+        pat: PatId,
+        oneline: bool,
+        edition: Edition,
+    ) -> String {
+        pretty::print_pat_hir(db, self, owner, pat, oneline, edition)
+    }
+}
+
+impl BodySourceMap {
+    pub fn self_param_syntax(&self) -> Option<InFile<SelfParamPtr>> {
+        self.self_param
+    }
+}
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/expr_store/lower.rs
similarity index 91%
rename from crates/hir-def/src/body/lower.rs
rename to crates/hir-def/src/expr_store/lower.rs
index 16c7b5ca00..dfc716eb84 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/expr_store/lower.rs
@@ -29,11 +29,14 @@ use triomphe::Arc;
 
 use crate::{
     attr::Attrs,
-    body::{Body, BodyDiagnostic, BodySourceMap, ExprPtr, HygieneId, LabelPtr, PatPtr},
     builtin_type::BuiltinUint,
     data::adt::StructKind,
     db::DefDatabase,
     expander::Expander,
+    expr_store::{
+        Body, BodySourceMap, ExprPtr, ExpressionStore, ExpressionStoreBuilder,
+        ExpressionStoreDiagnostics, ExpressionStoreSourceMap, HygieneId, LabelPtr, PatPtr,
+    },
     hir::{
         format_args::{
             self, FormatAlignment, FormatArgs, FormatArgsPiece, FormatArgument, FormatArgumentKind,
@@ -55,11 +58,11 @@ use crate::{
 
 type FxIndexSet<K> = indexmap::IndexSet<K, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
 
-pub(super) fn lower(
+pub(super) fn lower_body(
     db: &dyn DefDatabase,
     owner: DefWithBodyId,
     expander: Expander,
-    params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
+    parameters: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
     body: Option<ast::Expr>,
     krate: CrateId,
     is_async_fn: bool,
@@ -75,35 +78,146 @@ pub(super) fn lower(
         };
         Arc::clone(span_map)
     });
-    ExprCollector {
-        db,
-        owner,
-        krate,
-        def_map: expander.module.def_map(db),
-        source_map: BodySourceMap::default(),
-        ast_id_map: db.ast_id_map(expander.current_file_id()),
-        body: Body::default(),
-        expander,
-        current_try_block_label: None,
-        is_lowering_coroutine: false,
-        label_ribs: Vec::new(),
-        current_binding_owner: None,
-        awaitable_context: None,
-        current_span_map: span_map,
-        current_block_legacy_macro_defs_count: FxHashMap::default(),
-    }
-    .collect(params, body, is_async_fn)
+
+    let mut self_param = None;
+    let mut source_map_self_param = None;
+    let mut params = vec![];
+    let mut collector = ExprCollector::new(db, owner, expander, krate, span_map);
+
+    let skip_body = match owner {
+        DefWithBodyId::FunctionId(it) => db.attrs(it.into()),
+        DefWithBodyId::StaticId(it) => db.attrs(it.into()),
+        DefWithBodyId::ConstId(it) => db.attrs(it.into()),
+        DefWithBodyId::InTypeConstId(_) => Attrs::EMPTY,
+        DefWithBodyId::VariantId(it) => db.attrs(it.into()),
+    }
+    .rust_analyzer_tool()
+    .any(|attr| *attr.path() == tool_path![skip]);
+    // If #[rust_analyzer::skip] annotated, only construct enough information for the signature
+    // and skip the body.
+    if skip_body {
+        if let Some((param_list, mut attr_enabled)) = parameters {
+            if let Some(self_param_syn) =
+                param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false))
+            {
+                let is_mutable =
+                    self_param_syn.mut_token().is_some() && self_param_syn.amp_token().is_none();
+                let binding_id: la_arena::Idx<Binding> = collector.alloc_binding(
+                    Name::new_symbol_root(sym::self_.clone()),
+                    BindingAnnotation::new(is_mutable, false),
+                );
+                self_param = Some(binding_id);
+                source_map_self_param =
+                    Some(collector.expander.in_file(AstPtr::new(&self_param_syn)));
+            }
+            params = param_list
+                .params()
+                .zip(attr_enabled)
+                .filter(|(_, enabled)| *enabled)
+                .map(|_| collector.missing_pat())
+                .collect();
+        };
+        let body_expr = collector.missing_expr();
+        return (
+            Body {
+                store: collector.store.finish(),
+                params: params.into_boxed_slice(),
+                self_param,
+                body_expr,
+            },
+            BodySourceMap { self_param: source_map_self_param, store: collector.source_map },
+        );
+    }
+
+    if let Some((param_list, mut attr_enabled)) = parameters {
+        if let Some(self_param_syn) =
+            param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false))
+        {
+            let is_mutable =
+                self_param_syn.mut_token().is_some() && self_param_syn.amp_token().is_none();
+            let binding_id: la_arena::Idx<Binding> = collector.alloc_binding(
+                Name::new_symbol_root(sym::self_.clone()),
+                BindingAnnotation::new(is_mutable, false),
+            );
+            let hygiene = self_param_syn
+                .name()
+                .map(|name| collector.hygiene_id_for(name.syntax().text_range().start()))
+                .unwrap_or(HygieneId::ROOT);
+            if !hygiene.is_root() {
+                collector.store.binding_hygiene.insert(binding_id, hygiene);
+            }
+            self_param = Some(binding_id);
+            source_map_self_param = Some(collector.expander.in_file(AstPtr::new(&self_param_syn)));
+        }
+
+        for (param, _) in param_list.params().zip(attr_enabled).filter(|(_, enabled)| *enabled) {
+            let param_pat = collector.collect_pat_top(param.pat());
+            params.push(param_pat);
+        }
+    };
+
+    let body_expr = collector.collect(
+        body,
+        if is_async_fn {
+            Awaitable::Yes
+        } else {
+            match owner {
+                DefWithBodyId::FunctionId(..) => Awaitable::No("non-async function"),
+                DefWithBodyId::StaticId(..) => Awaitable::No("static"),
+                DefWithBodyId::ConstId(..) | DefWithBodyId::InTypeConstId(..) => {
+                    Awaitable::No("constant")
+                }
+                DefWithBodyId::VariantId(..) => Awaitable::No("enum variant"),
+            }
+        },
+    );
+
+    (
+        Body {
+            store: collector.store.finish(),
+            params: params.into_boxed_slice(),
+            self_param,
+            body_expr,
+        },
+        BodySourceMap { self_param: source_map_self_param, store: collector.source_map },
+    )
 }
 
+#[allow(dead_code)]
+pub(super) fn lower(
+    db: &dyn DefDatabase,
+    owner: ExprStoreOwnerId,
+    expander: Expander,
+    body: Option<ast::Expr>,
+    krate: CrateId,
+) -> (ExpressionStore, ExpressionStoreSourceMap) {
+    // We cannot leave the root span map empty and let any identifier from it be treated as root,
+    // because when inside nested macros `SyntaxContextId`s from the outer macro will be interleaved
+    // with the inner macro, and that will cause confusion because they won't be the same as `ROOT`
+    // even though they should be the same. Also, when the body comes from multiple expansions, their
+    // hygiene is different.
+    let span_map = expander.current_file_id().macro_file().map(|_| {
+        let SpanMap::ExpansionSpanMap(span_map) = expander.span_map(db) else {
+            panic!("in a macro file there should be `ExpansionSpanMap`");
+        };
+        Arc::clone(span_map)
+    });
+    let mut expr_collector = ExprCollector::new(db, owner, expander, krate, span_map);
+    expr_collector.collect(body, Awaitable::No("?"));
+    (expr_collector.store.finish(), expr_collector.source_map)
+}
+
+type ExprStoreOwnerId = DefWithBodyId;
+
 struct ExprCollector<'a> {
     db: &'a dyn DefDatabase,
     expander: Expander,
-    owner: DefWithBodyId,
+    owner: ExprStoreOwnerId,
     def_map: Arc<DefMap>,
     ast_id_map: Arc<AstIdMap>,
     krate: CrateId,
-    body: Body,
-    source_map: BodySourceMap,
+    store: ExpressionStoreBuilder,
+    source_map: ExpressionStoreSourceMap,
 
     is_lowering_coroutine: bool,
 
@@ -157,6 +271,7 @@ impl RibKind {
     }
 }
 
+#[derive(PartialEq, Eq, Debug, Copy, Clone)]
 enum Awaitable {
     Yes,
     No(&'static str),
@@ -180,12 +295,12 @@ impl BindingList {
         let id = *self.map.entry((name, hygiene)).or_insert_with_key(|(name, _)| {
             let id = ec.alloc_binding(name.clone(), mode);
             if !hygiene.is_root() {
-                ec.body.binding_hygiene.insert(id, hygiene);
+                ec.store.binding_hygiene.insert(id, hygiene);
             }
             id
         });
-        if ec.body.bindings[id].mode != mode {
-            ec.body.bindings[id].problems = Some(BindingProblems::BoundInconsistently);
+        if ec.store.bindings[id].mode != mode {
+            ec.store.bindings[id].problems = Some(BindingProblems::BoundInconsistently);
         }
         self.check_is_used(ec, id);
         id
@@ -195,11 +310,11 @@ impl BindingList {
         match self.is_used.get(&id) {
             None => {
                 if self.reject_new {
-                    ec.body.bindings[id].problems = Some(BindingProblems::NotBoundAcrossAll);
+                    ec.store.bindings[id].problems = Some(BindingProblems::NotBoundAcrossAll);
                 }
             }
             Some(true) => {
-                ec.body.bindings[id].problems = Some(BindingProblems::BoundMoreThanOnce);
+                ec.store.bindings[id].problems = Some(BindingProblems::BoundMoreThanOnce);
             }
             Some(false) => {}
         }
@@ -208,93 +323,37 @@ impl BindingList {
 }
 
 impl ExprCollector<'_> {
-    fn collect(
-        mut self,
-        param_list: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
-        body: Option<ast::Expr>,
-        is_async_fn: bool,
-    ) -> (Body, BodySourceMap) {
-        let skip_body = match self.owner {
-            DefWithBodyId::FunctionId(it) => self.db.attrs(it.into()),
-            DefWithBodyId::StaticId(it) => self.db.attrs(it.into()),
-            DefWithBodyId::ConstId(it) => self.db.attrs(it.into()),
-            DefWithBodyId::InTypeConstId(_) => Attrs::EMPTY,
-            DefWithBodyId::VariantId(it) => self.db.attrs(it.into()),
+    fn new(
+        db: &dyn DefDatabase,
+        owner: ExprStoreOwnerId,
+        expander: Expander,
+        krate: CrateId,
+        span_map: Option<Arc<ExpansionSpanMap>>,
+    ) -> ExprCollector<'_> {
+        ExprCollector {
+            db,
+            owner,
+            krate,
+            def_map: expander.module.def_map(db),
+            source_map: ExpressionStoreSourceMap::default(),
+            ast_id_map: db.ast_id_map(expander.current_file_id()),
+            store: ExpressionStoreBuilder::default(),
+            expander,
+            current_try_block_label: None,
+            is_lowering_coroutine: false,
+            label_ribs: Vec::new(),
+            current_binding_owner: None,
+            awaitable_context: None,
+            current_span_map: span_map,
+            current_block_legacy_macro_defs_count: FxHashMap::default(),
         }
-        .rust_analyzer_tool()
-        .any(|attr| *attr.path() == tool_path![skip]);
-        // If #[rust_analyzer::skip] annotated, only construct enough information for the signature
-        // and skip the body.
-        if skip_body {
-            self.body.body_expr = self.missing_expr();
-            if let Some((param_list, mut attr_enabled)) = param_list {
-                if let Some(self_param) =
-                    param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false))
-                {
-                    let is_mutable =
-                        self_param.mut_token().is_some() && self_param.amp_token().is_none();
-                    let binding_id: la_arena::Idx<Binding> = self.alloc_binding(
-                        Name::new_symbol_root(sym::self_.clone()),
-                        BindingAnnotation::new(is_mutable, false),
-                    );
-                    self.body.self_param = Some(binding_id);
-                    self.source_map.self_param =
-                        Some(self.expander.in_file(AstPtr::new(&self_param)));
-                }
-                self.body.params = param_list
-                    .params()
-                    .zip(attr_enabled)
-                    .filter(|(_, enabled)| *enabled)
-                    .map(|_| self.missing_pat())
-                    .collect();
-            };
-            return (self.body, self.source_map);
-        }
-
-        self.awaitable_context.replace(if is_async_fn {
-            Awaitable::Yes
-        } else {
-            match self.owner {
-                DefWithBodyId::FunctionId(..) => Awaitable::No("non-async function"),
-                DefWithBodyId::StaticId(..) => Awaitable::No("static"),
-                DefWithBodyId::ConstId(..) | DefWithBodyId::InTypeConstId(..) => {
-                    Awaitable::No("constant")
-                }
-                DefWithBodyId::VariantId(..) => Awaitable::No("enum variant"),
-            }
-        });
-        if let Some((param_list, mut attr_enabled)) = param_list {
-            let mut params = vec![];
-            if let Some(self_param) =
-                param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false))
-            {
-                let is_mutable =
-                    self_param.mut_token().is_some() && self_param.amp_token().is_none();
-                let binding_id: la_arena::Idx<Binding> = self.alloc_binding(
-                    Name::new_symbol_root(sym::self_.clone()),
-                    BindingAnnotation::new(is_mutable, false),
-                );
-                let hygiene = self_param
-                    .name()
-                    .map(|name| self.hygiene_id_for(name.syntax().text_range().start()))
-                    .unwrap_or(HygieneId::ROOT);
-                if !hygiene.is_root() {
-                    self.body.binding_hygiene.insert(binding_id, hygiene);
-                }
-                self.body.self_param = Some(binding_id);
-                self.source_map.self_param = Some(self.expander.in_file(AstPtr::new(&self_param)));
-            }
+    }
 
-            for (param, _) in param_list.params().zip(attr_enabled).filter(|(_, enabled)| *enabled)
-            {
-                let param_pat = self.collect_pat_top(param.pat());
-                params.push(param_pat);
-            }
-            self.body.params = params.into_boxed_slice();
-        };
-        self.body.body_expr = self.with_label_rib(RibKind::Closure, |this| {
-            if is_async_fn {
-                match body {
+    fn collect(&mut self, expr: Option<ast::Expr>, awaitable: Awaitable) -> ExprId {
+        self.awaitable_context.replace(awaitable);
+        self.with_label_rib(RibKind::Closure, |this| {
+            if awaitable == Awaitable::Yes {
+                match expr {
                     Some(e) => {
                         let syntax_ptr = AstPtr::new(&e);
                         let expr = this.collect_expr(e);
@@ -306,15 +365,13 @@ impl ExprCollector<'_> {
                     None => this.missing_expr(),
                 }
             } else {
-                this.collect_expr_opt(body)
+                this.collect_expr_opt(expr)
             }
-        });
-
-        (self.body, self.source_map)
+        })
     }
 
     fn ctx(&mut self) -> LowerCtx<'_> {
-        self.expander.ctx(self.db, &mut self.body.types, &mut self.source_map.types)
+        self.expander.ctx(self.db, &mut self.store.types, &mut self.source_map.types)
     }
 
     fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
@@ -390,7 +447,7 @@ impl ExprCollector<'_> {
                                 parent: this.owner,
                                 root: inner_expr,
                             });
-                            this.body.exprs[result_expr_id] = Expr::Const(it);
+                            this.store.exprs[result_expr_id] = Expr::Const(it);
                             this.current_binding_owner = prev_binding_owner;
                             result_expr_id
                         })
@@ -480,7 +537,7 @@ impl ExprCollector<'_> {
                     .unwrap_or((Expr::Missing, HygieneId::ROOT));
                 let expr_id = self.alloc_expr(path, syntax_ptr);
                 if !hygiene.is_root() {
-                    self.body.expr_hygiene.insert(expr_id, hygiene);
+                    self.store.ident_hygiene.insert(expr_id.into(), hygiene);
                 }
                 expr_id
             }
@@ -562,10 +619,12 @@ impl ExprCollector<'_> {
             ast::Expr::AwaitExpr(e) => {
                 let expr = self.collect_expr_opt(e.expr());
                 if let Awaitable::No(location) = self.is_lowering_awaitable_block() {
-                    self.source_map.diagnostics.push(BodyDiagnostic::AwaitOutsideOfAsync {
-                        node: InFile::new(self.expander.current_file_id(), AstPtr::new(&e)),
-                        location: location.to_string(),
-                    });
+                    self.source_map.diagnostics.push(
+                        ExpressionStoreDiagnostics::AwaitOutsideOfAsync {
+                            node: InFile::new(self.expander.current_file_id(), AstPtr::new(&e)),
+                            location: location.to_string(),
+                        },
+                    );
                 }
                 self.alloc_expr(Expr::Await { expr }, syntax_ptr)
             }
@@ -646,7 +705,7 @@ impl ExprCollector<'_> {
                 this.is_lowering_coroutine = prev_is_lowering_coroutine;
                 this.current_binding_owner = prev_binding_owner;
                 this.current_try_block_label = prev_try_block_label;
-                this.body.exprs[result_expr_id] = Expr::Closure {
+                this.store.exprs[result_expr_id] = Expr::Closure {
                     args: args.into(),
                     arg_types: arg_types.into(),
                     ret_type,
@@ -752,7 +811,7 @@ impl ExprCollector<'_> {
     }
 
     fn parse_path(&mut self, path: ast::Path) -> Option<Path> {
-        self.expander.parse_path(self.db, path, &mut self.body.types, &mut self.source_map.types)
+        self.expander.parse_path(self.db, path, &mut self.store.types, &mut self.source_map.types)
     }
 
     fn collect_expr_path(&mut self, e: ast::PathExpr) -> Option<(Path, HygieneId)> {
@@ -781,7 +840,7 @@ impl ExprCollector<'_> {
             let src = self.expander.in_file(AstPtr::new(&expr).wrap_left());
             let expr = self.collect_expr(expr);
             // Do not use `alloc_pat_from_expr()` here, it will override the entry in `expr_map`.
-            let id = self.body.pats.alloc(Pat::Expr(expr));
+            let id = self.store.pats.alloc(Pat::Expr(expr));
             self.source_map.pat_map_back.insert(id, src);
             id
         })
@@ -835,7 +894,7 @@ impl ExprCollector<'_> {
                     .unwrap_or((Pat::Missing, HygieneId::ROOT));
                 let pat_id = self.alloc_pat_from_expr(path, syntax_ptr);
                 if !hygiene.is_root() {
-                    self.body.pat_hygiene.insert(pat_id, hygiene);
+                    self.store.ident_hygiene.insert(pat_id.into(), hygiene);
                 }
                 pat_id
             }
@@ -967,7 +1026,7 @@ impl ExprCollector<'_> {
     ) -> ExprId {
         let (id, prev_owner) = self.initialize_binding_owner(syntax_ptr);
         let tmp = job(self);
-        self.body.exprs[id] = mem::replace(&mut self.body.exprs[tmp], Expr::Missing);
+        self.store.exprs[id] = mem::replace(&mut self.store.exprs[tmp], Expr::Missing);
         self.current_binding_owner = prev_owner;
         id
     }
@@ -979,8 +1038,9 @@ impl ExprCollector<'_> {
         let Some(try_from_output) = self.lang_path(LangItem::TryTraitFromOutput) else {
             return self.collect_block(e);
         };
-        let label = self
-            .alloc_label_desugared(Label { name: Name::generate_new_name(self.body.labels.len()) });
+        let label = self.alloc_label_desugared(Label {
+            name: Name::generate_new_name(self.store.labels.len()),
+        });
         let old_label = self.current_try_block_label.replace(label);
 
         let ptr = AstPtr::new(&e).upcast();
@@ -1006,7 +1066,7 @@ impl ExprCollector<'_> {
                 )
             }
         };
-        let Expr::Block { tail, .. } = &mut self.body.exprs[expr_id] else {
+        let Expr::Block { tail, .. } = &mut self.store.exprs[expr_id] else {
             unreachable!("block was lowered to non-block");
         };
         *tail = Some(next_tail);
@@ -1112,7 +1172,7 @@ impl ExprCollector<'_> {
                 this.collect_expr_opt(e.loop_body().map(|it| it.into()))
             }),
         };
-        let iter_name = Name::generate_new_name(self.body.exprs.len());
+        let iter_name = Name::generate_new_name(self.store.exprs.len());
         let iter_expr = self.alloc_expr(Expr::Path(Path::from(iter_name.clone())), syntax_ptr);
         let iter_expr_mut = self.alloc_expr(
             Expr::Ref { expr: iter_expr, rawness: Rawness::Ref, mutability: Mutability::Mut },
@@ -1177,7 +1237,7 @@ impl ExprCollector<'_> {
         let try_branch = self.alloc_expr(Expr::Path(try_branch), syntax_ptr);
         let expr = self
             .alloc_expr(Expr::Call { callee: try_branch, args: Box::new([operand]) }, syntax_ptr);
-        let continue_name = Name::generate_new_name(self.body.bindings.len());
+        let continue_name = Name::generate_new_name(self.store.bindings.len());
         let continue_binding =
             self.alloc_binding(continue_name.clone(), BindingAnnotation::Unannotated);
         let continue_bpat =
@@ -1192,7 +1252,7 @@ impl ExprCollector<'_> {
             guard: None,
             expr: self.alloc_expr(Expr::Path(Path::from(continue_name)), syntax_ptr),
         };
-        let break_name = Name::generate_new_name(self.body.bindings.len());
+        let break_name = Name::generate_new_name(self.store.bindings.len());
         let break_binding = self.alloc_binding(break_name.clone(), BindingAnnotation::Unannotated);
         let break_bpat = self.alloc_pat_desugared(Pat::Bind { id: break_binding, subpat: None });
         self.add_definition_to_binding(break_binding, break_bpat);
@@ -1261,17 +1321,19 @@ impl ExprCollector<'_> {
             Ok(res) => res,
             Err(UnresolvedMacro { path }) => {
                 if record_diagnostics {
-                    self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedMacroCall {
-                        node: InFile::new(outer_file, syntax_ptr),
-                        path,
-                    });
+                    self.source_map.diagnostics.push(
+                        ExpressionStoreDiagnostics::UnresolvedMacroCall {
+                            node: InFile::new(outer_file, syntax_ptr),
+                            path,
+                        },
+                    );
                 }
                 return collector(self, None);
             }
         };
         if record_diagnostics {
             if let Some(err) = res.err {
-                self.source_map.diagnostics.push(BodyDiagnostic::MacroError {
+                self.source_map.diagnostics.push(ExpressionStoreDiagnostics::MacroError {
                     node: InFile::new(outer_file, syntax_ptr),
                     err,
                 });
@@ -1464,7 +1526,7 @@ impl ExprCollector<'_> {
         let (module, def_map) =
             match block_id.map(|block_id| (self.db.block_def_map(block_id), block_id)) {
                 Some((def_map, block_id)) => {
-                    self.body.block_scopes.push(block_id);
+                    self.store.block_scopes.push(block_id);
                     (def_map.module_id(DefMap::ROOT), def_map)
                 }
                 None => (self.expander.module, self.def_map.clone()),
@@ -1621,7 +1683,7 @@ impl ExprCollector<'_> {
                     pats.push(self.collect_pat(rest, binding_list));
                     for (&id, &is_used) in binding_list.is_used.iter() {
                         if !is_used {
-                            self.body.bindings[id].problems =
+                            self.store.bindings[id].problems =
                                 Some(BindingProblems::NotBoundAcrossAll);
                         }
                     }
@@ -1825,7 +1887,7 @@ impl ExprCollector<'_> {
                     return Some(());
                 }
 
-                self.source_map.diagnostics.push(BodyDiagnostic::InactiveCode {
+                self.source_map.diagnostics.push(ExpressionStoreDiagnostics::InactiveCode {
                     node: self.expander.in_file(SyntaxNodePtr::new(owner.syntax())),
                     cfg,
                     opts: self.expander.cfg_options().clone(),
@@ -1853,7 +1915,7 @@ impl ExprCollector<'_> {
     fn resolve_label(
         &self,
         lifetime: Option<ast::Lifetime>,
-    ) -> Result<Option<LabelId>, BodyDiagnostic> {
+    ) -> Result<Option<LabelId>, ExpressionStoreDiagnostics> {
         let Some(lifetime) = lifetime else { return Ok(None) };
         let (mut hygiene_id, mut hygiene_info) = match &self.current_span_map {
             None => (HygieneId::ROOT, None),
@@ -1877,7 +1939,7 @@ impl ExprCollector<'_> {
                         return if self.is_label_valid_from_rib(rib_idx) {
                             Ok(Some(*id))
                         } else {
-                            Err(BodyDiagnostic::UnreachableLabel {
+                            Err(ExpressionStoreDiagnostics::UnreachableLabel {
                                 name,
                                 node: self.expander.in_file(AstPtr::new(&lifetime)),
                             })
@@ -1903,7 +1965,7 @@ impl ExprCollector<'_> {
             }
         }
 
-        Err(BodyDiagnostic::UndeclaredLabel {
+        Err(ExpressionStoreDiagnostics::UndeclaredLabel {
             name,
             node: self.expander.in_file(AstPtr::new(&lifetime)),
         })
@@ -1934,7 +1996,7 @@ impl ExprCollector<'_> {
         f: impl FnOnce(&mut Self) -> T,
     ) -> T {
         self.label_ribs.push(LabelRib::new(RibKind::Normal(
-            self.body[label].name.clone(),
+            self.store.labels[label].name.clone(),
             label,
             hygiene,
         )));
@@ -2023,7 +2085,7 @@ impl ExprCollector<'_> {
                                 );
                         }
                         if !hygiene.is_root() {
-                            self.body.expr_hygiene.insert(expr_id, hygiene);
+                            self.store.ident_hygiene.insert(expr_id.into(), hygiene);
                         }
                         expr_id
                     },
@@ -2417,7 +2479,7 @@ fn pat_literal_to_hir(lit: &ast::LiteralPat) -> Option<(Literal, ast::Literal)>
 impl ExprCollector<'_> {
     fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
         let src = self.expander.in_file(ptr);
-        let id = self.body.exprs.alloc(expr);
+        let id = self.store.exprs.alloc(expr);
         self.source_map.expr_map_back.insert(id, src);
         self.source_map.expr_map.insert(src, id.into());
         id
@@ -2425,11 +2487,11 @@ impl ExprCollector<'_> {
     // FIXME: desugared exprs don't have ptr, that's wrong and should be fixed.
     // Migrate to alloc_expr_desugared_with_ptr and then rename back
     fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
-        self.body.exprs.alloc(expr)
+        self.store.exprs.alloc(expr)
     }
     fn alloc_expr_desugared_with_ptr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
         let src = self.expander.in_file(ptr);
-        let id = self.body.exprs.alloc(expr);
+        let id = self.store.exprs.alloc(expr);
         self.source_map.expr_map_back.insert(id, src);
         // We intentionally don't fill this as it could overwrite a non-desugared entry
         // self.source_map.expr_map.insert(src, id);
@@ -2440,45 +2502,45 @@ impl ExprCollector<'_> {
     }
 
     fn alloc_binding(&mut self, name: Name, mode: BindingAnnotation) -> BindingId {
-        let binding = self.body.bindings.alloc(Binding { name, mode, problems: None });
+        let binding = self.store.bindings.alloc(Binding { name, mode, problems: None });
         if let Some(owner) = self.current_binding_owner {
-            self.body.binding_owners.insert(binding, owner);
+            self.store.binding_owners.insert(binding, owner);
         }
         binding
     }
 
     fn alloc_pat_from_expr(&mut self, pat: Pat, ptr: ExprPtr) -> PatId {
         let src = self.expander.in_file(ptr);
-        let id = self.body.pats.alloc(pat);
+        let id = self.store.pats.alloc(pat);
         self.source_map.expr_map.insert(src, id.into());
         self.source_map.pat_map_back.insert(id, src.map(AstPtr::wrap_left));
         id
     }
     fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
         let src = self.expander.in_file(ptr);
-        let id = self.body.pats.alloc(pat);
+        let id = self.store.pats.alloc(pat);
         self.source_map.pat_map_back.insert(id, src.map(AstPtr::wrap_right));
         self.source_map.pat_map.insert(src, id);
         id
     }
     // FIXME: desugared pats don't have ptr, that's wrong and should be fixed somehow.
     fn alloc_pat_desugared(&mut self, pat: Pat) -> PatId {
-        self.body.pats.alloc(pat)
+        self.store.pats.alloc(pat)
     }
     fn missing_pat(&mut self) -> PatId {
-        self.body.pats.alloc(Pat::Missing)
+        self.store.pats.alloc(Pat::Missing)
     }
 
     fn alloc_label(&mut self, label: Label, ptr: LabelPtr) -> LabelId {
         let src = self.expander.in_file(ptr);
-        let id = self.body.labels.alloc(label);
+        let id = self.store.labels.alloc(label);
         self.source_map.label_map_back.insert(id, src);
         self.source_map.label_map.insert(src, id);
         id
     }
     // FIXME: desugared labels don't have ptr, that's wrong and should be fixed somehow.
     fn alloc_label_desugared(&mut self, label: Label) -> LabelId {
-        self.body.labels.alloc(label)
+        self.store.labels.alloc(label)
     }
 
     fn is_lowering_awaitable_block(&self) -> &Awaitable {
diff --git a/crates/hir-def/src/body/lower/asm.rs b/crates/hir-def/src/expr_store/lower/asm.rs
similarity index 99%
rename from crates/hir-def/src/body/lower/asm.rs
rename to crates/hir-def/src/expr_store/lower/asm.rs
index 68c7173d1e..ab3d104b27 100644
--- a/crates/hir-def/src/body/lower/asm.rs
+++ b/crates/hir-def/src/expr_store/lower/asm.rs
@@ -9,7 +9,7 @@ use syntax::{
 use tt::TextRange;
 
 use crate::{
-    body::lower::{ExprCollector, FxIndexSet},
+    expr_store::lower::{ExprCollector, FxIndexSet},
     hir::{AsmOperand, AsmOptions, Expr, ExprId, InlineAsm, InlineAsmRegOrRegClass},
 };
 
diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/expr_store/pretty.rs
similarity index 95%
rename from crates/hir-def/src/body/pretty.rs
rename to crates/hir-def/src/expr_store/pretty.rs
index 52b91b522a..6a0b1e5197 100644
--- a/crates/hir-def/src/body/pretty.rs
+++ b/crates/hir-def/src/expr_store/pretty.rs
@@ -60,7 +60,7 @@ pub(super) fn print_body_hir(
 
     let mut p = Printer {
         db,
-        body,
+        store: body,
         buf: header,
         indent_level: 0,
         line_format: LineFormat::Newline,
@@ -103,14 +103,14 @@ pub(super) fn print_body_hir(
 
 pub(super) fn print_expr_hir(
     db: &dyn DefDatabase,
-    body: &Body,
+    store: &ExpressionStore,
     _owner: DefWithBodyId,
     expr: ExprId,
     edition: Edition,
 ) -> String {
     let mut p = Printer {
         db,
-        body,
+        store,
         buf: String::new(),
         indent_level: 0,
         line_format: LineFormat::Newline,
@@ -122,7 +122,7 @@ pub(super) fn print_expr_hir(
 
 pub(super) fn print_pat_hir(
     db: &dyn DefDatabase,
-    body: &Body,
+    store: &ExpressionStore,
     _owner: DefWithBodyId,
     pat: PatId,
     oneline: bool,
@@ -130,7 +130,7 @@ pub(super) fn print_pat_hir(
 ) -> String {
     let mut p = Printer {
         db,
-        body,
+        store,
         buf: String::new(),
         indent_level: 0,
         line_format: if oneline { LineFormat::Oneline } else { LineFormat::Newline },
@@ -157,7 +157,7 @@ macro_rules! wln {
 
 struct Printer<'a> {
     db: &'a dyn DefDatabase,
-    body: &'a Body,
+    store: &'a ExpressionStore,
     buf: String,
     indent_level: usize,
     line_format: LineFormat,
@@ -233,7 +233,7 @@ impl Printer<'_> {
     }
 
     fn print_expr(&mut self, expr: ExprId) {
-        let expr = &self.body[expr];
+        let expr = &self.store[expr];
 
         match expr {
             Expr::Missing => w!(self, "�"),
@@ -241,7 +241,7 @@ impl Printer<'_> {
             Expr::InlineAsm(_) => w!(self, "builtin#asm(_)"),
             Expr::OffsetOf(offset_of) => {
                 w!(self, "builtin#offset_of(");
-                self.print_type_ref(offset_of.container, &self.body.types);
+                self.print_type_ref(offset_of.container, &self.store.types);
                 let edition = self.edition;
                 w!(
                     self,
@@ -271,7 +271,7 @@ impl Printer<'_> {
             }
             Expr::Loop { body, label } => {
                 if let Some(lbl) = label {
-                    w!(self, "{}: ", self.body[*lbl].name.display(self.db.upcast(), self.edition));
+                    w!(self, "{}: ", self.store[*lbl].name.display(self.db.upcast(), self.edition));
                 }
                 w!(self, "loop ");
                 self.print_expr(*body);
@@ -295,7 +295,7 @@ impl Printer<'_> {
                 if let Some(args) = generic_args {
                     w!(self, "::<");
                     let edition = self.edition;
-                    print_generic_args(self.db, args, &self.body.types, self, edition).unwrap();
+                    print_generic_args(self.db, args, &self.store.types, self, edition).unwrap();
                     w!(self, ">");
                 }
                 w!(self, "(");
@@ -330,13 +330,13 @@ impl Printer<'_> {
             Expr::Continue { label } => {
                 w!(self, "continue");
                 if let Some(lbl) = label {
-                    w!(self, " {}", self.body[*lbl].name.display(self.db.upcast(), self.edition));
+                    w!(self, " {}", self.store[*lbl].name.display(self.db.upcast(), self.edition));
                 }
             }
             Expr::Break { expr, label } => {
                 w!(self, "break");
                 if let Some(lbl) = label {
-                    w!(self, " {}", self.body[*lbl].name.display(self.db.upcast(), self.edition));
+                    w!(self, " {}", self.store[*lbl].name.display(self.db.upcast(), self.edition));
                 }
                 if let Some(expr) = expr {
                     self.whitespace();
@@ -404,7 +404,7 @@ impl Printer<'_> {
             Expr::Cast { expr, type_ref } => {
                 self.print_expr(*expr);
                 w!(self, " as ");
-                self.print_type_ref(*type_ref, &self.body.types);
+                self.print_type_ref(*type_ref, &self.store.types);
             }
             Expr::Ref { expr, rawness, mutability } => {
                 w!(self, "&");
@@ -492,13 +492,13 @@ impl Printer<'_> {
                     self.print_pat(*pat);
                     if let Some(ty) = ty {
                         w!(self, ": ");
-                        self.print_type_ref(*ty, &self.body.types);
+                        self.print_type_ref(*ty, &self.store.types);
                     }
                 }
                 w!(self, "|");
                 if let Some(ret_ty) = ret_type {
                     w!(self, " -> ");
-                    self.print_type_ref(*ret_ty, &self.body.types);
+                    self.print_type_ref(*ret_ty, &self.store.types);
                 }
                 self.whitespace();
                 self.print_expr(*body);
@@ -534,7 +534,7 @@ impl Printer<'_> {
             Expr::Literal(lit) => self.print_literal(lit),
             Expr::Block { id: _, statements, tail, label } => {
                 let label = label.map(|lbl| {
-                    format!("{}: ", self.body[lbl].name.display(self.db.upcast(), self.edition))
+                    format!("{}: ", self.store[lbl].name.display(self.db.upcast(), self.edition))
                 });
                 self.print_block(label.as_deref(), statements, tail);
             }
@@ -581,7 +581,7 @@ impl Printer<'_> {
     }
 
     fn print_pat(&mut self, pat: PatId) {
-        let pat = &self.body[pat];
+        let pat = &self.store[pat];
 
         match pat {
             Pat::Missing => w!(self, "�"),
@@ -623,9 +623,9 @@ impl Printer<'_> {
                         let field_name = arg.name.display(self.db.upcast(), edition).to_string();
 
                         let mut same_name = false;
-                        if let Pat::Bind { id, subpat: None } = &self.body[arg.pat] {
+                        if let Pat::Bind { id, subpat: None } = &self.store[arg.pat] {
                             if let Binding { name, mode: BindingAnnotation::Unannotated, .. } =
-                                &self.body.bindings[*id]
+                                &self.store.bindings[*id]
                             {
                                 if name.as_str() == field_name {
                                     same_name = true;
@@ -734,7 +734,7 @@ impl Printer<'_> {
                 self.print_pat(*pat);
                 if let Some(ty) = type_ref {
                     w!(self, ": ");
-                    self.print_type_ref(*ty, &self.body.types);
+                    self.print_type_ref(*ty, &self.store.types);
                 }
                 if let Some(init) = initializer {
                     w!(self, " = ");
@@ -799,11 +799,11 @@ impl Printer<'_> {
 
     fn print_path(&mut self, path: &Path) {
         let edition = self.edition;
-        print_path(self.db, path, &self.body.types, self, edition).unwrap();
+        print_path(self.db, path, &self.store.types, self, edition).unwrap();
     }
 
     fn print_binding(&mut self, id: BindingId) {
-        let Binding { name, mode, .. } = &self.body.bindings[id];
+        let Binding { name, mode, .. } = &self.store.bindings[id];
         let mode = match mode {
             BindingAnnotation::Unannotated => "",
             BindingAnnotation::Mutable => "mut ",
diff --git a/crates/hir-def/src/body/scope.rs b/crates/hir-def/src/expr_store/scope.rs
similarity index 90%
rename from crates/hir-def/src/body/scope.rs
rename to crates/hir-def/src/expr_store/scope.rs
index 08af470b96..859a706177 100644
--- a/crates/hir-def/src/body/scope.rs
+++ b/crates/hir-def/src/expr_store/scope.rs
@@ -4,8 +4,8 @@ use la_arena::{Arena, ArenaMap, Idx, IdxRange, RawIdx};
 use triomphe::Arc;
 
 use crate::{
-    body::{Body, HygieneId},
     db::DefDatabase,
+    expr_store::{Body, ExpressionStore, HygieneId},
     hir::{Binding, BindingId, Expr, ExprId, Item, LabelId, Pat, PatId, Statement},
     BlockId, ConstBlockId, DefWithBodyId,
 };
@@ -53,7 +53,7 @@ pub struct ScopeData {
 impl ExprScopes {
     pub(crate) fn expr_scopes_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<ExprScopes> {
         let body = db.body(def);
-        let mut scopes = ExprScopes::new(&body, |const_block| {
+        let mut scopes = ExprScopes::new_body(&body, |const_block| {
             db.lookup_intern_anonymous_const(const_block).root
         });
         scopes.shrink_to_fit();
@@ -104,7 +104,7 @@ fn empty_entries(idx: usize) -> IdxRange<ScopeEntry> {
 }
 
 impl ExprScopes {
-    fn new(
+    fn new_body(
         body: &Body,
         resolve_const_block: impl (Fn(ConstBlockId) -> ExprId) + Copy,
     ) -> ExprScopes {
@@ -179,28 +179,28 @@ impl ExprScopes {
 
     fn add_bindings(
         &mut self,
-        body: &Body,
+        store: &ExpressionStore,
         scope: ScopeId,
         binding: BindingId,
         hygiene: HygieneId,
     ) {
-        let Binding { name, .. } = &body.bindings[binding];
+        let Binding { name, .. } = &store.bindings[binding];
         let entry = self.scope_entries.alloc(ScopeEntry { name: name.clone(), binding, hygiene });
         self.scopes[scope].entries =
             IdxRange::new_inclusive(self.scopes[scope].entries.start()..=entry);
     }
 
-    fn add_pat_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) {
-        let pattern = &body[pat];
+    fn add_pat_bindings(&mut self, store: &ExpressionStore, scope: ScopeId, pat: PatId) {
+        let pattern = &store[pat];
         if let Pat::Bind { id, .. } = *pattern {
-            self.add_bindings(body, scope, id, body.binding_hygiene(id));
+            self.add_bindings(store, scope, id, store.binding_hygiene(id));
         }
 
-        pattern.walk_child_pats(|pat| self.add_pat_bindings(body, scope, pat));
+        pattern.walk_child_pats(|pat| self.add_pat_bindings(store, scope, pat));
     }
 
-    fn add_params_bindings(&mut self, body: &Body, scope: ScopeId, params: &[PatId]) {
-        params.iter().for_each(|pat| self.add_pat_bindings(body, scope, *pat));
+    fn add_params_bindings(&mut self, store: &ExpressionStore, scope: ScopeId, params: &[PatId]) {
+        params.iter().for_each(|pat| self.add_pat_bindings(store, scope, *pat));
     }
 
     fn set_scope(&mut self, node: ExprId, scope: ScopeId) {
@@ -218,7 +218,7 @@ impl ExprScopes {
 fn compute_block_scopes(
     statements: &[Statement],
     tail: Option<ExprId>,
-    body: &Body,
+    store: &ExpressionStore,
     scopes: &mut ExprScopes,
     scope: &mut ScopeId,
     resolve_const_block: impl (Fn(ConstBlockId) -> ExprId) + Copy,
@@ -227,17 +227,17 @@ fn compute_block_scopes(
         match stmt {
             Statement::Let { pat, initializer, else_branch, .. } => {
                 if let Some(expr) = initializer {
-                    compute_expr_scopes(*expr, body, scopes, scope, resolve_const_block);
+                    compute_expr_scopes(*expr, store, scopes, scope, resolve_const_block);
                 }
                 if let Some(expr) = else_branch {
-                    compute_expr_scopes(*expr, body, scopes, scope, resolve_const_block);
+                    compute_expr_scopes(*expr, store, scopes, scope, resolve_const_block);
                 }
 
                 *scope = scopes.new_scope(*scope);
-                scopes.add_pat_bindings(body, *scope, *pat);
+                scopes.add_pat_bindings(store, *scope, *pat);
             }
             Statement::Expr { expr, .. } => {
-                compute_expr_scopes(*expr, body, scopes, scope, resolve_const_block);
+                compute_expr_scopes(*expr, store, scopes, scope, resolve_const_block);
             }
             Statement::Item(Item::MacroDef(macro_id)) => {
                 *scope = scopes.new_macro_def_scope(*scope, macro_id.clone());
@@ -246,32 +246,32 @@ fn compute_block_scopes(
         }
     }
     if let Some(expr) = tail {
-        compute_expr_scopes(expr, body, scopes, scope, resolve_const_block);
+        compute_expr_scopes(expr, store, scopes, scope, resolve_const_block);
     }
 }
 
 fn compute_expr_scopes(
     expr: ExprId,
-    body: &Body,
+    store: &ExpressionStore,
     scopes: &mut ExprScopes,
     scope: &mut ScopeId,
     resolve_const_block: impl (Fn(ConstBlockId) -> ExprId) + Copy,
 ) {
     let make_label =
-        |label: &Option<LabelId>| label.map(|label| (label, body.labels[label].name.clone()));
+        |label: &Option<LabelId>| label.map(|label| (label, store.labels[label].name.clone()));
 
     let compute_expr_scopes = |scopes: &mut ExprScopes, expr: ExprId, scope: &mut ScopeId| {
-        compute_expr_scopes(expr, body, scopes, scope, resolve_const_block)
+        compute_expr_scopes(expr, store, scopes, scope, resolve_const_block)
     };
 
     scopes.set_scope(expr, *scope);
-    match &body[expr] {
+    match &store[expr] {
         Expr::Block { statements, tail, id, label } => {
             let mut scope = scopes.new_block_scope(*scope, *id, make_label(label));
             // Overwrite the old scope for the block expr, so that every block scope can be found
             // via the block itself (important for blocks that only contain items, no expressions).
             scopes.set_scope(expr, scope);
-            compute_block_scopes(statements, *tail, body, scopes, &mut scope, resolve_const_block);
+            compute_block_scopes(statements, *tail, store, scopes, &mut scope, resolve_const_block);
         }
         Expr::Const(id) => {
             let mut scope = scopes.root_scope();
@@ -282,7 +282,7 @@ fn compute_expr_scopes(
             // Overwrite the old scope for the block expr, so that every block scope can be found
             // via the block itself (important for blocks that only contain items, no expressions).
             scopes.set_scope(expr, scope);
-            compute_block_scopes(statements, *tail, body, scopes, &mut scope, resolve_const_block);
+            compute_block_scopes(statements, *tail, store, scopes, &mut scope, resolve_const_block);
         }
         Expr::Loop { body: body_expr, label } => {
             let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
@@ -290,14 +290,14 @@ fn compute_expr_scopes(
         }
         Expr::Closure { args, body: body_expr, .. } => {
             let mut scope = scopes.new_scope(*scope);
-            scopes.add_params_bindings(body, scope, args);
+            scopes.add_params_bindings(store, scope, args);
             compute_expr_scopes(scopes, *body_expr, &mut scope);
         }
         Expr::Match { expr, arms } => {
             compute_expr_scopes(scopes, *expr, scope);
             for arm in arms.iter() {
                 let mut scope = scopes.new_scope(*scope);
-                scopes.add_pat_bindings(body, scope, arm.pat);
+                scopes.add_pat_bindings(store, scope, arm.pat);
                 if let Some(guard) = arm.guard {
                     scope = scopes.new_scope(scope);
                     compute_expr_scopes(scopes, guard, &mut scope);
@@ -316,9 +316,9 @@ fn compute_expr_scopes(
         &Expr::Let { pat, expr } => {
             compute_expr_scopes(scopes, expr, scope);
             *scope = scopes.new_scope(*scope);
-            scopes.add_pat_bindings(body, *scope, pat);
+            scopes.add_pat_bindings(store, *scope, pat);
         }
-        _ => body.walk_child_exprs(expr, |e| compute_expr_scopes(scopes, e, scope)),
+        _ => store.walk_child_exprs(expr, |e| compute_expr_scopes(scopes, e, scope)),
     };
 }
 
diff --git a/crates/hir-def/src/body/tests/block.rs b/crates/hir-def/src/expr_store/tests/block.rs
similarity index 100%
rename from crates/hir-def/src/body/tests/block.rs
rename to crates/hir-def/src/expr_store/tests/block.rs
diff --git a/crates/hir-def/src/generics.rs b/crates/hir-def/src/generics.rs
index 7b3f1d06d2..e2b36da79b 100644
--- a/crates/hir-def/src/generics.rs
+++ b/crates/hir-def/src/generics.rs
@@ -433,7 +433,7 @@ impl GenericParams {
             GenericDefId::TraitAliasId(id) => id_to_generics(db, id, enabled_params),
             GenericDefId::TypeAliasId(id) => id_to_generics(db, id, enabled_params),
             GenericDefId::ImplId(id) => id_to_generics(db, id, enabled_params),
-            GenericDefId::ConstId(_) => (
+            GenericDefId::ConstId(_) | GenericDefId::StaticId(_) => (
                 Arc::new(GenericParams {
                     type_or_consts: Default::default(),
                     lifetimes: Default::default(),
diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs
index 8596346943..0dcddf162b 100644
--- a/crates/hir-def/src/hir.rs
+++ b/crates/hir-def/src/hir.rs
@@ -19,7 +19,7 @@ use std::fmt;
 
 use hir_expand::{name::Name, MacroDefId};
 use intern::Symbol;
-use la_arena::{Idx, RawIdx};
+use la_arena::Idx;
 use rustc_apfloat::ieee::{Half as f16, Quad as f128};
 use syntax::ast;
 use type_ref::TypeRefId;
@@ -37,13 +37,10 @@ pub type BindingId = Idx<Binding>;
 
 pub type ExprId = Idx<Expr>;
 
-/// FIXME: this is a hacky function which should be removed
-pub(crate) fn dummy_expr_id() -> ExprId {
-    ExprId::from_raw(RawIdx::from(u32::MAX))
-}
-
 pub type PatId = Idx<Pat>;
 
+// FIXME: Encode this as a single u32, we won't ever reach all 32 bits especially given these counts
+// are local to the body.
 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
 pub enum ExprOrPatId {
     ExprId(ExprId),
diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs
index c78818c642..c8efd90432 100644
--- a/crates/hir-def/src/lib.rs
+++ b/crates/hir-def/src/lib.rs
@@ -42,7 +42,7 @@ pub mod lang_item;
 
 pub mod hir;
 pub use self::hir::type_ref;
-pub mod body;
+pub mod expr_store;
 pub mod resolver;
 
 pub mod nameres;
@@ -693,6 +693,7 @@ impl TypeOwnerId {
         Some(match self {
             TypeOwnerId::FunctionId(it) => GenericDefId::FunctionId(it),
             TypeOwnerId::ConstId(it) => GenericDefId::ConstId(it),
+            TypeOwnerId::StaticId(it) => GenericDefId::StaticId(it),
             TypeOwnerId::AdtId(it) => GenericDefId::AdtId(it),
             TypeOwnerId::TraitId(it) => GenericDefId::TraitId(it),
             TypeOwnerId::TraitAliasId(it) => GenericDefId::TraitAliasId(it),
@@ -701,7 +702,7 @@ impl TypeOwnerId {
             TypeOwnerId::EnumVariantId(it) => {
                 GenericDefId::AdtId(AdtId::EnumId(it.lookup(db).parent))
             }
-            TypeOwnerId::InTypeConstId(_) | TypeOwnerId::StaticId(_) => return None,
+            TypeOwnerId::InTypeConstId(_) => return None,
         })
     }
 }
@@ -743,6 +744,7 @@ impl From<GenericDefId> for TypeOwnerId {
             GenericDefId::TypeAliasId(it) => it.into(),
             GenericDefId::ImplId(it) => it.into(),
             GenericDefId::ConstId(it) => it.into(),
+            GenericDefId::StaticId(it) => it.into(),
         }
     }
 }
@@ -851,7 +853,7 @@ impl GeneralConstId {
     pub fn generic_def(self, db: &dyn DefDatabase) -> Option<GenericDefId> {
         match self {
             GeneralConstId::ConstId(it) => Some(it.into()),
-            GeneralConstId::StaticId(_) => None,
+            GeneralConstId::StaticId(it) => Some(it.into()),
             GeneralConstId::ConstBlockId(it) => it.lookup(db).parent.as_generic_def_id(db),
             GeneralConstId::InTypeConstId(it) => it.lookup(db).owner.as_generic_def_id(db),
         }
@@ -897,7 +899,7 @@ impl DefWithBodyId {
     pub fn as_generic_def_id(self, db: &dyn DefDatabase) -> Option<GenericDefId> {
         match self {
             DefWithBodyId::FunctionId(f) => Some(f.into()),
-            DefWithBodyId::StaticId(_) => None,
+            DefWithBodyId::StaticId(s) => Some(s.into()),
             DefWithBodyId::ConstId(c) => Some(c.into()),
             DefWithBodyId::VariantId(c) => Some(c.lookup(db).parent.into()),
             // FIXME: stable rust doesn't allow generics in constants, but we should
@@ -922,23 +924,28 @@ impl_from!(FunctionId, ConstId, TypeAliasId for AssocItemId);
 
 #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
 pub enum GenericDefId {
-    FunctionId(FunctionId),
     AdtId(AdtId),
-    TraitId(TraitId),
-    TraitAliasId(TraitAliasId),
-    TypeAliasId(TypeAliasId),
-    ImplId(ImplId),
     // consts can have type parameters from their parents (i.e. associated consts of traits)
     ConstId(ConstId),
+    FunctionId(FunctionId),
+    ImplId(ImplId),
+    // can't actually have generics currently, but they might in the future
+    // More importantly, this completes the set of items that contain type references
+    // which is to be used by the signature expression store in the future.
+    StaticId(StaticId),
+    TraitAliasId(TraitAliasId),
+    TraitId(TraitId),
+    TypeAliasId(TypeAliasId),
 }
 impl_from!(
-    FunctionId,
     AdtId(StructId, EnumId, UnionId),
-    TraitId,
-    TraitAliasId,
-    TypeAliasId,
+    ConstId,
+    FunctionId,
     ImplId,
-    ConstId
+    StaticId,
+    TraitAliasId,
+    TraitId,
+    TypeAliasId
     for GenericDefId
 );
 
@@ -969,6 +976,7 @@ impl GenericDefId {
             GenericDefId::TraitAliasId(it) => file_id_and_params_of_item_loc(db, it),
             GenericDefId::ImplId(it) => file_id_and_params_of_item_loc(db, it),
             GenericDefId::ConstId(it) => (it.lookup(db).id.file_id(), None),
+            GenericDefId::StaticId(it) => (it.lookup(db).id.file_id(), None),
         }
     }
 
@@ -1350,6 +1358,7 @@ impl HasModule for GenericDefId {
             GenericDefId::TypeAliasId(it) => it.module(db),
             GenericDefId::ImplId(it) => it.module(db),
             GenericDefId::ConstId(it) => it.module(db),
+            GenericDefId::StaticId(it) => it.module(db),
         }
     }
 }
diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs
index 8c556d8a8c..7e13ae2f7a 100644
--- a/crates/hir-def/src/resolver.rs
+++ b/crates/hir-def/src/resolver.rs
@@ -10,13 +10,13 @@ use smallvec::{smallvec, SmallVec};
 use triomphe::Arc;
 
 use crate::{
-    body::{
-        scope::{ExprScopes, ScopeId},
-        HygieneId,
-    },
     builtin_type::BuiltinType,
     data::ExternCrateDeclData,
     db::DefDatabase,
+    expr_store::{
+        scope::{ExprScopes, ScopeId},
+        HygieneId,
+    },
     generics::{GenericParams, TypeOrConstParamData},
     hir::{BindingId, ExprId, LabelId},
     item_scope::{BuiltinShadowMode, ImportOrExternCrate, ImportOrGlob, BUILTIN_SCOPE},
@@ -1264,6 +1264,7 @@ impl HasResolver for GenericDefId {
             GenericDefId::TypeAliasId(inner) => inner.resolver(db),
             GenericDefId::ImplId(inner) => inner.resolver(db),
             GenericDefId::ConstId(inner) => inner.resolver(db),
+            GenericDefId::StaticId(inner) => inner.resolver(db),
         }
     }
 }
diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs
index 142766c039..836a236444 100644
--- a/crates/hir-ty/src/consteval.rs
+++ b/crates/hir-ty/src/consteval.rs
@@ -3,7 +3,7 @@
 use base_db::{ra_salsa::Cycle, CrateId};
 use chalk_ir::{cast::Cast, BoundVar, DebruijnIndex};
 use hir_def::{
-    body::{Body, HygieneId},
+    expr_store::{Body, HygieneId},
     hir::{Expr, ExprId},
     path::Path,
     resolver::{Resolver, ValueNs},
diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs
index 7f9f0c0de1..0b5f131924 100644
--- a/crates/hir-ty/src/diagnostics/expr.rs
+++ b/crates/hir-ty/src/diagnostics/expr.rs
@@ -36,7 +36,7 @@ use crate::{
 };
 
 pub(crate) use hir_def::{
-    body::Body,
+    expr_store::Body,
     hir::{Expr, ExprId, MatchArm, Pat, PatId, Statement},
     LocalFieldId, VariantId,
 };
diff --git a/crates/hir-ty/src/diagnostics/match_check.rs b/crates/hir-ty/src/diagnostics/match_check.rs
index c5d8c95661..b0f9fc53e2 100644
--- a/crates/hir-ty/src/diagnostics/match_check.rs
+++ b/crates/hir-ty/src/diagnostics/match_check.rs
@@ -11,7 +11,8 @@ pub(crate) mod pat_analysis;
 
 use chalk_ir::Mutability;
 use hir_def::{
-    body::Body, data::adt::VariantData, hir::PatId, AdtId, EnumVariantId, LocalFieldId, VariantId,
+    data::adt::VariantData, expr_store::Body, hir::PatId, AdtId, EnumVariantId, LocalFieldId,
+    VariantId,
 };
 use hir_expand::name::Name;
 use span::Edition;
diff --git a/crates/hir-ty/src/diagnostics/unsafe_check.rs b/crates/hir-ty/src/diagnostics/unsafe_check.rs
index 6bba83fac9..e9d3797bc8 100644
--- a/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -5,7 +5,7 @@ use std::mem;
 
 use either::Either;
 use hir_def::{
-    body::Body,
+    expr_store::Body,
     hir::{Expr, ExprId, ExprOrPatId, Pat, PatId, Statement, UnaryOp},
     path::Path,
     resolver::{HasResolver, ResolveValueResult, Resolver, ValueNs},
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index ae8fbe2ce6..d960aaf99f 100644
--- a/crates/hir-ty/src/display.rs
+++ b/crates/hir-ty/src/display.rs
@@ -34,7 +34,6 @@ use rustc_apfloat::{
     ieee::{Half as f16, Quad as f128},
     Float,
 };
-use rustc_hash::FxHashSet;
 use smallvec::SmallVec;
 use span::Edition;
 use stdx::never;
@@ -88,35 +87,6 @@ pub struct HirFormatter<'a> {
     omit_verbose_types: bool,
     closure_style: ClosureStyle,
     display_target: DisplayTarget,
-    bounds_formatting_ctx: BoundsFormattingCtx,
-}
-
-#[derive(Default)]
-enum BoundsFormattingCtx {
-    Entered {
-        /// We can have recursive bounds like the following case:
-        /// ```rust
-        /// where
-        ///     T: Foo,
-        ///     T::FooAssoc: Baz<<T::FooAssoc as Bar>::BarAssoc> + Bar
-        /// ```
-        /// So, record the projection types met while formatting bounds and
-        //. prevent recursing into their bounds to avoid infinite loops.
-        projection_tys_met: FxHashSet<ProjectionTy>,
-    },
-    #[default]
-    Exited,
-}
-
-impl BoundsFormattingCtx {
-    fn contains(&mut self, proj: &ProjectionTy) -> bool {
-        match self {
-            BoundsFormattingCtx::Entered { projection_tys_met } => {
-                projection_tys_met.contains(proj)
-            }
-            BoundsFormattingCtx::Exited => false,
-        }
-    }
 }
 
 impl HirFormatter<'_> {
@@ -127,30 +97,6 @@ impl HirFormatter<'_> {
     fn end_location_link(&mut self) {
         self.fmt.end_location_link();
     }
-
-    fn format_bounds_with<T, F: FnOnce(&mut Self) -> T>(
-        &mut self,
-        target: ProjectionTy,
-        format_bounds: F,
-    ) -> T {
-        match self.bounds_formatting_ctx {
-            BoundsFormattingCtx::Entered { ref mut projection_tys_met } => {
-                projection_tys_met.insert(target);
-                format_bounds(self)
-            }
-            BoundsFormattingCtx::Exited => {
-                let mut projection_tys_met = FxHashSet::default();
-                projection_tys_met.insert(target);
-                self.bounds_formatting_ctx = BoundsFormattingCtx::Entered { projection_tys_met };
-                let res = format_bounds(self);
-                // Since we want to prevent only the infinite recursions in bounds formatting
-                // and do not want to skip formatting of other separate bounds, clear context
-                // when exiting the formatting of outermost bounds
-                self.bounds_formatting_ctx = BoundsFormattingCtx::Exited;
-                res
-            }
-        }
-    }
 }
 
 pub trait HirDisplay {
@@ -274,7 +220,6 @@ pub trait HirDisplay {
             closure_style: ClosureStyle::ImplFn,
             display_target: DisplayTarget::SourceCode { module_id, allow_opaque },
             show_container_bounds: false,
-            bounds_formatting_ctx: Default::default(),
         }) {
             Ok(()) => {}
             Err(HirDisplayError::FmtError) => panic!("Writing to String can't fail!"),
@@ -482,7 +427,6 @@ impl<T: HirDisplay> HirDisplayWrapper<'_, T> {
             display_target: self.display_target,
             closure_style: self.closure_style,
             show_container_bounds: self.show_container_bounds,
-            bounds_formatting_ctx: Default::default(),
         })
     }
 
@@ -535,46 +479,42 @@ impl HirDisplay for ProjectionTy {
         // `<Param as Trait>::Assoc`
         if !f.display_target.is_source_code() {
             if let TyKind::Placeholder(idx) = self_ty.kind(Interner) {
-                if !f.bounds_formatting_ctx.contains(self) {
-                    let db = f.db;
-                    let id = from_placeholder_idx(db, *idx);
-                    let generics = generics(db.upcast(), id.parent);
-
-                    let substs = generics.placeholder_subst(db);
-                    let bounds = db
-                        .generic_predicates(id.parent)
-                        .iter()
-                        .map(|pred| pred.clone().substitute(Interner, &substs))
-                        .filter(|wc| match wc.skip_binders() {
-                            WhereClause::Implemented(tr) => {
-                                matches!(
-                                    tr.self_type_parameter(Interner).kind(Interner),
-                                    TyKind::Alias(_)
-                                )
-                            }
-                            WhereClause::TypeOutlives(t) => {
-                                matches!(t.ty.kind(Interner), TyKind::Alias(_))
+                let db = f.db;
+                let id = from_placeholder_idx(db, *idx);
+                let generics = generics(db.upcast(), id.parent);
+
+                let substs = generics.placeholder_subst(db);
+                let bounds = db
+                    .generic_predicates(id.parent)
+                    .iter()
+                    .map(|pred| pred.clone().substitute(Interner, &substs))
+                    .filter(|wc| match wc.skip_binders() {
+                        WhereClause::Implemented(tr) => {
+                            match tr.self_type_parameter(Interner).kind(Interner) {
+                                TyKind::Alias(AliasTy::Projection(proj)) => proj == self,
+                                _ => false,
                             }
-                            // We shouldn't be here if these exist
-                            WhereClause::AliasEq(_) => false,
-                            WhereClause::LifetimeOutlives(_) => false,
-                        })
-                        .collect::<Vec<_>>();
-                    if !bounds.is_empty() {
-                        return f.format_bounds_with(self.clone(), |f| {
-                            write_bounds_like_dyn_trait_with_prefix(
-                                f,
-                                "impl",
-                                Either::Left(
-                                    &TyKind::Alias(AliasTy::Projection(self.clone()))
-                                        .intern(Interner),
-                                ),
-                                &bounds,
-                                SizedByDefault::NotSized,
-                            )
-                        });
-                    }
-                }
+                        }
+                        WhereClause::TypeOutlives(t) => match t.ty.kind(Interner) {
+                            TyKind::Alias(AliasTy::Projection(proj)) => proj == self,
+                            _ => false,
+                        },
+                        // We shouldn't be here if these exist
+                        WhereClause::AliasEq(_) => false,
+                        WhereClause::LifetimeOutlives(_) => false,
+                    })
+                    .collect::<Vec<_>>();
+                if !bounds.is_empty() {
+                    return write_bounds_like_dyn_trait_with_prefix(
+                        f,
+                        "impl",
+                        Either::Left(
+                            &TyKind::Alias(AliasTy::Projection(self.clone())).intern(Interner),
+                        ),
+                        &bounds,
+                        SizedByDefault::NotSized,
+                    );
+                };
             }
         }
 
diff --git a/crates/hir-ty/src/generics.rs b/crates/hir-ty/src/generics.rs
index abbf2a4f2e..18cf6e5ce3 100644
--- a/crates/hir-ty/src/generics.rs
+++ b/crates/hir-ty/src/generics.rs
@@ -262,7 +262,8 @@ fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option<Generic
         GenericDefId::FunctionId(it) => it.lookup(db).container,
         GenericDefId::TypeAliasId(it) => it.lookup(db).container,
         GenericDefId::ConstId(it) => it.lookup(db).container,
-        GenericDefId::AdtId(_)
+        GenericDefId::StaticId(_)
+        | GenericDefId::AdtId(_)
         | GenericDefId::TraitId(_)
         | GenericDefId::ImplId(_)
         | GenericDefId::TraitAliasId(_) => return None,
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 25bb3a76de..3c258e3c4c 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -34,9 +34,9 @@ use chalk_ir::{
 };
 use either::Either;
 use hir_def::{
-    body::{Body, HygieneId},
     builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
     data::{ConstData, StaticData},
+    expr_store::{Body, HygieneId},
     hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId},
     lang_item::{LangItem, LangItemTarget},
     layout::Integer,
diff --git a/crates/hir-ty/src/infer/diagnostics.rs b/crates/hir-ty/src/infer/diagnostics.rs
index 032dc37899..b85378531a 100644
--- a/crates/hir-ty/src/infer/diagnostics.rs
+++ b/crates/hir-ty/src/infer/diagnostics.rs
@@ -5,7 +5,7 @@
 use std::cell::RefCell;
 use std::ops::{Deref, DerefMut};
 
-use hir_def::body::HygieneId;
+use hir_def::expr_store::HygieneId;
 use hir_def::hir::ExprOrPatId;
 use hir_def::path::{Path, PathSegment, PathSegments};
 use hir_def::resolver::{ResolveValueResult, Resolver, TypeNs};
diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs
index 00398f019d..ca8d5bae5e 100644
--- a/crates/hir-ty/src/infer/pat.rs
+++ b/crates/hir-ty/src/infer/pat.rs
@@ -3,7 +3,7 @@
 use std::iter::repeat_with;
 
 use hir_def::{
-    body::Body,
+    expr_store::Body,
     hir::{Binding, BindingAnnotation, BindingId, Expr, ExprId, Literal, Pat, PatId},
     path::Path,
 };
@@ -528,7 +528,7 @@ impl InferenceContext<'_> {
         self.infer_expr(expr, &Expectation::has_type(expected.clone()), ExprIsRead::Yes)
     }
 
-    fn is_non_ref_pat(&mut self, body: &hir_def::body::Body, pat: PatId) -> bool {
+    fn is_non_ref_pat(&mut self, body: &hir_def::expr_store::Body, pat: PatId) -> bool {
         match &body[pat] {
             Pat::Tuple { .. }
             | Pat::TupleStruct { .. }
diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs
index 73bcefaf2a..36ec60a7a2 100644
--- a/crates/hir-ty/src/infer/path.rs
+++ b/crates/hir-ty/src/infer/path.rs
@@ -86,10 +86,9 @@ impl InferenceContext<'_> {
             }
         };
 
-        let generic_def_id = value_def.to_generic_def_id(self.db);
-        let Some(generic_def) = generic_def_id else {
-            // `value_def` is the kind of item that can never be generic (i.e. statics, at least
-            // currently). We can just skip the binders to get its type.
+        let generic_def = value_def.to_generic_def_id(self.db);
+        if let GenericDefId::StaticId(_) = generic_def {
+            // `Static` is the kind of item that can never be generic currently. We can just skip the binders to get its type.
             let (ty, binders) = self.db.value_ty(value_def)?.into_value_and_skipped_binders();
             stdx::always!(binders.is_empty(Interner), "non-empty binders for non-generic def",);
             return Some(ValuePathResolution::NonGeneric(ty));
@@ -122,7 +121,7 @@ impl InferenceContext<'_> {
         }
 
         let parent_substs = self_subst.or_else(|| {
-            let generics = generics(self.db.upcast(), generic_def_id?);
+            let generics = generics(self.db.upcast(), generic_def);
             let parent_params_len = generics.parent_generics()?.len();
             let parent_args = &substs[substs.len() - parent_params_len..];
             Some(Substitution::from_iter(Interner, parent_args))
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index 432b8f4d94..db13e1fd35 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -23,10 +23,10 @@ use chalk_ir::{
 
 use either::Either;
 use hir_def::{
-    body::HygieneId,
     builtin_type::BuiltinType,
     data::{adt::StructKind, TraitFlags},
     expander::Expander,
+    expr_store::HygieneId,
     generics::{
         GenericParamDataRef, TypeOrConstParamData, TypeParamProvenance, WherePredicate,
         WherePredicateTypeTarget,
@@ -2471,14 +2471,14 @@ pub enum ValueTyDefId {
 impl_from!(FunctionId, StructId, UnionId, EnumVariantId, ConstId, StaticId for ValueTyDefId);
 
 impl ValueTyDefId {
-    pub(crate) fn to_generic_def_id(self, db: &dyn HirDatabase) -> Option<GenericDefId> {
+    pub(crate) fn to_generic_def_id(self, db: &dyn HirDatabase) -> GenericDefId {
         match self {
-            Self::FunctionId(id) => Some(id.into()),
-            Self::StructId(id) => Some(id.into()),
-            Self::UnionId(id) => Some(id.into()),
-            Self::EnumVariantId(var) => Some(var.lookup(db.upcast()).parent.into()),
-            Self::ConstId(id) => Some(id.into()),
-            Self::StaticId(_) => None,
+            Self::FunctionId(id) => id.into(),
+            Self::StructId(id) => id.into(),
+            Self::UnionId(id) => id.into(),
+            Self::EnumVariantId(var) => var.lookup(db.upcast()).parent.into(),
+            Self::ConstId(id) => id.into(),
+            Self::StaticId(id) => id.into(),
         }
     }
 }
diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs
index 59c583afb2..84d8950b1a 100644
--- a/crates/hir-ty/src/mir.rs
+++ b/crates/hir-ty/src/mir.rs
@@ -16,7 +16,7 @@ use base_db::CrateId;
 use chalk_ir::Mutability;
 use either::Either;
 use hir_def::{
-    body::Body,
+    expr_store::Body,
     hir::{BindingAnnotation, BindingId, Expr, ExprId, Ordering, PatId},
     DefWithBodyId, FieldId, StaticId, TupleFieldId, UnionId, VariantId,
 };
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index dcae6877ba..8e4c4db102 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -6,9 +6,9 @@ use base_db::CrateId;
 use chalk_ir::{cast::Cast, Mutability};
 use either::Either;
 use hir_def::{
-    body::HygieneId,
     builtin_type::BuiltinType,
     data::adt::{StructFlags, VariantData},
+    expr_store::HygieneId,
     lang_item::LangItem,
     layout::{TagEncoding, Variants},
     resolver::{HasResolver, TypeNs, ValueNs},
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index 1d1044df6e..cc6ed122af 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -5,8 +5,8 @@ use std::{fmt::Write, iter, mem};
 use base_db::ra_salsa::Cycle;
 use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind};
 use hir_def::{
-    body::{Body, HygieneId},
     data::adt::{StructKind, VariantData},
+    expr_store::{Body, HygieneId},
     hir::{
         ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal,
         LiteralOrConst, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField,
diff --git a/crates/hir-ty/src/mir/pretty.rs b/crates/hir-ty/src/mir/pretty.rs
index 06765a104c..2a26101ac4 100644
--- a/crates/hir-ty/src/mir/pretty.rs
+++ b/crates/hir-ty/src/mir/pretty.rs
@@ -6,7 +6,7 @@ use std::{
 };
 
 use either::Either;
-use hir_def::{body::Body, hir::BindingId};
+use hir_def::{expr_store::Body, hir::BindingId};
 use hir_expand::{name::Name, Lookup};
 use la_arena::ArenaMap;
 use span::Edition;
diff --git a/crates/hir-ty/src/variance.rs b/crates/hir-ty/src/variance.rs
index afd163fbd9..3a22158ce6 100644
--- a/crates/hir-ty/src/variance.rs
+++ b/crates/hir-ty/src/variance.rs
@@ -1028,6 +1028,7 @@ struct FixedPoint<T, U, V>(&'static FixedPoint<(), T, U>, V);
                         }
                         GenericDefId::ImplId(_) => return None,
                         GenericDefId::ConstId(_) => return None,
+                        GenericDefId::StaticId(_) => return None,
                     },
                 ))
             })
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index fc77d1889c..066a322e32 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -411,7 +411,7 @@ impl AnyDiagnostic {
     pub(crate) fn body_validation_diagnostic(
         db: &dyn HirDatabase,
         diagnostic: BodyValidationDiagnostic,
-        source_map: &hir_def::body::BodySourceMap,
+        source_map: &hir_def::expr_store::BodySourceMap,
     ) -> Option<AnyDiagnostic> {
         match diagnostic {
             BodyValidationDiagnostic::RecordMissingFields { record, variant, missed_fields } => {
@@ -547,7 +547,7 @@ impl AnyDiagnostic {
         def: DefWithBodyId,
         d: &InferenceDiagnostic,
         outer_types_source_map: &TypesSourceMap,
-        source_map: &hir_def::body::BodySourceMap,
+        source_map: &hir_def::expr_store::BodySourceMap,
     ) -> Option<AnyDiagnostic> {
         let expr_syntax = |expr| {
             source_map.expr_syntax(expr).inspect_err(|_| stdx::never!("synthetic syntax")).ok()
diff --git a/crates/hir/src/from_id.rs b/crates/hir/src/from_id.rs
index 2ad39817b2..537401afdc 100644
--- a/crates/hir/src/from_id.rs
+++ b/crates/hir/src/from_id.rs
@@ -183,6 +183,7 @@ impl From<GenericDef> for GenericDefId {
             GenericDef::TypeAlias(it) => GenericDefId::TypeAliasId(it.id),
             GenericDef::Impl(it) => GenericDefId::ImplId(it.id),
             GenericDef::Const(it) => GenericDefId::ConstId(it.id),
+            GenericDef::Static(it) => GenericDefId::StaticId(it.id),
         }
     }
 }
@@ -197,6 +198,7 @@ impl From<GenericDefId> for GenericDef {
             GenericDefId::TypeAliasId(it) => GenericDef::TypeAlias(it.into()),
             GenericDefId::ImplId(it) => GenericDef::Impl(it.into()),
             GenericDefId::ConstId(it) => GenericDef::Const(it.into()),
+            GenericDefId::StaticId(it) => GenericDef::Static(it.into()),
         }
     }
 }
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 0cbc75726b..aceff2b9ae 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -42,8 +42,8 @@ use arrayvec::ArrayVec;
 use base_db::{CrateDisplayName, CrateId, CrateOrigin};
 use either::Either;
 use hir_def::{
-    body::BodyDiagnostic,
     data::{adt::VariantData, TraitFlags},
+    expr_store::ExpressionStoreDiagnostics,
     generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance},
     hir::{BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, LabelId, Pat},
     item_tree::{AttrOwner, FieldParent, ItemTreeFieldId, ItemTreeNode},
@@ -1892,10 +1892,10 @@ impl DefWithBody {
 
         for diag in source_map.diagnostics() {
             acc.push(match diag {
-                BodyDiagnostic::InactiveCode { node, cfg, opts } => {
+                ExpressionStoreDiagnostics::InactiveCode { node, cfg, opts } => {
                     InactiveCode { node: *node, cfg: cfg.clone(), opts: opts.clone() }.into()
                 }
-                BodyDiagnostic::MacroError { node, err } => {
+                ExpressionStoreDiagnostics::MacroError { node, err } => {
                     let RenderedExpandError { message, error, kind } =
                         err.render_to_string(db.upcast());
 
@@ -1919,20 +1919,22 @@ impl DefWithBody {
                     }
                     .into()
                 }
-                BodyDiagnostic::UnresolvedMacroCall { node, path } => UnresolvedMacroCall {
-                    macro_call: (*node).map(|ast_ptr| ast_ptr.into()),
-                    precise_location: None,
-                    path: path.clone(),
-                    is_bang: true,
+                ExpressionStoreDiagnostics::UnresolvedMacroCall { node, path } => {
+                    UnresolvedMacroCall {
+                        macro_call: (*node).map(|ast_ptr| ast_ptr.into()),
+                        precise_location: None,
+                        path: path.clone(),
+                        is_bang: true,
+                    }
+                    .into()
                 }
-                .into(),
-                BodyDiagnostic::AwaitOutsideOfAsync { node, location } => {
+                ExpressionStoreDiagnostics::AwaitOutsideOfAsync { node, location } => {
                     AwaitOutsideOfAsync { node: *node, location: location.clone() }.into()
                 }
-                BodyDiagnostic::UnreachableLabel { node, name } => {
+                ExpressionStoreDiagnostics::UnreachableLabel { node, name } => {
                     UnreachableLabel { node: *node, name: name.clone() }.into()
                 }
-                BodyDiagnostic::UndeclaredLabel { node, name } => {
+                ExpressionStoreDiagnostics::UndeclaredLabel { node, name } => {
                     UndeclaredLabel { node: *node, name: name.clone() }.into()
                 }
             });
@@ -3453,6 +3455,7 @@ pub enum GenericDef {
     Impl(Impl),
     // consts can have type parameters from their parents (i.e. associated consts of traits)
     Const(Const),
+    Static(Static),
 }
 impl_from!(
     Function,
@@ -3461,7 +3464,8 @@ impl_from!(
     TraitAlias,
     TypeAlias,
     Impl,
-    Const
+    Const,
+    Static
     for GenericDef
 );
 
@@ -3511,6 +3515,7 @@ impl GenericDef {
             GenericDef::TypeAlias(it) => it.id.into(),
             GenericDef::Impl(it) => it.id.into(),
             GenericDef::Const(it) => it.id.into(),
+            GenericDef::Static(it) => it.id.into(),
         }
     }
 
@@ -3568,6 +3573,7 @@ impl GenericDef {
                     item_tree_source_maps.impl_(id.value).generics()
                 }
                 GenericDefId::ConstId(_) => return,
+                GenericDefId::StaticId(_) => return,
             },
         };
 
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index b699ccde41..496b6566bd 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -14,7 +14,7 @@ use crate::{
 };
 use either::Either;
 use hir_def::{
-    body::{
+    expr_store::{
         scope::{ExprScopes, ScopeId},
         Body, BodySourceMap, HygieneId,
     },
diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs
index a22e7b272e..88f893e42a 100644
--- a/crates/ide-completion/src/completions.rs
+++ b/crates/ide-completion/src/completions.rs
@@ -188,6 +188,9 @@ impl Completions {
         resolution: hir::ScopeDef,
         doc_aliases: Vec<syntax::SmolStr>,
     ) {
+        if !ctx.check_stability(resolution.attrs(ctx.db).as_deref()) {
+            return;
+        }
         let is_private_editable = match ctx.def_is_visible(&resolution) {
             Visible::Yes => false,
             Visible::Editable => true,
@@ -213,6 +216,9 @@ impl Completions {
         local_name: hir::Name,
         resolution: hir::ScopeDef,
     ) {
+        if !ctx.check_stability(resolution.attrs(ctx.db).as_deref()) {
+            return;
+        }
         let is_private_editable = match ctx.def_is_visible(&resolution) {
             Visible::Yes => false,
             Visible::Editable => true,
@@ -235,7 +241,7 @@ impl Completions {
         path_ctx: &PathCompletionCtx,
         e: hir::Enum,
     ) {
-        if !ctx.check_stability_and_hidden(e) {
+        if !ctx.check_stability(Some(&e.attrs(ctx.db))) {
             return;
         }
         e.variants(ctx.db)
@@ -251,6 +257,9 @@ impl Completions {
         local_name: hir::Name,
         doc_aliases: Vec<syntax::SmolStr>,
     ) {
+        if !ctx.check_stability(Some(&module.attrs(ctx.db))) {
+            return;
+        }
         self.add_path_resolution(
             ctx,
             path_ctx,
@@ -267,6 +276,9 @@ impl Completions {
         mac: hir::Macro,
         local_name: hir::Name,
     ) {
+        if !ctx.check_stability(Some(&mac.attrs(ctx.db))) {
+            return;
+        }
         let is_private_editable = match ctx.is_visible(&mac) {
             Visible::Yes => false,
             Visible::Editable => true,
@@ -290,6 +302,9 @@ impl Completions {
         func: hir::Function,
         local_name: Option<hir::Name>,
     ) {
+        if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
+            return;
+        }
         let is_private_editable = match ctx.is_visible(&func) {
             Visible::Yes => false,
             Visible::Editable => true,
@@ -317,6 +332,9 @@ impl Completions {
         receiver: Option<SmolStr>,
         local_name: Option<hir::Name>,
     ) {
+        if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
+            return;
+        }
         let is_private_editable = match ctx.is_visible(&func) {
             Visible::Yes => false,
             Visible::Editable => true,
@@ -344,6 +362,9 @@ impl Completions {
         func: hir::Function,
         import: LocatedImport,
     ) {
+        if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
+            return;
+        }
         let is_private_editable = match ctx.is_visible(&func) {
             Visible::Yes => false,
             Visible::Editable => true,
@@ -366,6 +387,9 @@ impl Completions {
     }
 
     pub(crate) fn add_const(&mut self, ctx: &CompletionContext<'_>, konst: hir::Const) {
+        if !ctx.check_stability(Some(&konst.attrs(ctx.db))) {
+            return;
+        }
         let is_private_editable = match ctx.is_visible(&konst) {
             Visible::Yes => false,
             Visible::Editable => true,
@@ -382,6 +406,9 @@ impl Completions {
         ctx: &CompletionContext<'_>,
         type_alias: hir::TypeAlias,
     ) {
+        if !ctx.check_stability(Some(&type_alias.attrs(ctx.db))) {
+            return;
+        }
         let is_private_editable = match ctx.is_visible(&type_alias) {
             Visible::Yes => false,
             Visible::Editable => true,
@@ -411,7 +438,7 @@ impl Completions {
         variant: hir::Variant,
         path: hir::ModPath,
     ) {
-        if !ctx.check_stability_and_hidden(variant) {
+        if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
             return;
         }
         if let Some(builder) =
@@ -428,7 +455,7 @@ impl Completions {
         variant: hir::Variant,
         local_name: Option<hir::Name>,
     ) {
-        if !ctx.check_stability_and_hidden(variant) {
+        if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
             return;
         }
         if let PathCompletionCtx { kind: PathKind::Pat { pat_ctx }, .. } = path_ctx {
@@ -452,6 +479,9 @@ impl Completions {
         field: hir::Field,
         ty: &hir::Type,
     ) {
+        if !ctx.check_stability(Some(&field.attrs(ctx.db))) {
+            return;
+        }
         let is_private_editable = match ctx.is_visible(&field) {
             Visible::Yes => false,
             Visible::Editable => true,
@@ -476,18 +506,12 @@ impl Completions {
         path: Option<hir::ModPath>,
         local_name: Option<hir::Name>,
     ) {
-        let is_private_editable = match ctx.is_visible(&strukt) {
-            Visible::Yes => false,
-            Visible::Editable => true,
-            Visible::No => return,
-        };
-        if let Some(builder) = render_struct_literal(
-            RenderContext::new(ctx).private_editable(is_private_editable),
-            path_ctx,
-            strukt,
-            path,
-            local_name,
-        ) {
+        if !ctx.check_stability(Some(&strukt.attrs(ctx.db))) {
+            return;
+        }
+        if let Some(builder) =
+            render_struct_literal(RenderContext::new(ctx), path_ctx, strukt, path, local_name)
+        {
             self.add(builder.build(ctx.db));
         }
     }
@@ -499,17 +523,10 @@ impl Completions {
         path: Option<hir::ModPath>,
         local_name: Option<hir::Name>,
     ) {
-        let is_private_editable = match ctx.is_visible(&un) {
-            Visible::Yes => false,
-            Visible::Editable => true,
-            Visible::No => return,
-        };
-        let item = render_union_literal(
-            RenderContext::new(ctx).private_editable(is_private_editable),
-            un,
-            path,
-            local_name,
-        );
+        if !ctx.check_stability(Some(&un.attrs(ctx.db))) {
+            return;
+        }
+        let item = render_union_literal(RenderContext::new(ctx), un, path, local_name);
         self.add_opt(item);
     }
 
@@ -554,7 +571,7 @@ impl Completions {
         variant: hir::Variant,
         local_name: Option<hir::Name>,
     ) {
-        if !ctx.check_stability_and_hidden(variant) {
+        if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
             return;
         }
         self.add_opt(render_variant_pat(
@@ -574,7 +591,7 @@ impl Completions {
         variant: hir::Variant,
         path: hir::ModPath,
     ) {
-        if !ctx.check_stability_and_hidden(variant) {
+        if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
             return;
         }
         let path = Some(&path);
@@ -595,17 +612,10 @@ impl Completions {
         strukt: hir::Struct,
         local_name: Option<hir::Name>,
     ) {
-        let is_private_editable = match ctx.is_visible(&strukt) {
-            Visible::Yes => false,
-            Visible::Editable => true,
-            Visible::No => return,
-        };
-        self.add_opt(render_struct_pat(
-            RenderContext::new(ctx).private_editable(is_private_editable),
-            pattern_ctx,
-            strukt,
-            local_name,
-        ));
+        if !ctx.check_stability(Some(&strukt.attrs(ctx.db))) {
+            return;
+        }
+        self.add_opt(render_struct_pat(RenderContext::new(ctx), pattern_ctx, strukt, local_name));
     }
 
     pub(crate) fn suggest_name(&mut self, ctx: &CompletionContext<'_>, name: &str) {
diff --git a/crates/ide-completion/src/completions/item_list/trait_impl.rs b/crates/ide-completion/src/completions/item_list/trait_impl.rs
index 831f5665f4..5f0288ae95 100644
--- a/crates/ide-completion/src/completions/item_list/trait_impl.rs
+++ b/crates/ide-completion/src/completions/item_list/trait_impl.rs
@@ -31,7 +31,7 @@
 //! }
 //! ```
 
-use hir::{db::ExpandDatabase, MacroFileId, Name};
+use hir::{db::ExpandDatabase, HasAttrs, MacroFileId, Name};
 use ide_db::text_edit::TextEdit;
 use ide_db::{
     documentation::HasDocs, path_transform::PathTransform,
@@ -155,7 +155,7 @@ fn complete_trait_impl(
     if let Some(hir_impl) = ctx.sema.to_def(impl_def) {
         get_missing_assoc_items(&ctx.sema, impl_def)
             .into_iter()
-            .filter(|item| ctx.check_stability_and_hidden(*item))
+            .filter(|item| ctx.check_stability(Some(&item.attrs(ctx.db))))
             .for_each(|item| {
                 use self::ImplCompletionKind::*;
                 match (item, kind) {
diff --git a/crates/ide-completion/src/completions/use_.rs b/crates/ide-completion/src/completions/use_.rs
index b384987c51..9d62622add 100644
--- a/crates/ide-completion/src/completions/use_.rs
+++ b/crates/ide-completion/src/completions/use_.rs
@@ -52,14 +52,8 @@ pub(crate) fn complete_use_path(
                         )
                     };
                     for (name, def) in module_scope {
-                        if let (Some(attrs), Some(defining_crate)) =
-                            (def.attrs(ctx.db), def.krate(ctx.db))
-                        {
-                            if !ctx.check_stability(Some(&attrs))
-                                || ctx.is_doc_hidden(&attrs, defining_crate)
-                            {
-                                continue;
-                            }
+                        if !ctx.check_stability(def.attrs(ctx.db).as_deref()) {
+                            continue;
                         }
                         let is_name_already_imported =
                             already_imported_names.contains(name.as_str());
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index 2f1860cbb5..366e79cddf 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -534,7 +534,7 @@ impl CompletionContext<'_> {
         }
     }
 
-    /// Checks if an item is visible, not `doc(hidden)` and stable at the completion site.
+    /// Checks if an item is visible and not `doc(hidden)` at the completion site.
     pub(crate) fn is_visible<I>(&self, item: &I) -> Visible
     where
         I: hir::HasVisibility + hir::HasAttrs + hir::HasCrate + Copy,
@@ -570,15 +570,6 @@ impl CompletionContext<'_> {
         !attrs.is_unstable() || self.is_nightly
     }
 
-    pub(crate) fn check_stability_and_hidden<I>(&self, item: I) -> bool
-    where
-        I: hir::HasAttrs + hir::HasCrate,
-    {
-        let defining_crate = item.krate(self.db);
-        let attrs = item.attrs(self.db);
-        self.check_stability(Some(&attrs)) && !self.is_doc_hidden(&attrs, defining_crate)
-    }
-
     /// Whether the given trait is an operator trait or not.
     pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool {
         match trait_.attrs(self.db).lang() {
@@ -656,10 +647,6 @@ impl CompletionContext<'_> {
         attrs: &hir::Attrs,
         defining_crate: hir::Crate,
     ) -> Visible {
-        if !self.check_stability(Some(attrs)) {
-            return Visible::No;
-        }
-
         if !vis.is_visible_from(self.db, self.module.into()) {
             if !self.config.enable_private_editable {
                 return Visible::No;
@@ -679,7 +666,7 @@ impl CompletionContext<'_> {
         }
     }
 
-    pub(crate) fn is_doc_hidden(&self, attrs: &hir::Attrs, defining_crate: hir::Crate) -> bool {
+    fn is_doc_hidden(&self, attrs: &hir::Attrs, defining_crate: hir::Crate) -> bool {
         // `doc(hidden)` items are only completed within the defining crate.
         self.krate != defining_crate && attrs.has_doc_hidden()
     }
diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs
index d12bda0816..bad5360805 100644
--- a/crates/ide-db/src/defs.rs
+++ b/crates/ide-db/src/defs.rs
@@ -986,6 +986,7 @@ impl From<GenericDef> for Definition {
             GenericDef::TypeAlias(it) => it.into(),
             GenericDef::Impl(it) => it.into(),
             GenericDef::Const(it) => it.into(),
+            GenericDef::Static(it) => it.into(),
         }
     }
 }
diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs
index 7fc563a424..7963e8ae4f 100644
--- a/crates/ide-db/src/search.rs
+++ b/crates/ide-db/src/search.rs
@@ -354,6 +354,7 @@ impl Definition {
                 hir::GenericDef::TypeAlias(it) => it.source(db).map(|src| src.syntax().cloned()),
                 hir::GenericDef::Impl(it) => it.source(db).map(|src| src.syntax().cloned()),
                 hir::GenericDef::Const(it) => it.source(db).map(|src| src.syntax().cloned()),
+                hir::GenericDef::Static(it) => it.source(db).map(|src| src.syntax().cloned()),
             };
             return match def {
                 Some(def) => SearchScope::file_range(
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 9d4c103fc2..18a3fed07e 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -346,7 +346,7 @@ fn hover_offset(
         .unique()
         .reduce(|mut acc: HoverResult, HoverResult { markup, actions }| {
             acc.actions.extend(actions);
-            acc.markup = Markup::from(format!("{}\n\n---\n{markup}", acc.markup));
+            acc.markup = Markup::from(format!("{}\n---\n{markup}", acc.markup));
             acc
         })
         .map(|mut res: HoverResult| {
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index 40f3406b72..c996230c3a 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -434,6 +434,7 @@ fn definition_owner_name(db: &RootDatabase, def: Definition, edition: Edition) -
                     None => it.name(db),
                 }
             }
+            hir::GenericDef::Static(it) => Some(it.name(db)),
         },
         Definition::DeriveHelper(derive_helper) => Some(derive_helper.derive().name(db)),
         d => {
diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs
index 01a1a4545c..ab5464156f 100644
--- a/crates/ide/src/inlay_hints/bind_pat.rs
+++ b/crates/ide/src/inlay_hints/bind_pat.rs
@@ -1203,40 +1203,6 @@ fn f5<G: T<Assoc = ()>>(it: G) {
     let l = it.f();
       //^ ()
 }
-"#,
-        );
-    }
-
-    #[test]
-    fn regression_19007() {
-        check_types(
-            r#"
-trait Foo {
-    type Assoc;
-
-    fn foo(&self) -> Self::Assoc;
-}
-
-trait Bar {
-    type Target;
-}
-
-trait Baz<T> {}
-
-struct Struct<T: Foo> {
-    field: T,
-}
-
-impl<T> Struct<T>
-where
-    T: Foo,
-    T::Assoc: Baz<<T::Assoc as Bar>::Target> + Bar,
-{
-    fn f(&self) {
-        let x = self.field.foo();
-          //^ impl Baz<<<T as Foo>::Assoc as Bar>::Target> + Bar
-    }
-}
 "#,
         );
     }
diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs
index f8c60418eb..f997211600 100644
--- a/crates/ide/src/signature_help.rs
+++ b/crates/ide/src/signature_help.rs
@@ -321,7 +321,9 @@ fn signature_help_for_generics(
             format_to!(res.signature, "type {}", it.name(db).display(db, edition));
         }
         // These don't have generic args that can be specified
-        hir::GenericDef::Impl(_) | hir::GenericDef::Const(_) => return None,
+        hir::GenericDef::Impl(_) | hir::GenericDef::Const(_) | hir::GenericDef::Static(_) => {
+            return None
+        }
     }
 
     let params = generics_def.params(sema.db);
diff --git a/crates/intern/Cargo.toml b/crates/intern/Cargo.toml
index c0358ef929..5e7ee54c6a 100644
--- a/crates/intern/Cargo.toml
+++ b/crates/intern/Cargo.toml
@@ -19,6 +19,7 @@ dashmap.workspace = true
 hashbrown.workspace = true
 rustc-hash.workspace = true
 triomphe.workspace = true
+sptr = "0.3.2"
 
 [lints]
 workspace = true
diff --git a/crates/intern/src/symbol.rs b/crates/intern/src/symbol.rs
index b3bf285edf..200b14027f 100644
--- a/crates/intern/src/symbol.rs
+++ b/crates/intern/src/symbol.rs
@@ -13,6 +13,7 @@ use std::{
 use dashmap::{DashMap, SharedValue};
 use hashbrown::{hash_map::RawEntryMut, HashMap};
 use rustc_hash::FxHasher;
+use sptr::Strict;
 use triomphe::Arc;
 
 pub mod symbols;
@@ -83,7 +84,7 @@ impl TaggedArcPtr {
     #[inline]
     pub(crate) unsafe fn try_as_arc_owned(self) -> Option<ManuallyDrop<Arc<Box<str>>>> {
         // Unpack the tag from the alignment niche
-        let tag = self.packed.as_ptr().addr() & Self::BOOL_BITS;
+        let tag = Strict::addr(self.packed.as_ptr()) & Self::BOOL_BITS;
         if tag != 0 {
             // Safety: We checked that the tag is non-zero -> true, so we are pointing to the data offset of an `Arc`
             Some(ManuallyDrop::new(unsafe {
@@ -98,18 +99,40 @@ impl TaggedArcPtr {
     fn pack_arc(ptr: NonNull<*const str>) -> NonNull<*const str> {
         let packed_tag = true as usize;
 
-        unsafe {
-            // Safety: The pointer is derived from a non-null and bit-oring it with true (1) will
-            // not make it null.
-            NonNull::new_unchecked(ptr.as_ptr().map_addr(|addr| addr | packed_tag))
-        }
+        // can't use this strict provenance stuff here due to trait methods not being const
+        // unsafe {
+        //     // Safety: The pointer is derived from a non-null
+        //     NonNull::new_unchecked(Strict::map_addr(ptr.as_ptr(), |addr| {
+        //         // Safety:
+        //         // - The pointer is `NonNull` => it's address is `NonZero<usize>`
+        //         // - `P::BITS` least significant bits are always zero (`Pointer` contract)
+        //         // - `T::BITS <= P::BITS` (from `Self::ASSERTION`)
+        //         //
+        //         // Thus `addr >> T::BITS` is guaranteed to be non-zero.
+        //         //
+        //         // `{non_zero} | packed_tag` can't make the value zero.
+
+        //         (addr >> Self::BOOL_BITS) | packed_tag
+        //     }))
+        // }
+        // so what follows is roughly what the above looks like but inlined
+
+        let self_addr = ptr.as_ptr() as *const *const str as usize;
+        let addr = self_addr | packed_tag;
+        let dest_addr = addr as isize;
+        let offset = dest_addr.wrapping_sub(self_addr as isize);
+
+        // SAFETY: The resulting pointer is guaranteed to be NonNull as we only modify the niche bytes
+        unsafe { NonNull::new_unchecked(ptr.as_ptr().cast::<u8>().wrapping_offset(offset).cast()) }
     }
 
     #[inline]
     pub(crate) fn pointer(self) -> NonNull<*const str> {
         // SAFETY: The resulting pointer is guaranteed to be NonNull as we only modify the niche bytes
         unsafe {
-            NonNull::new_unchecked(self.packed.as_ptr().map_addr(|addr| addr & !Self::BOOL_BITS))
+            NonNull::new_unchecked(Strict::map_addr(self.packed.as_ptr(), |addr| {
+                addr & !Self::BOOL_BITS
+            }))
         }
     }
 
diff --git a/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
index 59293ee3f9..c7614849e0 100644
--- a/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
+++ b/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
@@ -392,12 +392,12 @@ impl server::Span for RaSpanServer {
 
     fn line(&mut self, _span: Self::Span) -> usize {
         // FIXME requires db to resolve line index, THIS IS NOT INCREMENTAL
-        1
+        0
     }
 
     fn column(&mut self, _span: Self::Span) -> usize {
         // FIXME requires db to resolve line index, THIS IS NOT INCREMENTAL
-        1
+        0
     }
 }
 
diff --git a/crates/proc-macro-srv/src/server_impl/token_id.rs b/crates/proc-macro-srv/src/server_impl/token_id.rs
index 409cf3cc78..466eb14b55 100644
--- a/crates/proc-macro-srv/src/server_impl/token_id.rs
+++ b/crates/proc-macro-srv/src/server_impl/token_id.rs
@@ -291,11 +291,11 @@ impl server::Span for TokenIdServer {
     }
 
     fn line(&mut self, _span: Self::Span) -> usize {
-        1
+        0
     }
 
     fn column(&mut self, _span: Self::Span) -> usize {
-        1
+        0
     }
 }
 
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index 18c27c8449..cd709afe09 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -13,7 +13,7 @@ use hir::{
     ModuleDef, Name,
 };
 use hir_def::{
-    body::BodySourceMap,
+    expr_store::BodySourceMap,
     hir::{ExprId, PatId},
     SyntheticSyntax,
 };
diff --git a/docs/dev/README.md b/docs/dev/README.md
index c990212d58..3ba492e095 100644
--- a/docs/dev/README.md
+++ b/docs/dev/README.md
@@ -269,13 +269,19 @@ Note: we tag releases by dates, releasing a patch release on the same day should
 
 ## Permissions
 
-There are two sets of people with extra permissions:
+There are three sets of people with extra permissions:
 
-* The [rust-lang](https://github.com/rust-lang) team [t-rust-analyzer](https://github.com/rust-lang/team/blob/master/teams/rust-analyzer.toml).
-  This team has write access to the repository and merge queue permissions (note the repo itself is managed by infra admins).
+* rust-analyzer GitHub organization [**admins**](https://github.com/orgs/rust-analyzer/people?query=role:owner) (which include current t-compiler leads).
+  Admins have full access to the org.
+* [**review**](https://github.com/orgs/rust-analyzer/teams/review) team in the organization.
+  Reviewers have `r+` access to all of organization's repositories and publish rights on crates.io.
+  They also have direct commit access, but all changes should via bors queue.
   It's ok to self-approve if you think you know what you are doing!
+  bors should automatically sync the permissions.
   Feel free to request a review or assign any PR to a reviewer with the relevant expertise to bring the work to their attention.
   Don't feel pressured to review assigned PRs though.
-  If you don't feel like reviewing for whatever reason, someone else will pick the review up (but please speak up if you don't feel like it)!
-* The [rust-lang](https://github.com/rust-lang) team [t-rust-analyzer-contributors]([https://github.com/orgs/rust-analyzer/teams/triage](https://github.com/rust-lang/team/blob/master/teams/rust-analyzer-contributors.toml)).
-  This team has general triaging permissions allowing to label, close and re-open issues.
+  If you don't feel like reviewing for whatever reason, someone else will pick the review up!
+* [**triage**](https://github.com/orgs/rust-analyzer/teams/triage) team in the organization.
+  This team can label and close issues.
+
+Note that at the time being you need to be a member of the org yourself to view the links.


Test patch:
diff --git a/crates/hir-def/src/body/tests.rs b/crates/hir-def/src/expr_store/tests.rs
similarity index 99%
rename from crates/hir-def/src/body/tests.rs
rename to crates/hir-def/src/expr_store/tests.rs
index edc7c4c1f2..9bf1ddb479 100644
--- a/crates/hir-def/src/body/tests.rs
+++ b/crates/hir-def/src/expr_store/tests.rs
@@ -1,6 +1,7 @@
 mod block;
 
 use expect_test::{expect, Expect};
+use la_arena::RawIdx;
 use test_fixture::WithFixture;
 
 use crate::{test_db::TestDB, ModuleDefId};
diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs
index 00da9b2517..69ec35f406 100644
--- a/crates/hir-ty/src/tests.rs
+++ b/crates/hir-ty/src/tests.rs
@@ -18,8 +18,8 @@ use std::sync::LazyLock;
 use base_db::SourceDatabaseFileInputExt as _;
 use expect_test::Expect;
 use hir_def::{
-    body::{Body, BodySourceMap},
     db::DefDatabase,
+    expr_store::{Body, BodySourceMap},
     hir::{ExprId, Pat, PatId},
     item_scope::ItemScope,
     nameres::DefMap,
diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs
index 663a038580..e117dbf4bd 100644
--- a/crates/ide-completion/src/tests/expression.rs
+++ b/crates/ide-completion/src/tests/expression.rs
@@ -1965,24 +1965,3 @@ fn bar() {
         "#]],
     );
 }
-
-#[test]
-fn doc_hidden_enum_variant() {
-    check(
-        r#"
-//- /foo.rs crate:foo
-pub enum Enum {
-    #[doc(hidden)] Hidden,
-    Visible,
-}
-
-//- /lib.rs crate:lib deps:foo
-fn foo() {
-    let _ = foo::Enum::$0;
-}
-    "#,
-        expect![[r#"
-            ev Visible Visible
-        "#]],
-    );
-}
diff --git a/crates/ide-completion/src/tests/use_tree.rs b/crates/ide-completion/src/tests/use_tree.rs
index 593b1edde5..04b3a47a64 100644
--- a/crates/ide-completion/src/tests/use_tree.rs
+++ b/crates/ide-completion/src/tests/use_tree.rs
@@ -451,20 +451,3 @@ marco_rules! m { () => {} }
         "#]],
     );
 }
-
-#[test]
-fn use_tree_doc_hidden() {
-    check(
-        r#"
-//- /foo.rs crate:foo
-#[doc(hidden)] pub struct Hidden;
-pub struct Visible;
-
-//- /lib.rs crate:lib deps:foo
-use foo::$0;
-    "#,
-        expect![[r#"
-            st Visible Visible
-        "#]],
-    );
-}
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 8c32cc9720..064a845dc5 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -303,7 +303,6 @@ m!(ab$0c);
             ---
 
             Outer
-
             ---
 
             ```rust
@@ -4402,7 +4401,6 @@ fn main() {
             ---
 
             size = 8, align = 8, niches = 1
-
             ---
 
             ```rust
@@ -10096,7 +10094,6 @@ fn bar() {
             ```rust
             let field: i32
             ```
-
             ---
 
             ```rust
@@ -10131,7 +10128,6 @@ fn bar() {
             ---
 
             size = 4, align = 4
-
             ---
 
             ```rust