From b898808a35c8f38ec795187ed79ca13f4bdf7848 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 2 Apr 2022 15:32:40 +0200 Subject: [PATCH] fix: Don't rely on lang items to find primitive impls rustc has removed the use of lang items to mark the primitive impls, so just look through the crate graph for them (this should be fine performance-wise since we cache the crates that contain these impls). Fixes #11876. --- crates/hir_ty/src/db.rs | 9 +- crates/hir_ty/src/method_resolution.rs | 100 +++++++++---------- crates/hir_ty/src/tests/method_resolution.rs | 73 +++++++------- 3 files changed, 92 insertions(+), 90 deletions(-) diff --git a/crates/hir_ty/src/db.rs b/crates/hir_ty/src/db.rs index 467dcfa33ea7..de4c15359024 100644 --- a/crates/hir_ty/src/db.rs +++ b/crates/hir_ty/src/db.rs @@ -3,6 +3,7 @@ use std::sync::Arc; +use arrayvec::ArrayVec; use base_db::{impl_intern_key, salsa, CrateId, Upcast}; use hir_def::{ db::DefDatabase, expr::ExprId, BlockId, ConstId, ConstParamId, DefWithBodyId, FunctionId, @@ -13,7 +14,7 @@ use la_arena::ArenaMap; use crate::{ chalk_db, consteval::{ComputedExpr, ConstEvalError}, - method_resolution::{InherentImpls, TraitImpls}, + method_resolution::{InherentImpls, TraitImpls, TyFingerprint}, Binders, CallableDefId, FnDefId, GenericArg, ImplTraitId, InferenceResult, Interner, PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, TraitRef, Ty, TyDefId, ValueTyDefId, }; @@ -86,6 +87,12 @@ pub trait HirDatabase: DefDatabase + Upcast { #[salsa::invoke(InherentImpls::inherent_impls_in_block_query)] fn inherent_impls_in_block(&self, block: BlockId) -> Option>; + /// Collects all crates in the dependency graph that have impls for the + /// given fingerprint. This is only used for primitive types; for + /// user-defined types we just look at the crate where the type is defined. + #[salsa::invoke(crate::method_resolution::inherent_impl_crates_query)] + fn inherent_impl_crates(&self, krate: CrateId, fp: TyFingerprint) -> ArrayVec; + #[salsa::invoke(TraitImpls::trait_impls_in_crate_query)] fn trait_impls_in_crate(&self, krate: CrateId) -> Arc; diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs index 768772d5c343..d5285c171060 100644 --- a/crates/hir_ty/src/method_resolution.rs +++ b/crates/hir_ty/src/method_resolution.rs @@ -8,9 +8,8 @@ use arrayvec::ArrayVec; use base_db::{CrateId, Edition}; use chalk_ir::{cast::Cast, Mutability, UniverseIndex}; use hir_def::{ - item_scope::ItemScope, lang_item::LangItemTarget, nameres::DefMap, AssocItemId, BlockId, - ConstId, FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, - ModuleId, TraitId, + item_scope::ItemScope, nameres::DefMap, AssocItemId, BlockId, ConstId, FunctionId, + GenericDefId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, ModuleId, TraitId, }; use hir_expand::name::Name; use rustc_hash::{FxHashMap, FxHashSet}; @@ -21,7 +20,7 @@ use crate::{ db::HirDatabase, from_foreign_def_id, infer::{unify::InferenceTable, Adjust, Adjustment, AutoBorrow, OverloadedDeref, PointerCast}, - primitive::{self, FloatTy, IntTy, UintTy}, + primitive::{FloatTy, IntTy, UintTy}, static_lifetime, utils::all_super_traits, AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, ForeignDefId, InEnvironment, Interner, @@ -337,6 +336,30 @@ impl InherentImpls { } } +pub fn inherent_impl_crates_query( + db: &dyn HirDatabase, + krate: CrateId, + fp: TyFingerprint, +) -> ArrayVec { + let _p = profile::span("inherent_impl_crates_query"); + let mut res = ArrayVec::new(); + let crate_graph = db.crate_graph(); + + for krate in crate_graph.transitive_deps(krate) { + if res.is_full() { + // we don't currently look for or store more than two crates here, + // so don't needlessly look at more crates than necessary. + break; + } + let impls = db.inherent_impls_in_crate(krate); + if impls.map.get(&fp).map_or(false, |v| !v.is_empty()) { + res.push(krate); + } + } + + res +} + fn collect_unnamed_consts<'a>( db: &'a dyn HirDatabase, scope: &'a ItemScope, @@ -370,63 +393,30 @@ pub fn def_crates( ty: &Ty, cur_crate: CrateId, ) -> Option> { - // Types like slice can have inherent impls in several crates, (core and alloc). - // The corresponding impls are marked with lang items, so we can use them to find the required crates. - macro_rules! lang_item_crate { - ($($name:expr),+ $(,)?) => {{ - let mut v = ArrayVec::::new(); - $( - v.extend(db.lang_item(cur_crate, $name.into())); - )+ - v - }}; - } - let mod_to_crate_ids = |module: ModuleId| Some(iter::once(module.krate()).collect()); - let lang_item_targets = match ty.kind(Interner) { - TyKind::Adt(AdtId(def_id), _) => { - return mod_to_crate_ids(def_id.module(db.upcast())); - } + let fp = TyFingerprint::for_inherent_impl(ty); + + match ty.kind(Interner) { + TyKind::Adt(AdtId(def_id), _) => mod_to_crate_ids(def_id.module(db.upcast())), TyKind::Foreign(id) => { - return mod_to_crate_ids( - from_foreign_def_id(*id).lookup(db.upcast()).module(db.upcast()), - ); - } - TyKind::Scalar(Scalar::Bool) => lang_item_crate!("bool"), - TyKind::Scalar(Scalar::Char) => lang_item_crate!("char"), - TyKind::Scalar(Scalar::Float(f)) => match f { - // There are two lang items: one in libcore (fXX) and one in libstd (fXX_runtime) - FloatTy::F32 => lang_item_crate!("f32", "f32_runtime"), - FloatTy::F64 => lang_item_crate!("f64", "f64_runtime"), - }, - &TyKind::Scalar(Scalar::Int(t)) => { - lang_item_crate!(primitive::int_ty_to_string(t)) + mod_to_crate_ids(from_foreign_def_id(*id).lookup(db.upcast()).module(db.upcast())) } - &TyKind::Scalar(Scalar::Uint(t)) => { - lang_item_crate!(primitive::uint_ty_to_string(t)) - } - TyKind::Str => lang_item_crate!("str_alloc", "str"), - TyKind::Slice(_) => lang_item_crate!("slice_alloc", "slice"), - TyKind::Array(..) => lang_item_crate!("array"), - TyKind::Raw(Mutability::Not, _) => lang_item_crate!("const_ptr"), - TyKind::Raw(Mutability::Mut, _) => lang_item_crate!("mut_ptr"), - TyKind::Dyn(_) => { - return ty.dyn_trait().and_then(|trait_| { - mod_to_crate_ids(GenericDefId::TraitId(trait_).module(db.upcast())) - }); + TyKind::Dyn(_) => ty + .dyn_trait() + .and_then(|trait_| mod_to_crate_ids(GenericDefId::TraitId(trait_).module(db.upcast()))), + // for primitives, there may be impls in various places (core and alloc + // mostly). We just check the whole crate graph for crates with impls + // (cached behind a query). + TyKind::Scalar(_) + | TyKind::Str + | TyKind::Slice(_) + | TyKind::Array(..) + | TyKind::Raw(..) => { + Some(db.inherent_impl_crates(cur_crate, fp.expect("fingerprint for primitive"))) } _ => return None, - }; - let res = lang_item_targets - .into_iter() - .filter_map(|it| match it { - LangItemTarget::ImplDefId(it) => Some(it), - _ => None, - }) - .map(|it| it.lookup(db.upcast()).container.krate()) - .collect(); - Some(res) + } } /// Look up the method with the given name. diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs index 011347e5c45a..d97147541ad1 100644 --- a/crates/hir_ty/src/tests/method_resolution.rs +++ b/crates/hir_ty/src/tests/method_resolution.rs @@ -6,33 +6,39 @@ use super::{check_infer, check_no_mismatches, check_types}; #[test] fn infer_slice_method() { - check_infer( + check_types( r#" - #[lang = "slice"] - impl [T] { - fn foo(&self) -> T { - loop {} - } - } - - #[lang = "slice_alloc"] - impl [T] {} +impl [T] { + fn foo(&self) -> T { + loop {} + } +} - fn test(x: &[u8]) { - <[_]>::foo(x); - } +fn test(x: &[u8]) { + <[_]>::foo(x); + //^^^^^^^^^^^^^ u8 +} "#, - expect![[r#" - 44..48 'self': &[T] - 55..78 '{ ... }': T - 65..72 'loop {}': ! - 70..72 '{}': () - 130..131 'x': &[u8] - 140..162 '{ ...(x); }': () - 146..156 '<[_]>::foo': fn foo(&[u8]) -> u8 - 146..159 '<[_]>::foo(x)': u8 - 157..158 'x': &[u8] - "#]], + ); +} + +#[test] +fn cross_crate_primitive_method() { + check_types( + r#" +//- /main.rs crate:main deps:other_crate +fn test() { + let x = 1f32; + x.foo(); +} //^^^^^^^ f32 + +//- /lib.rs crate:other_crate +mod foo { + impl f32 { + pub fn foo(self) -> f32 { 0. } + } +} +"#, ); } @@ -40,16 +46,15 @@ fn infer_slice_method() { fn infer_array_inherent_impl() { check_types( r#" - #[lang = "array"] - impl [T; N] { - fn foo(&self) -> T { - loop {} - } - } - fn test(x: &[u8; 0]) { - <[_; 0]>::foo(x); - //^^^^^^^^^^^^^^^^ u8 - } +impl [T; N] { + fn foo(&self) -> T { + loop {} + } +} +fn test(x: &[u8; 0]) { + <[_; 0]>::foo(x); + //^^^^^^^^^^^^^^^^ u8 +} "#, ); }