From bc3e7e1d5f66eae90b02d61bba60f979e5003853 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 29 Nov 2025 11:47:30 +0100 Subject: [PATCH 1/2] perf: Shrink `InferenceResult` by ~40 bytes --- Cargo.lock | 1 + crates/hir-ty/Cargo.toml | 1 + crates/hir-ty/src/infer.rs | 53 ++++++++++++------- crates/hir-ty/src/infer/coerce.rs | 2 +- crates/hir-ty/src/infer/diagnostics.rs | 5 +- crates/hir-ty/src/infer/expr.rs | 7 ++- crates/hir-ty/src/infer/pat.rs | 8 ++- .../hir-ty/src/method_resolution/confirm.rs | 2 +- 8 files changed, 54 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e21776cdabce..7e1bd6320d5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -905,6 +905,7 @@ dependencies = [ "syntax", "test-fixture", "test-utils", + "thin-vec", "tracing", "tracing-subscriber", "tracing-tree", diff --git a/crates/hir-ty/Cargo.toml b/crates/hir-ty/Cargo.toml index 8adf95bd0408..c60ecef58eaf 100644 --- a/crates/hir-ty/Cargo.toml +++ b/crates/hir-ty/Cargo.toml @@ -53,6 +53,7 @@ hir-expand.workspace = true base-db.workspace = true syntax.workspace = true span.workspace = true +thin-vec = "0.2.14" [dev-dependencies] expect-test = "1.5.1" diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 15eb35512808..616b9fd4484a 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -56,6 +56,7 @@ use rustc_type_ir::{ }; use span::Edition; use stdx::never; +use thin_vec::ThinVec; use triomphe::Arc; use crate::{ @@ -489,8 +490,7 @@ pub struct InferenceResult<'db> { /// [`InferenceContext`] and store the tuples substitution there. This map is the reverse of /// that which allows us to resolve a [`TupleFieldId`]s type. tuple_field_access_types: FxHashMap>, - /// During inference this field is empty and [`InferenceContext::diagnostics`] is filled instead. - diagnostics: Vec>, + pub(crate) type_of_expr: ArenaMap>, /// For each pattern record the type it resolves to. /// @@ -500,15 +500,21 @@ pub struct InferenceResult<'db> { pub(crate) type_of_binding: ArenaMap>, pub(crate) type_of_type_placeholder: ArenaMap>, pub(crate) type_of_opaque: FxHashMap>, - pub(crate) type_mismatches: FxHashMap>, + + pub(crate) type_mismatches: Option>>>, /// Whether there are any type-mismatching errors in the result. // FIXME: This isn't as useful as initially thought due to us falling back placeholders to // `TyKind::Error`. // Which will then mark this field. pub(crate) has_errors: bool, + /// During inference this field is empty and [`InferenceContext::diagnostics`] is filled instead. + diagnostics: ThinVec>, + /// Interned `Error` type to return references to. // FIXME: Remove this. error_ty: Ty<'db>, + + pub(crate) expr_adjustments: FxHashMap]>>, /// Stores the types which were implicitly dereferenced in pattern binding modes. pub(crate) pat_adjustments: FxHashMap>>, /// Stores the binding mode (`ref` in `let ref x = 2`) of bindings. @@ -525,10 +531,11 @@ pub struct InferenceResult<'db> { /// ``` /// the first `rest` has implicit `ref` binding mode, but the second `rest` binding mode is `move`. pub(crate) binding_modes: ArenaMap, - pub(crate) expr_adjustments: FxHashMap]>>, + pub(crate) closure_info: FxHashMap>, FnTrait)>, // FIXME: remove this field pub mutated_bindings_in_closure: FxHashSet, + pub(crate) coercion_casts: FxHashSet, } @@ -595,19 +602,25 @@ impl<'db> InferenceResult<'db> { } } pub fn type_mismatch_for_expr(&self, expr: ExprId) -> Option<&TypeMismatch<'db>> { - self.type_mismatches.get(&expr.into()) + self.type_mismatches.as_deref()?.get(&expr.into()) } pub fn type_mismatch_for_pat(&self, pat: PatId) -> Option<&TypeMismatch<'db>> { - self.type_mismatches.get(&pat.into()) + self.type_mismatches.as_deref()?.get(&pat.into()) } pub fn type_mismatches(&self) -> impl Iterator)> { - self.type_mismatches.iter().map(|(expr_or_pat, mismatch)| (*expr_or_pat, mismatch)) + self.type_mismatches + .as_deref() + .into_iter() + .flatten() + .map(|(expr_or_pat, mismatch)| (*expr_or_pat, mismatch)) } pub fn expr_type_mismatches(&self) -> impl Iterator)> { - self.type_mismatches.iter().filter_map(|(expr_or_pat, mismatch)| match *expr_or_pat { - ExprOrPatId::ExprId(expr) => Some((expr, mismatch)), - _ => None, - }) + self.type_mismatches.as_deref().into_iter().flatten().filter_map( + |(expr_or_pat, mismatch)| match *expr_or_pat { + ExprOrPatId::ExprId(expr) => Some((expr, mismatch)), + _ => None, + }, + ) } pub fn placeholder_types(&self) -> impl Iterator)> { self.type_of_type_placeholder.iter() @@ -1063,13 +1076,14 @@ impl<'body, 'db> InferenceContext<'body, 'db> { type_of_type_placeholder.shrink_to_fit(); type_of_opaque.shrink_to_fit(); - *has_errors |= !type_mismatches.is_empty(); - - for mismatch in (*type_mismatches).values_mut() { - mismatch.expected = table.resolve_completely(mismatch.expected); - mismatch.actual = table.resolve_completely(mismatch.actual); + if let Some(type_mismatches) = type_mismatches { + *has_errors = true; + for mismatch in type_mismatches.values_mut() { + mismatch.expected = table.resolve_completely(mismatch.expected); + mismatch.actual = table.resolve_completely(mismatch.actual); + } + type_mismatches.shrink_to_fit(); } - type_mismatches.shrink_to_fit(); diagnostics.retain_mut(|diagnostic| { use InferenceDiagnostic::*; match diagnostic { @@ -1520,7 +1534,10 @@ impl<'body, 'db> InferenceContext<'body, 'db> { ) -> Result<(), ()> { let result = self.demand_eqtype_fixme_no_diag(expected, actual); if result.is_err() { - self.result.type_mismatches.insert(id, TypeMismatch { expected, actual }); + self.result + .type_mismatches + .get_or_insert_default() + .insert(id, TypeMismatch { expected, actual }); } result } diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs index 4acf964bbc85..add8fc9bfcac 100644 --- a/crates/hir-ty/src/infer/coerce.rs +++ b/crates/hir-ty/src/infer/coerce.rs @@ -1514,7 +1514,7 @@ impl<'db, 'exprs> CoerceMany<'db, 'exprs> { self.final_ty = Some(icx.types.error); - icx.result.type_mismatches.insert( + icx.result.type_mismatches.get_or_insert_default().insert( expression.into(), if label_expression_as_expected { TypeMismatch { expected: found, actual: expected } diff --git a/crates/hir-ty/src/infer/diagnostics.rs b/crates/hir-ty/src/infer/diagnostics.rs index 844eb02ab0d4..0eb7a2f4740f 100644 --- a/crates/hir-ty/src/infer/diagnostics.rs +++ b/crates/hir-ty/src/infer/diagnostics.rs @@ -11,6 +11,7 @@ use hir_def::expr_store::ExpressionStore; use hir_def::expr_store::path::Path; use hir_def::{hir::ExprOrPatId, resolver::Resolver}; use la_arena::{Idx, RawIdx}; +use thin_vec::ThinVec; use crate::{ InferenceDiagnostic, InferenceTyDiagnosticSource, TyLoweringDiagnostic, @@ -24,7 +25,7 @@ use crate::{ // to our resolver and so we cannot have mutable reference, but we really want to have // ability to dispatch diagnostics during this work otherwise the code becomes a complete mess. #[derive(Debug, Default, Clone)] -pub(super) struct Diagnostics<'db>(RefCell>>); +pub(super) struct Diagnostics<'db>(RefCell>>); impl<'db> Diagnostics<'db> { pub(super) fn push(&self, diagnostic: InferenceDiagnostic<'db>) { @@ -41,7 +42,7 @@ impl<'db> Diagnostics<'db> { ); } - pub(super) fn finish(self) -> Vec> { + pub(super) fn finish(self) -> ThinVec> { self.0.into_inner() } } diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 7487660a7618..d3d6d1ed61d1 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -71,6 +71,7 @@ impl<'db> InferenceContext<'_, 'db> { if !could_unify { self.result .type_mismatches + .get_or_insert_default() .insert(tgt_expr.into(), TypeMismatch { expected: expected_ty, actual: ty }); } } @@ -100,6 +101,7 @@ impl<'db> InferenceContext<'_, 'db> { Err(_) => { self.result .type_mismatches + .get_or_insert_default() .insert(expr.into(), TypeMismatch { expected: target, actual: ty }); target } @@ -293,6 +295,7 @@ impl<'db> InferenceContext<'_, 'db> { if !could_unify { self.result .type_mismatches + .get_or_insert_default() .insert(expr.into(), TypeMismatch { expected: expected_ty, actual: ty }); } } @@ -1188,6 +1191,7 @@ impl<'db> InferenceContext<'_, 'db> { Err(_) => { this.result .type_mismatches + .get_or_insert_default() .insert(tgt_expr.into(), TypeMismatch { expected: target, actual: ty }); target } @@ -1556,7 +1560,7 @@ impl<'db> InferenceContext<'_, 'db> { ) .is_err() { - this.result.type_mismatches.insert( + this.result.type_mismatches.get_or_insert_default().insert( expr.into(), TypeMismatch { expected: t, actual: this.types.unit }, ); @@ -2130,6 +2134,7 @@ impl<'db> InferenceContext<'_, 'db> { // Don't report type mismatches if there is a mismatch in args count. self.result .type_mismatches + .get_or_insert_default() .insert((*arg).into(), TypeMismatch { expected, actual: found }); } } diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs index ece2bdc4fd19..a02e280ac637 100644 --- a/crates/hir-ty/src/infer/pat.rs +++ b/crates/hir-ty/src/infer/pat.rs @@ -331,7 +331,7 @@ impl<'db> InferenceContext<'_, 'db> { return self.pat_ty_after_adjustment(pat); } Err(_) => { - self.result.type_mismatches.insert( + self.result.type_mismatches.get_or_insert_default().insert( pat.into(), TypeMismatch { expected, actual: ty_inserted_vars }, ); @@ -415,6 +415,7 @@ impl<'db> InferenceContext<'_, 'db> { Err(_) => { self.result .type_mismatches + .get_or_insert_default() .insert(pat.into(), TypeMismatch { expected, actual: lhs_ty }); // `rhs_ty` is returned so no further type mismatches are // reported because of this mismatch. @@ -431,7 +432,10 @@ impl<'db> InferenceContext<'_, 'db> { let ty = self.insert_type_vars_shallow(ty); // FIXME: This never check is odd, but required with out we do inference right now if !expected.is_never() && !self.unify(ty, expected) { - self.result.type_mismatches.insert(pat.into(), TypeMismatch { expected, actual: ty }); + self.result + .type_mismatches + .get_or_insert_default() + .insert(pat.into(), TypeMismatch { expected, actual: ty }); } self.write_pat_ty(pat, ty); self.pat_ty_after_adjustment(pat) diff --git a/crates/hir-ty/src/method_resolution/confirm.rs b/crates/hir-ty/src/method_resolution/confirm.rs index 9e8791edde3b..9017c2f8777b 100644 --- a/crates/hir-ty/src/method_resolution/confirm.rs +++ b/crates/hir-ty/src/method_resolution/confirm.rs @@ -481,7 +481,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { } Err(_) => { if self.ctx.unstable_features.arbitrary_self_types { - self.ctx.result.type_mismatches.insert( + self.ctx.result.type_mismatches.get_or_insert_default().insert( self.expr.into(), TypeMismatch { expected: method_self_ty, actual: self_ty }, ); From 2fab9d801279d68d9aa10b17d7a26177258d72e6 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 29 Nov 2025 11:51:32 +0100 Subject: [PATCH 2/2] Replace incorrect `ArenaMap` use with `FxHashMap` --- crates/hir-ty/src/infer.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 616b9fd4484a..1d9f7dc06514 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -498,7 +498,7 @@ pub struct InferenceResult<'db> { /// unresolved or missing subpatterns or subpatterns of mismatched types. pub(crate) type_of_pat: ArenaMap>, pub(crate) type_of_binding: ArenaMap>, - pub(crate) type_of_type_placeholder: ArenaMap>, + pub(crate) type_of_type_placeholder: FxHashMap>, pub(crate) type_of_opaque: FxHashMap>, pub(crate) type_mismatches: Option>>>, @@ -623,10 +623,10 @@ impl<'db> InferenceResult<'db> { ) } pub fn placeholder_types(&self) -> impl Iterator)> { - self.type_of_type_placeholder.iter() + self.type_of_type_placeholder.iter().map(|(&type_ref, ty)| (type_ref, ty)) } pub fn type_of_type_placeholder(&self, type_ref: TypeRefId) -> Option> { - self.type_of_type_placeholder.get(type_ref).copied() + self.type_of_type_placeholder.get(&type_ref).copied() } pub fn closure_info(&self, closure: InternedClosureId) -> &(Vec>, FnTrait) { self.closure_info.get(&closure).unwrap()