From 1bbc255ec59118e4e971dc65468b5a0c9955f9b4 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 10 Dec 2021 20:01:24 +0100 Subject: [PATCH] Remove some allocations --- crates/hir/src/display.rs | 8 +++- crates/hir/src/lib.rs | 4 +- crates/hir_ty/src/autoderef.rs | 18 +++++--- crates/hir_ty/src/chalk_db.rs | 5 ++- crates/hir_ty/src/chalk_ext.rs | 3 +- .../match_check/deconstruct_pat.rs | 3 +- crates/hir_ty/src/display.rs | 6 ++- crates/hir_ty/src/infer/coerce.rs | 10 +++-- crates/hir_ty/src/lower.rs | 8 ++-- crates/hir_ty/src/method_resolution.rs | 43 ++++++++----------- crates/hir_ty/src/traits.rs | 5 ++- crates/hir_ty/src/utils.rs | 18 ++++---- crates/stdx/src/lib.rs | 5 +++ 13 files changed, 78 insertions(+), 58 deletions(-) diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index 38bbf417971..17465dc2b04 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs @@ -12,7 +12,10 @@ }, Interner, TraitRefExt, WhereClause, }; -use syntax::ast::{self, HasName}; +use syntax::{ + ast::{self, HasName}, + SmolStr, +}; use crate::{ Adt, Const, ConstParam, Enum, Field, Function, GenericParam, HasCrate, HasVisibility, @@ -247,7 +250,8 @@ fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { bounds.iter().cloned().map(|b| b.substitute(&Interner, &substs)).collect(); let krate = self.id.parent.krate(f.db).id; let sized_trait = - f.db.lang_item(krate, "sized".into()).and_then(|lang_item| lang_item.as_trait()); + f.db.lang_item(krate, SmolStr::new_inline("sized")) + .and_then(|lang_item| lang_item.as_trait()); let has_only_sized_bound = predicates.iter().all(move |pred| match pred.skip_binders() { WhereClause::Implemented(it) => Some(it.hir_trait_id()) == sized_trait, _ => false, diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 3739e522a18..7add0f4a436 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -2427,7 +2427,7 @@ pub fn impls_future(&self, db: &dyn HirDatabase) -> bool { let krate = self.krate; let std_future_trait = - db.lang_item(krate, "future_trait".into()).and_then(|it| it.as_trait()); + db.lang_item(krate, SmolStr::new_inline("future_trait")).and_then(|it| it.as_trait()); let std_future_trait = match std_future_trait { Some(it) => it, None => return false, @@ -2516,7 +2516,7 @@ pub fn normalize_trait_assoc_type( } pub fn is_copy(&self, db: &dyn HirDatabase) -> bool { - let lang_item = db.lang_item(self.krate, SmolStr::new("copy")); + let lang_item = db.lang_item(self.krate, SmolStr::new_inline("copy")); let copy_trait = match lang_item { Some(LangItemTarget::TraitId(it)) => it, _ => return false, diff --git a/crates/hir_ty/src/autoderef.rs b/crates/hir_ty/src/autoderef.rs index 1eb54905078..2d3d16cda0e 100644 --- a/crates/hir_ty/src/autoderef.rs +++ b/crates/hir_ty/src/autoderef.rs @@ -10,6 +10,7 @@ use hir_def::lang_item::LangItemTarget; use hir_expand::name::name; use limit::Limit; +use syntax::SmolStr; use tracing::{info, warn}; use crate::{ @@ -71,7 +72,10 @@ fn next(&mut self) -> Option { } let (kind, new_ty) = if let Some(derefed) = builtin_deref(&self.ty.value) { - (AutoderefKind::Builtin, Canonical { value: derefed, binders: self.ty.binders.clone() }) + ( + AutoderefKind::Builtin, + Canonical { value: derefed.clone(), binders: self.ty.binders.clone() }, + ) } else { ( AutoderefKind::Overloaded, @@ -110,15 +114,17 @@ pub(crate) fn deref( ) -> Option> { let _p = profile::span("deref"); match builtin_deref(&ty.goal.value) { - Some(derefed) => Some(Canonical { value: derefed, binders: ty.goal.binders.clone() }), + Some(derefed) => { + Some(Canonical { value: derefed.clone(), binders: ty.goal.binders.clone() }) + } None => deref_by_trait(db, krate, ty), } } -fn builtin_deref(ty: &Ty) -> Option { +fn builtin_deref(ty: &Ty) -> Option<&Ty> { match ty.kind(&Interner) { - TyKind::Ref(.., ty) => Some(ty.clone()), - TyKind::Raw(.., ty) => Some(ty.clone()), + TyKind::Ref(.., ty) => Some(ty), + TyKind::Raw(.., ty) => Some(ty), _ => None, } } @@ -129,7 +135,7 @@ fn deref_by_trait( ty: InEnvironment<&Canonical>, ) -> Option> { let _p = profile::span("deref_by_trait"); - let deref_trait = match db.lang_item(krate, "deref".into())? { + let deref_trait = match db.lang_item(krate, SmolStr::new_inline("deref"))? { LangItemTarget::TraitId(it) => it, _ => return None, }; diff --git a/crates/hir_ty/src/chalk_db.rs b/crates/hir_ty/src/chalk_db.rs index dc5dcd163c3..4a7b2d73637 100644 --- a/crates/hir_ty/src/chalk_db.rs +++ b/crates/hir_ty/src/chalk_db.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use cov_mark::hit; +use syntax::SmolStr; use tracing::debug; use chalk_ir::{cast::Cast, fold::shift::Shift, CanonicalVarKinds}; @@ -213,7 +214,7 @@ fn opaque_ty_data(&self, id: chalk_ir::OpaqueTyId) -> Arc { if let Some((future_trait, future_output)) = self .db - .lang_item(self.krate, "future_trait".into()) + .lang_item(self.krate, SmolStr::new_inline("future_trait")) .and_then(|item| item.as_trait()) .and_then(|trait_| { let alias = @@ -419,7 +420,7 @@ pub(crate) fn associated_ty_data_query( if !ctx.unsized_types.borrow().contains(&self_ty) { let sized_trait = resolver .krate() - .and_then(|krate| db.lang_item(krate, "sized".into())) + .and_then(|krate| db.lang_item(krate, SmolStr::new_inline("sized"))) .and_then(|lang_item| lang_item.as_trait().map(to_chalk_trait_id)); let sized_bound = sized_trait.into_iter().map(|sized_trait| { let trait_bound = diff --git a/crates/hir_ty/src/chalk_ext.rs b/crates/hir_ty/src/chalk_ext.rs index 653c4d5ec84..97d1ad0f9cd 100644 --- a/crates/hir_ty/src/chalk_ext.rs +++ b/crates/hir_ty/src/chalk_ext.rs @@ -6,6 +6,7 @@ type_ref::Rawness, FunctionId, GenericDefId, HasModule, ItemContainerId, Lookup, TraitId, }; +use syntax::SmolStr; use crate::{ db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, @@ -187,7 +188,7 @@ fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option { let krate = def.module(db.upcast()).krate(); if let Some(future_trait) = db - .lang_item(krate, "future_trait".into()) + .lang_item(krate, SmolStr::new_inline("future_trait")) .and_then(|item| item.as_trait()) { // This is only used by type walking. diff --git a/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs b/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs index f9439c706e3..f268746809e 100644 --- a/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs +++ b/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs @@ -50,6 +50,7 @@ use hir_def::{EnumVariantId, HasModule, LocalFieldId, VariantId}; use smallvec::{smallvec, SmallVec}; use stdx::never; +use syntax::SmolStr; use crate::{AdtId, Interner, Scalar, Ty, TyExt, TyKind}; @@ -905,7 +906,7 @@ fn is_field_list_non_exhaustive(variant_id: VariantId, cx: &MatchCheckCtx<'_>) - fn adt_is_box(adt: hir_def::AdtId, cx: &MatchCheckCtx<'_>) -> bool { use hir_def::lang_item::LangItemTarget; - match cx.db.lang_item(cx.module.krate(), "owned_box".into()) { + match cx.db.lang_item(cx.module.krate(), SmolStr::new_inline("owned_box")) { Some(LangItemTarget::StructId(box_id)) => adt == box_id.into(), _ => false, } diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs index 307323690a5..c09f8db86e3 100644 --- a/crates/hir_ty/src/display.rs +++ b/crates/hir_ty/src/display.rs @@ -20,6 +20,7 @@ }; use hir_expand::{hygiene::Hygiene, name::Name}; use itertools::Itertools; +use syntax::SmolStr; use crate::{ const_from_placeholder_idx, @@ -774,8 +775,9 @@ fn is_sized_trait(self, trait_: TraitId, db: &dyn DefDatabase) -> bool { match self { Self::NotSized => false, Self::Sized { anchor } => { - let sized_trait = - db.lang_item(anchor, "sized".into()).and_then(|lang_item| lang_item.as_trait()); + let sized_trait = db + .lang_item(anchor, SmolStr::new_inline("sized")) + .and_then(|lang_item| lang_item.as_trait()); Some(trait_) == sized_trait } } diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs index 4d7ac6fd85f..aebff59113e 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs @@ -10,6 +10,7 @@ use chalk_ir::{cast::Cast, Goal, Mutability, TyVariableKind}; use hir_def::{expr::ExprId, lang_item::LangItemTarget}; use stdx::always; +use syntax::SmolStr; use crate::{ autoderef::{Autoderef, AutoderefKind}, @@ -536,10 +537,11 @@ fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> CoerceResult { reborrow.as_ref().map_or_else(|| from_ty.clone(), |(_, adj)| adj.target.clone()); let krate = self.resolver.krate().unwrap(); - let coerce_unsized_trait = match self.db.lang_item(krate, "coerce_unsized".into()) { - Some(LangItemTarget::TraitId(trait_)) => trait_, - _ => return Err(TypeError), - }; + let coerce_unsized_trait = + match self.db.lang_item(krate, SmolStr::new_inline("coerce_unsized")) { + Some(LangItemTarget::TraitId(trait_)) => trait_, + _ => return Err(TypeError), + }; let coerce_unsized_tref = { let b = TyBuilder::trait_ref(self.db, coerce_unsized_trait); diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs index f7e2af46d29..7373c9eb8b4 100644 --- a/crates/hir_ty/src/lower.rs +++ b/crates/hir_ty/src/lower.rs @@ -28,7 +28,7 @@ use rustc_hash::FxHashSet; use smallvec::SmallVec; use stdx::impl_from; -use syntax::ast; +use syntax::{ast, SmolStr}; use crate::all_super_traits; use crate::{ @@ -797,7 +797,7 @@ pub(crate) fn lower_type_bound( let sized_trait = self .resolver .krate() - .and_then(|krate| self.db.lang_item(krate, "sized".into())) + .and_then(|krate| self.db.lang_item(krate, SmolStr::new_inline("sized"))) .and_then(|lang_item| lang_item.as_trait()); // Don't lower associated type bindings as the only possible relaxed trait bound // `?Sized` has no of them. @@ -895,7 +895,7 @@ fn lower_impl_trait( let krate = func.lookup(ctx.db.upcast()).module(ctx.db.upcast()).krate(); let sized_trait = ctx .db - .lang_item(krate, "sized".into()) + .lang_item(krate, SmolStr::new_inline("sized")) .and_then(|lang_item| lang_item.as_trait().map(to_chalk_trait_id)); let sized_clause = sized_trait.map(|trait_id| { let clause = WhereClause::Implemented(TraitRef { @@ -1200,7 +1200,7 @@ fn implicitly_sized_clauses<'a>( let generic_args = &substitution.as_slice(&Interner)[is_trait_def as usize..]; let sized_trait = resolver .krate() - .and_then(|krate| db.lang_item(krate, "sized".into())) + .and_then(|krate| db.lang_item(krate, SmolStr::new_inline("sized"))) .and_then(|lang_item| lang_item.as_trait().map(to_chalk_trait_id)); sized_trait.into_iter().flat_map(move |sized_trait| { diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs index 6af39828be5..1549a40c91d 100644 --- a/crates/hir_ty/src/method_resolution.rs +++ b/crates/hir_ty/src/method_resolution.rs @@ -541,9 +541,10 @@ pub fn iterate_method_candidates_dyn( // types*. let deref_chain = autoderef_method_receiver(db, krate, ty); - for i in 0..deref_chain.len() { + let mut deref_chains = stdx::slice_tails(&deref_chain); + deref_chains.try_for_each(|deref_chain| { iterate_method_candidates_with_autoref( - &deref_chain[i..], + deref_chain, db, env.clone(), krate, @@ -551,9 +552,8 @@ pub fn iterate_method_candidates_dyn( visible_from_module, name, callback, - )?; - } - ControlFlow::Continue(()) + ) + }) } LookupMode::Path => { // No autoderef for path lookups @@ -716,15 +716,14 @@ fn iterate_trait_method_candidates( // if ty is `dyn Trait`, the trait doesn't need to be in scope let inherent_trait = self_ty.value.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t)); - let env_traits = match self_ty.value.kind(&Interner) { - TyKind::Placeholder(_) => { - // if we have `T: Trait` in the param env, the trait doesn't need to be in scope + let env_traits = matches!(self_ty.value.kind(&Interner), TyKind::Placeholder(_)) + // if we have `T: Trait` in the param env, the trait doesn't need to be in scope + .then(|| { env.traits_in_scope_from_clauses(self_ty.value.clone()) .flat_map(|t| all_super_traits(db.upcast(), t)) - .collect() - } - _ => Vec::new(), - }; + }) + .into_iter() + .flatten(); let traits = inherent_trait.chain(env_traits).chain(traits_in_scope.iter().copied()); 'traits: for t in traits { @@ -747,10 +746,10 @@ fn iterate_trait_method_candidates( // trait, but if we find out it doesn't, we'll skip the rest of the // iteration let mut known_implemented = false; - for (_name, item) in data.items.iter() { + for &(_, item) in data.items.iter() { // Don't pass a `visible_from_module` down to `is_valid_candidate`, // since only inherent methods should be included into visibility checking. - if !is_valid_candidate(db, env.clone(), name, receiver_ty, *item, self_ty, None) { + if !is_valid_candidate(db, env.clone(), name, receiver_ty, item, self_ty, None) { continue; } if !known_implemented { @@ -761,7 +760,7 @@ fn iterate_trait_method_candidates( } known_implemented = true; // FIXME: we shouldn't be ignoring the binders here - callback(self_ty, *item)? + callback(self_ty, item)? } } ControlFlow::Continue(()) @@ -774,18 +773,14 @@ fn filter_inherent_impls_for_self_ty<'i>( // inherent methods on arrays are fingerprinted as [T; {unknown}], so we must also consider them when // resolving a method call on an array with a known len let array_impls = { - if let TyKind::Array(parameters, array_len) = self_ty.kind(&Interner) { - if !array_len.is_unknown() { + match self_ty.kind(&Interner) { + TyKind::Array(parameters, array_len) if !array_len.is_unknown() => { let unknown_array_len_ty = - TyKind::Array(parameters.clone(), consteval::usize_const(None)) - .intern(&Interner); + TyKind::Array(parameters.clone(), consteval::usize_const(None)); - Some(impls.for_self_ty(&unknown_array_len_ty)) - } else { - None + Some(impls.for_self_ty(&unknown_array_len_ty.intern(&Interner))) } - } else { - None + _ => None, } } .into_iter() diff --git a/crates/hir_ty/src/traits.rs b/crates/hir_ty/src/traits.rs index 3876a9da269..0d135651c78 100644 --- a/crates/hir_ty/src/traits.rs +++ b/crates/hir_ty/src/traits.rs @@ -9,6 +9,7 @@ use base_db::CrateId; use hir_def::{lang_item::LangItemTarget, TraitId}; use stdx::panic_context; +use syntax::SmolStr; use crate::{ db::HirDatabase, AliasEq, AliasTy, Canonical, DomainGoal, Goal, Guidance, InEnvironment, @@ -169,7 +170,7 @@ pub enum FnTrait { } impl FnTrait { - fn lang_item_name(self) -> &'static str { + const fn lang_item_name(self) -> &'static str { match self { FnTrait::FnOnce => "fn_once", FnTrait::FnMut => "fn_mut", @@ -178,7 +179,7 @@ fn lang_item_name(self) -> &'static str { } pub fn get_id(&self, db: &dyn HirDatabase, krate: CrateId) -> Option { - let target = db.lang_item(krate, self.lang_item_name().into())?; + let target = db.lang_item(krate, SmolStr::new_inline(self.lang_item_name()))?; match target { LangItemTarget::TraitId(t) => Some(t), _ => None, diff --git a/crates/hir_ty/src/utils.rs b/crates/hir_ty/src/utils.rs index a0837377c6e..f36a5f89871 100644 --- a/crates/hir_ty/src/utils.rs +++ b/crates/hir_ty/src/utils.rs @@ -18,6 +18,8 @@ }; use hir_expand::name::{name, Name}; use rustc_hash::FxHashSet; +use smallvec::{smallvec, SmallVec}; +use syntax::SmolStr; use crate::{ db::HirDatabase, ChalkTraitId, Interner, Substitution, TraitRef, TraitRefExt, TyKind, @@ -26,16 +28,16 @@ pub(crate) fn fn_traits(db: &dyn DefDatabase, krate: CrateId) -> impl Iterator { [ - db.lang_item(krate, "fn".into()), - db.lang_item(krate, "fn_mut".into()), - db.lang_item(krate, "fn_once".into()), + db.lang_item(krate, SmolStr::new_inline("fn")), + db.lang_item(krate, SmolStr::new_inline("fn_mut")), + db.lang_item(krate, SmolStr::new_inline("fn_once")), ] .into_iter() .flatten() .flat_map(|it| it.as_trait()) } -fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec { +fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> SmallVec<[TraitId; 4]> { let resolver = trait_.resolver(db); // returning the iterator directly doesn't easily work because of // lifetime problems, but since there usually shouldn't be more than a @@ -100,13 +102,13 @@ fn direct_super_trait_refs(db: &dyn HirDatabase, trait_ref: &TraitRef) -> Vec Vec { +pub fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> SmallVec<[TraitId; 4]> { // we need to take care a bit here to avoid infinite loops in case of cycles // (i.e. if we have `trait A: B; trait B: A;`) - let mut result = vec![trait_]; + + let mut result = smallvec![trait_]; let mut i = 0; - while i < result.len() { - let t = result[i]; + while let Some(&t) = result.get(i) { // yeah this is quadratic, but trait hierarchies should be flat // enough that this doesn't matter for tt in direct_super_traits(db, t) { diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs index ee23f579345..e33b45cc8ef 100644 --- a/crates/stdx/src/lib.rs +++ b/crates/stdx/src/lib.rs @@ -193,6 +193,11 @@ pub fn iter_eq_by(this: I2, other: I, mut eq: F) -> bool } } +/// Returns all final segments of the argument, longest first. +pub fn slice_tails(this: &[T]) -> impl Iterator { + (0..this.len()).map(|i| &this[i..]) +} + #[cfg(test)] mod tests { use super::*;