mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 14:21:44 +00:00
Make block-local trait impls work
As long as either the trait or the implementing type are defined in the same block.
This commit is contained in:
parent
adbee621a7
commit
5ca71a1990
5 changed files with 154 additions and 36 deletions
|
@ -112,6 +112,10 @@ impl ModuleId {
|
||||||
self.def_map(db).containing_module(self.local_id)
|
self.def_map(db).containing_module(self.local_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn containing_block(&self) -> Option<BlockId> {
|
||||||
|
self.block
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns `true` if this module represents a block expression.
|
/// Returns `true` if this module represents a block expression.
|
||||||
///
|
///
|
||||||
/// Returns `false` if this module is a submodule *inside* a block expression
|
/// Returns `false` if this module is a submodule *inside* a block expression
|
||||||
|
@ -581,6 +585,18 @@ impl HasModule for GenericDefId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl HasModule for TypeAliasId {
|
||||||
|
fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
|
||||||
|
self.lookup(db).module(db)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasModule for TraitId {
|
||||||
|
fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
|
||||||
|
self.lookup(db).container
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl HasModule for StaticLoc {
|
impl HasModule for StaticLoc {
|
||||||
fn module(&self, _db: &dyn db::DefDatabase) -> ModuleId {
|
fn module(&self, _db: &dyn db::DefDatabase) -> ModuleId {
|
||||||
self.container
|
self.container
|
||||||
|
|
|
@ -10,16 +10,16 @@ use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait};
|
||||||
use base_db::CrateId;
|
use base_db::CrateId;
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
lang_item::{lang_attr, LangItemTarget},
|
lang_item::{lang_attr, LangItemTarget},
|
||||||
AssocContainerId, AssocItemId, GenericDefId, HasModule, Lookup, TypeAliasId,
|
AssocContainerId, AssocItemId, GenericDefId, HasModule, Lookup, ModuleId, TypeAliasId,
|
||||||
};
|
};
|
||||||
use hir_expand::name::name;
|
use hir_expand::name::name;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase,
|
db::HirDatabase,
|
||||||
display::HirDisplay,
|
display::HirDisplay,
|
||||||
from_assoc_type_id, from_chalk_trait_id, make_only_type_binders,
|
from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, make_only_type_binders,
|
||||||
mapping::{from_chalk, ToChalk, TypeAliasAsValue},
|
mapping::{from_chalk, ToChalk, TypeAliasAsValue},
|
||||||
method_resolution::{TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS},
|
method_resolution::{TraitImpls, TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS},
|
||||||
to_assoc_type_id, to_chalk_trait_id,
|
to_assoc_type_id, to_chalk_trait_id,
|
||||||
traits::ChalkContext,
|
traits::ChalkContext,
|
||||||
utils::generics,
|
utils::generics,
|
||||||
|
@ -105,12 +105,30 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
|
||||||
_ => self_ty_fp.as_ref().map(std::slice::from_ref).unwrap_or(&[]),
|
_ => self_ty_fp.as_ref().map(std::slice::from_ref).unwrap_or(&[]),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fn local_impls(db: &dyn HirDatabase, module: ModuleId) -> Option<Arc<TraitImpls>> {
|
||||||
|
db.trait_impls_in_block(module.containing_block()?)
|
||||||
|
}
|
||||||
|
|
||||||
// Note: Since we're using impls_for_trait, only impls where the trait
|
// Note: Since we're using impls_for_trait, only impls where the trait
|
||||||
// can be resolved should ever reach Chalk. Symbol’s value as variable is void: impl_datum relies on that
|
// can be resolved should ever reach Chalk. impl_datum relies on that
|
||||||
// and will panic if the trait can't be resolved.
|
// and will panic if the trait can't be resolved.
|
||||||
let in_deps = self.db.trait_impls_in_deps(self.krate);
|
let in_deps = self.db.trait_impls_in_deps(self.krate);
|
||||||
let in_self = self.db.trait_impls_in_crate(self.krate);
|
let in_self = self.db.trait_impls_in_crate(self.krate);
|
||||||
let impl_maps = [in_deps, in_self];
|
let trait_module = trait_.module(self.db.upcast());
|
||||||
|
let type_module = match self_ty_fp {
|
||||||
|
Some(TyFingerprint::Adt(adt_id)) => Some(adt_id.module(self.db.upcast())),
|
||||||
|
Some(TyFingerprint::ForeignType(type_id)) => {
|
||||||
|
Some(from_foreign_def_id(type_id).module(self.db.upcast()))
|
||||||
|
}
|
||||||
|
Some(TyFingerprint::Dyn(trait_id)) => Some(trait_id.module(self.db.upcast())),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
let impl_maps = [
|
||||||
|
Some(in_deps),
|
||||||
|
Some(in_self),
|
||||||
|
local_impls(self.db, trait_module),
|
||||||
|
type_module.and_then(|m| local_impls(self.db, m)),
|
||||||
|
];
|
||||||
|
|
||||||
let id_to_chalk = |id: hir_def::ImplId| id.to_chalk(self.db);
|
let id_to_chalk = |id: hir_def::ImplId| id.to_chalk(self.db);
|
||||||
|
|
||||||
|
@ -118,14 +136,16 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
|
||||||
debug!("Unrestricted search for {:?} impls...", trait_);
|
debug!("Unrestricted search for {:?} impls...", trait_);
|
||||||
impl_maps
|
impl_maps
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|crate_impl_defs| crate_impl_defs.for_trait(trait_).map(id_to_chalk))
|
.filter_map(|o| o.as_ref())
|
||||||
|
.flat_map(|impls| impls.for_trait(trait_).map(id_to_chalk))
|
||||||
.collect()
|
.collect()
|
||||||
} else {
|
} else {
|
||||||
impl_maps
|
impl_maps
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|crate_impl_defs| {
|
.filter_map(|o| o.as_ref())
|
||||||
|
.flat_map(|impls| {
|
||||||
fps.iter().flat_map(move |fp| {
|
fps.iter().flat_map(move |fp| {
|
||||||
crate_impl_defs.for_trait_and_self_ty(trait_, *fp).map(id_to_chalk)
|
impls.for_trait_and_self_ty(trait_, *fp).map(id_to_chalk)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
|
|
|
@ -5,8 +5,8 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use base_db::{impl_intern_key, salsa, CrateId, Upcast};
|
use base_db::{impl_intern_key, salsa, CrateId, Upcast};
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
db::DefDatabase, expr::ExprId, ConstParamId, DefWithBodyId, FunctionId, GenericDefId, ImplId,
|
db::DefDatabase, expr::ExprId, BlockId, ConstParamId, DefWithBodyId, FunctionId, GenericDefId,
|
||||||
LifetimeParamId, LocalFieldId, TypeParamId, VariantId,
|
ImplId, LifetimeParamId, LocalFieldId, TypeParamId, VariantId,
|
||||||
};
|
};
|
||||||
use la_arena::ArenaMap;
|
use la_arena::ArenaMap;
|
||||||
|
|
||||||
|
@ -79,6 +79,9 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
|
||||||
#[salsa::invoke(TraitImpls::trait_impls_in_crate_query)]
|
#[salsa::invoke(TraitImpls::trait_impls_in_crate_query)]
|
||||||
fn trait_impls_in_crate(&self, krate: CrateId) -> Arc<TraitImpls>;
|
fn trait_impls_in_crate(&self, krate: CrateId) -> Arc<TraitImpls>;
|
||||||
|
|
||||||
|
#[salsa::invoke(TraitImpls::trait_impls_in_block_query)]
|
||||||
|
fn trait_impls_in_block(&self, krate: BlockId) -> Option<Arc<TraitImpls>>;
|
||||||
|
|
||||||
#[salsa::invoke(TraitImpls::trait_impls_in_deps_query)]
|
#[salsa::invoke(TraitImpls::trait_impls_in_deps_query)]
|
||||||
fn trait_impls_in_deps(&self, krate: CrateId) -> Arc<TraitImpls>;
|
fn trait_impls_in_deps(&self, krate: CrateId) -> Arc<TraitImpls>;
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ use arrayvec::ArrayVec;
|
||||||
use base_db::{CrateId, Edition};
|
use base_db::{CrateId, Edition};
|
||||||
use chalk_ir::{cast::Cast, Mutability, UniverseIndex};
|
use chalk_ir::{cast::Cast, Mutability, UniverseIndex};
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
lang_item::LangItemTarget, nameres::DefMap, AssocContainerId, AssocItemId, FunctionId,
|
lang_item::LangItemTarget, nameres::DefMap, AssocContainerId, AssocItemId, BlockId, FunctionId,
|
||||||
GenericDefId, HasModule, ImplId, Lookup, ModuleId, TraitId,
|
GenericDefId, HasModule, ImplId, Lookup, ModuleId, TraitId,
|
||||||
};
|
};
|
||||||
use hir_expand::name::Name;
|
use hir_expand::name::Name;
|
||||||
|
@ -139,35 +139,47 @@ impl TraitImpls {
|
||||||
let mut impls = Self { map: FxHashMap::default() };
|
let mut impls = Self { map: FxHashMap::default() };
|
||||||
|
|
||||||
let crate_def_map = db.crate_def_map(krate);
|
let crate_def_map = db.crate_def_map(krate);
|
||||||
collect_def_map(db, &crate_def_map, &mut impls);
|
impls.collect_def_map(db, &crate_def_map);
|
||||||
|
|
||||||
return Arc::new(impls);
|
return Arc::new(impls);
|
||||||
|
}
|
||||||
|
|
||||||
fn collect_def_map(db: &dyn HirDatabase, def_map: &DefMap, impls: &mut TraitImpls) {
|
pub(crate) fn trait_impls_in_block_query(
|
||||||
for (_module_id, module_data) in def_map.modules() {
|
db: &dyn HirDatabase,
|
||||||
for impl_id in module_data.scope.impls() {
|
block: BlockId,
|
||||||
let target_trait = match db.impl_trait(impl_id) {
|
) -> Option<Arc<Self>> {
|
||||||
Some(tr) => tr.skip_binders().hir_trait_id(),
|
let _p = profile::span("trait_impls_in_block_query");
|
||||||
None => continue,
|
let mut impls = Self { map: FxHashMap::default() };
|
||||||
};
|
|
||||||
let self_ty = db.impl_self_ty(impl_id);
|
|
||||||
let self_ty_fp = TyFingerprint::for_trait_impl(self_ty.skip_binders());
|
|
||||||
impls
|
|
||||||
.map
|
|
||||||
.entry(target_trait)
|
|
||||||
.or_default()
|
|
||||||
.entry(self_ty_fp)
|
|
||||||
.or_default()
|
|
||||||
.push(impl_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// To better support custom derives, collect impls in all unnamed const items.
|
let block_def_map = db.block_def_map(block)?;
|
||||||
// const _: () = { ... };
|
impls.collect_def_map(db, &block_def_map);
|
||||||
for konst in module_data.scope.unnamed_consts() {
|
|
||||||
let body = db.body(konst.into());
|
return Some(Arc::new(impls));
|
||||||
for (_, block_def_map) in body.blocks(db.upcast()) {
|
}
|
||||||
collect_def_map(db, &block_def_map, impls);
|
|
||||||
}
|
fn collect_def_map(&mut self, db: &dyn HirDatabase, def_map: &DefMap) {
|
||||||
|
for (_module_id, module_data) in def_map.modules() {
|
||||||
|
for impl_id in module_data.scope.impls() {
|
||||||
|
let target_trait = match db.impl_trait(impl_id) {
|
||||||
|
Some(tr) => tr.skip_binders().hir_trait_id(),
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
let self_ty = db.impl_self_ty(impl_id);
|
||||||
|
let self_ty_fp = TyFingerprint::for_trait_impl(self_ty.skip_binders());
|
||||||
|
self.map
|
||||||
|
.entry(target_trait)
|
||||||
|
.or_default()
|
||||||
|
.entry(self_ty_fp)
|
||||||
|
.or_default()
|
||||||
|
.push(impl_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// To better support custom derives, collect impls in all unnamed const items.
|
||||||
|
// const _: () = { ... };
|
||||||
|
for konst in module_data.scope.unnamed_consts() {
|
||||||
|
let body = db.body(konst.into());
|
||||||
|
for (_, block_def_map) in body.blocks(db.upcast()) {
|
||||||
|
self.collect_def_map(db, &block_def_map);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3740,3 +3740,70 @@ mod future {
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn local_impl_1() {
|
||||||
|
check_types(
|
||||||
|
r#"
|
||||||
|
trait Trait<T> {
|
||||||
|
fn foo(&self) -> T;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test() {
|
||||||
|
struct S;
|
||||||
|
impl Trait<u32> for S {
|
||||||
|
fn foo(&self) { 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
S.foo();
|
||||||
|
// ^^^^^^^ u32
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn local_impl_2() {
|
||||||
|
check_types(
|
||||||
|
r#"
|
||||||
|
struct S;
|
||||||
|
|
||||||
|
fn test() {
|
||||||
|
trait Trait<T> {
|
||||||
|
fn foo(&self) -> T;
|
||||||
|
}
|
||||||
|
impl Trait<u32> for S {
|
||||||
|
fn foo(&self) { 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
S.foo();
|
||||||
|
// ^^^^^^^ u32
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn local_impl_3() {
|
||||||
|
check_types(
|
||||||
|
r#"
|
||||||
|
trait Trait<T> {
|
||||||
|
fn foo(&self) -> T;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test() {
|
||||||
|
struct S1;
|
||||||
|
{
|
||||||
|
struct S2;
|
||||||
|
|
||||||
|
impl Trait<S1> for S2 {
|
||||||
|
fn foo(&self) { S1 }
|
||||||
|
}
|
||||||
|
|
||||||
|
S2.foo();
|
||||||
|
// ^^^^^^^^ S1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue