Merge #1040
1040: Trait beginnings r=matklad a=flodiebold This adds some very simple trait method resolution, going through traits in scope, looking for methods of the given name, and checking very naively whether there's an impl for the given type and trait. Co-authored-by: Florian Diebold <flodiebold@gmail.com>
This commit is contained in:
commit
dc94f36125
12 changed files with 208 additions and 34 deletions
|
@ -17,6 +17,7 @@ use crate::{
|
|||
impl_block::ImplBlock,
|
||||
resolve::Resolver,
|
||||
diagnostics::DiagnosticSink,
|
||||
traits::{TraitItem, TraitData},
|
||||
};
|
||||
|
||||
/// hir::Crate describes a single crate. It's the main interface with which
|
||||
|
@ -649,6 +650,18 @@ impl Trait {
|
|||
pub fn generic_params(&self, db: &impl DefDatabase) -> Arc<GenericParams> {
|
||||
db.generic_params((*self).into())
|
||||
}
|
||||
|
||||
pub fn name(self, db: &impl DefDatabase) -> Option<Name> {
|
||||
self.trait_data(db).name().clone()
|
||||
}
|
||||
|
||||
pub fn items(self, db: &impl DefDatabase) -> Vec<TraitItem> {
|
||||
self.trait_data(db).items().to_vec()
|
||||
}
|
||||
|
||||
pub(crate) fn trait_data(self, db: &impl DefDatabase) -> Arc<TraitData> {
|
||||
db.trait_data(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Docs for Trait {
|
||||
|
|
|
@ -14,6 +14,7 @@ use crate::{
|
|||
impl_block::{ModuleImplBlocks, ImplSourceMap},
|
||||
generics::{GenericParams, GenericDef},
|
||||
type_ref::TypeRef,
|
||||
traits::TraitData, Trait, ty::TraitRef
|
||||
};
|
||||
|
||||
#[salsa::query_group(DefDatabaseStorage)]
|
||||
|
@ -27,6 +28,9 @@ pub trait DefDatabase: SourceDatabase + AsRef<HirInterner> {
|
|||
#[salsa::invoke(crate::adt::EnumData::enum_data_query)]
|
||||
fn enum_data(&self, e: Enum) -> Arc<EnumData>;
|
||||
|
||||
#[salsa::invoke(crate::traits::TraitData::trait_data_query)]
|
||||
fn trait_data(&self, t: Trait) -> Arc<TraitData>;
|
||||
|
||||
#[salsa::invoke(crate::ids::SourceFileItems::file_items_query)]
|
||||
fn file_items(&self, file_id: HirFileId) -> Arc<SourceFileItems>;
|
||||
|
||||
|
@ -98,6 +102,9 @@ pub trait HirDatabase: DefDatabase {
|
|||
|
||||
#[salsa::invoke(crate::ty::method_resolution::CrateImplBlocks::impls_in_crate_query)]
|
||||
fn impls_in_crate(&self, krate: Crate) -> Arc<CrateImplBlocks>;
|
||||
|
||||
#[salsa::invoke(crate::ty::method_resolution::implements)]
|
||||
fn implements(&self, trait_ref: TraitRef) -> bool;
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -27,6 +27,7 @@ mod ids;
|
|||
mod name;
|
||||
mod nameres;
|
||||
mod adt;
|
||||
mod traits;
|
||||
mod type_alias;
|
||||
mod type_ref;
|
||||
mod ty;
|
||||
|
|
|
@ -62,7 +62,7 @@ use test_utils::tested_by;
|
|||
|
||||
use crate::{
|
||||
ModuleDef, Name, Crate, Module,
|
||||
DefDatabase, Path, PathKind, HirFileId,
|
||||
DefDatabase, Path, PathKind, HirFileId, Trait,
|
||||
ids::{SourceItemId, SourceFileItemId, MacroCallId},
|
||||
diagnostics::DiagnosticSink,
|
||||
nameres::diagnostics::DefDiagnostic,
|
||||
|
@ -139,6 +139,12 @@ impl ModuleScope {
|
|||
pub fn get(&self, name: &Name) -> Option<&Resolution> {
|
||||
self.items.get(name)
|
||||
}
|
||||
pub fn traits<'a>(&'a self) -> impl Iterator<Item = Trait> + 'a {
|
||||
self.items.values().filter_map(|r| match r.def.take_types() {
|
||||
Some(ModuleDef::Trait(t)) => Some(t),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||
|
|
|
@ -11,7 +11,7 @@ use crate::{
|
|||
generics::GenericParams,
|
||||
expr::{scope::{ExprScopes, ScopeId}, PatId, Body},
|
||||
impl_block::ImplBlock,
|
||||
path::Path,
|
||||
path::Path, Trait
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
|
@ -175,6 +175,21 @@ impl Resolver {
|
|||
names
|
||||
}
|
||||
|
||||
pub(crate) fn traits_in_scope<'a>(&'a self) -> impl Iterator<Item = Trait> + 'a {
|
||||
// FIXME prelude
|
||||
self.scopes
|
||||
.iter()
|
||||
.rev()
|
||||
.flat_map(|scope| {
|
||||
match scope {
|
||||
Scope::ModuleScope(m) => Some(m.crate_def_map[m.module_id].scope.traits()),
|
||||
_ => None,
|
||||
}
|
||||
.into_iter()
|
||||
})
|
||||
.flatten()
|
||||
}
|
||||
|
||||
fn module(&self) -> Option<(&CrateDefMap, CrateModuleId)> {
|
||||
self.scopes.iter().rev().find_map(|scope| match scope {
|
||||
Scope::ModuleScope(m) => Some((&*m.crate_def_map, m.module_id)),
|
||||
|
|
52
crates/ra_hir/src/traits.rs
Normal file
52
crates/ra_hir/src/traits.rs
Normal file
|
@ -0,0 +1,52 @@
|
|||
//! HIR for trait definitions.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use ra_syntax::ast::{self, NameOwner};
|
||||
|
||||
use crate::{Function, Const, TypeAlias, Name, DefDatabase, Trait, ids::LocationCtx, name::AsName};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct TraitData {
|
||||
name: Option<Name>,
|
||||
items: Vec<TraitItem>,
|
||||
}
|
||||
|
||||
impl TraitData {
|
||||
pub(crate) fn trait_data_query(db: &impl DefDatabase, tr: Trait) -> Arc<TraitData> {
|
||||
let (file_id, node) = tr.source(db);
|
||||
let name = node.name().map(|n| n.as_name());
|
||||
let module = tr.module(db);
|
||||
let ctx = LocationCtx::new(db, module, file_id);
|
||||
let items = if let Some(item_list) = node.item_list() {
|
||||
item_list
|
||||
.impl_items()
|
||||
.map(|item_node| match item_node.kind() {
|
||||
ast::ImplItemKind::FnDef(it) => Function { id: ctx.to_def(it) }.into(),
|
||||
ast::ImplItemKind::ConstDef(it) => Const { id: ctx.to_def(it) }.into(),
|
||||
ast::ImplItemKind::TypeAliasDef(it) => TypeAlias { id: ctx.to_def(it) }.into(),
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
Arc::new(TraitData { name, items })
|
||||
}
|
||||
|
||||
pub(crate) fn name(&self) -> &Option<Name> {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub(crate) fn items(&self) -> &[TraitItem] {
|
||||
&self.items
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum TraitItem {
|
||||
Function(Function),
|
||||
Const(Const),
|
||||
TypeAlias(TypeAlias),
|
||||
// Existential
|
||||
}
|
||||
impl_froms!(TraitItem: Function, Const, TypeAlias);
|
|
@ -14,7 +14,7 @@ pub(crate) mod display;
|
|||
use std::sync::Arc;
|
||||
use std::{fmt, mem};
|
||||
|
||||
use crate::{Name, AdtDef, type_ref::Mutability, db::HirDatabase};
|
||||
use crate::{Name, AdtDef, type_ref::Mutability, db::HirDatabase, Trait};
|
||||
|
||||
pub(crate) use lower::{TypableDef, CallableDef, type_for_def, type_for_field, callable_item_sig};
|
||||
pub(crate) use infer::{infer, InferenceResult, InferTy};
|
||||
|
@ -91,7 +91,7 @@ pub enum TypeCtor {
|
|||
/// A nominal type with (maybe 0) type parameters. This might be a primitive
|
||||
/// type like `bool`, a struct, tuple, function pointer, reference or
|
||||
/// several other things.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct ApplicationTy {
|
||||
pub ctor: TypeCtor,
|
||||
pub parameters: Substs,
|
||||
|
@ -103,7 +103,7 @@ pub struct ApplicationTy {
|
|||
/// the same thing (but in a different way).
|
||||
///
|
||||
/// This should be cheap to clone.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum Ty {
|
||||
/// A nominal type with (maybe 0) type parameters. This might be a primitive
|
||||
/// type like `bool`, a struct, tuple, function pointer, reference or
|
||||
|
@ -132,7 +132,7 @@ pub enum Ty {
|
|||
}
|
||||
|
||||
/// A list of substitutions for generic parameters.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct Substs(Arc<[Ty]>);
|
||||
|
||||
impl Substs {
|
||||
|
@ -169,6 +169,21 @@ impl Substs {
|
|||
}
|
||||
}
|
||||
|
||||
/// A trait with type parameters. This includes the `Self`, so this represents a concrete type implementing the trait.
|
||||
/// Name to be bikeshedded: TraitBound? TraitImplements?
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct TraitRef {
|
||||
/// FIXME name?
|
||||
trait_: Trait,
|
||||
substs: Substs,
|
||||
}
|
||||
|
||||
impl TraitRef {
|
||||
pub fn self_ty(&self) -> &Ty {
|
||||
&self.substs.0[0]
|
||||
}
|
||||
}
|
||||
|
||||
/// A function signature as seen by type inference: Several parameter types and
|
||||
/// one return type.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
|
|
|
@ -821,7 +821,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
}
|
||||
Expr::MethodCall { receiver, args, method_name, generic_args } => {
|
||||
let receiver_ty = self.infer_expr(*receiver, &Expectation::none());
|
||||
let resolved = receiver_ty.clone().lookup_method(self.db, method_name);
|
||||
let resolved =
|
||||
receiver_ty.clone().lookup_method(self.db, method_name, &self.resolver);
|
||||
let (derefed_receiver_ty, method_ty, def_generics) = match resolved {
|
||||
Some((ty, func)) => {
|
||||
self.write_method_resolution(tgt_expr, func);
|
||||
|
|
|
@ -8,12 +8,12 @@ use rustc_hash::FxHashMap;
|
|||
|
||||
use crate::{
|
||||
HirDatabase, Module, Crate, Name, Function, Trait,
|
||||
ids::TraitId,
|
||||
impl_block::{ImplId, ImplBlock, ImplItem},
|
||||
ty::{Ty, TypeCtor},
|
||||
nameres::CrateModuleId,
|
||||
nameres::CrateModuleId, resolve::Resolver, traits::TraitItem
|
||||
|
||||
};
|
||||
use super::{ TraitRef, Substs};
|
||||
|
||||
/// This is used as a key for indexing impls.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
|
@ -38,7 +38,7 @@ pub struct CrateImplBlocks {
|
|||
/// To make sense of the CrateModuleIds, we need the source root.
|
||||
krate: Crate,
|
||||
impls: FxHashMap<TyFingerprint, Vec<(CrateModuleId, ImplId)>>,
|
||||
impls_by_trait: FxHashMap<TraitId, Vec<(CrateModuleId, ImplId)>>,
|
||||
impls_by_trait: FxHashMap<Trait, Vec<(CrateModuleId, ImplId)>>,
|
||||
}
|
||||
|
||||
impl CrateImplBlocks {
|
||||
|
@ -56,8 +56,7 @@ impl CrateImplBlocks {
|
|||
&'a self,
|
||||
tr: &Trait,
|
||||
) -> impl Iterator<Item = ImplBlock> + 'a {
|
||||
let id = tr.id;
|
||||
self.impls_by_trait.get(&id).into_iter().flat_map(|i| i.iter()).map(
|
||||
self.impls_by_trait.get(&tr).into_iter().flat_map(|i| i.iter()).map(
|
||||
move |(module_id, impl_id)| {
|
||||
let module = Module { krate: self.krate, module_id: *module_id };
|
||||
ImplBlock::from_id(module, *impl_id)
|
||||
|
@ -73,18 +72,18 @@ impl CrateImplBlocks {
|
|||
|
||||
let target_ty = impl_block.target_ty(db);
|
||||
|
||||
if let Some(target_ty_fp) = TyFingerprint::for_impl(&target_ty) {
|
||||
self.impls
|
||||
.entry(target_ty_fp)
|
||||
.or_insert_with(Vec::new)
|
||||
.push((module.module_id, impl_id));
|
||||
}
|
||||
|
||||
if let Some(tr) = impl_block.target_trait(db) {
|
||||
self.impls_by_trait
|
||||
.entry(tr.id)
|
||||
.entry(tr)
|
||||
.or_insert_with(Vec::new)
|
||||
.push((module.module_id, impl_id));
|
||||
} else {
|
||||
if let Some(target_ty_fp) = TyFingerprint::for_impl(&target_ty) {
|
||||
self.impls
|
||||
.entry(target_ty_fp)
|
||||
.or_insert_with(Vec::new)
|
||||
.push((module.module_id, impl_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,6 +108,20 @@ impl CrateImplBlocks {
|
|||
}
|
||||
}
|
||||
|
||||
/// Rudimentary check whether an impl exists for a given type and trait; this
|
||||
/// will actually be done by chalk.
|
||||
pub(crate) fn implements(db: &impl HirDatabase, trait_ref: TraitRef) -> bool {
|
||||
// FIXME use all trait impls in the whole crate graph
|
||||
let krate = trait_ref.trait_.module(db).krate(db);
|
||||
let krate = match krate {
|
||||
Some(krate) => krate,
|
||||
None => return false,
|
||||
};
|
||||
let crate_impl_blocks = db.impls_in_crate(krate);
|
||||
let mut impl_blocks = crate_impl_blocks.lookup_impl_blocks_for_trait(&trait_ref.trait_);
|
||||
impl_blocks.any(|impl_block| &impl_block.target_ty(db) == trait_ref.self_ty())
|
||||
}
|
||||
|
||||
fn def_crate(db: &impl HirDatabase, ty: &Ty) -> Option<Crate> {
|
||||
match ty {
|
||||
Ty::Apply(a_ty) => match a_ty.ctor {
|
||||
|
@ -120,20 +133,64 @@ fn def_crate(db: &impl HirDatabase, ty: &Ty) -> Option<Crate> {
|
|||
}
|
||||
|
||||
impl Ty {
|
||||
// FIXME: cache this as a query?
|
||||
// - if so, what signature? (TyFingerprint, Name)?
|
||||
// - or maybe cache all names and def_ids of methods per fingerprint?
|
||||
/// Look up the method with the given name, returning the actual autoderefed
|
||||
/// receiver type (but without autoref applied yet).
|
||||
pub fn lookup_method(self, db: &impl HirDatabase, name: &Name) -> Option<(Ty, Function)> {
|
||||
self.iterate_methods(db, |ty, f| {
|
||||
pub fn lookup_method(
|
||||
self,
|
||||
db: &impl HirDatabase,
|
||||
name: &Name,
|
||||
resolver: &Resolver,
|
||||
) -> Option<(Ty, Function)> {
|
||||
// FIXME: trait methods should be used before autoderefs
|
||||
let inherent_method = self.clone().iterate_methods(db, |ty, f| {
|
||||
let sig = f.signature(db);
|
||||
if sig.name() == name && sig.has_self_param() {
|
||||
Some((ty.clone(), f))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
});
|
||||
inherent_method.or_else(|| self.lookup_trait_method(db, name, resolver))
|
||||
}
|
||||
|
||||
fn lookup_trait_method(
|
||||
self,
|
||||
db: &impl HirDatabase,
|
||||
name: &Name,
|
||||
resolver: &Resolver,
|
||||
) -> Option<(Ty, Function)> {
|
||||
let mut candidates = Vec::new();
|
||||
for t in resolver.traits_in_scope() {
|
||||
let data = t.trait_data(db);
|
||||
for item in data.items() {
|
||||
match item {
|
||||
&TraitItem::Function(m) => {
|
||||
let sig = m.signature(db);
|
||||
if sig.name() == name && sig.has_self_param() {
|
||||
candidates.push((t, m));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
// FIXME:
|
||||
// - we might not actually be able to determine fully that the type
|
||||
// implements the trait here; it's enough if we (well, Chalk) determine
|
||||
// that it's possible.
|
||||
// - when the trait method is picked, we need to register an
|
||||
// 'obligation' somewhere so that we later check that it's really
|
||||
// implemented
|
||||
// - both points go for additional requirements from where clauses as
|
||||
// well (in fact, the 'implements' condition could just be considered a
|
||||
// 'where Self: Trait' clause)
|
||||
candidates.retain(|(t, _m)| {
|
||||
let trait_ref = TraitRef { trait_: *t, substs: Substs::single(self.clone()) };
|
||||
db.implements(trait_ref)
|
||||
});
|
||||
// FIXME if there's multiple candidates here, that's an ambiguity error
|
||||
let (_chosen_trait, chosen_method) = candidates.first()?;
|
||||
Some((self.clone(), *chosen_method))
|
||||
}
|
||||
|
||||
// This would be nicer if it just returned an iterator, but that runs into
|
||||
|
|
|
@ -1272,8 +1272,8 @@ fn test() {
|
|||
[241; 252) 'Struct::FOO': u32
|
||||
[262; 263) 'y': u32
|
||||
[266; 275) 'Enum::BAR': u32
|
||||
[285; 286) 'z': u32
|
||||
[289; 302) 'TraitTest::ID': u32"###
|
||||
[285; 286) 'z': {unknown}
|
||||
[289; 302) 'TraitTest::ID': {unknown}"###
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1918,9 +1918,9 @@ fn test() {
|
|||
[110; 114) 'self': &{unknown}
|
||||
[170; 228) '{ ...i128 }': ()
|
||||
[176; 178) 'S1': S1
|
||||
[176; 187) 'S1.method()': {unknown}
|
||||
[176; 187) 'S1.method()': u32
|
||||
[203; 205) 'S2': S2
|
||||
[203; 214) 'S2.method()': {unknown}"###
|
||||
[203; 214) 'S2.method()': i128"###
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1964,10 +1964,10 @@ mod bar_test {
|
|||
[169; 173) 'self': &{unknown}
|
||||
[300; 337) '{ ... }': ()
|
||||
[310; 311) 'S': S
|
||||
[310; 320) 'S.method()': {unknown}
|
||||
[310; 320) 'S.method()': u32
|
||||
[416; 454) '{ ... }': ()
|
||||
[426; 427) 'S': S
|
||||
[426; 436) 'S.method()': {unknown}"###
|
||||
[426; 436) 'S.method()': i128"###
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -4061,7 +4061,11 @@ impl ast::NameOwner for TraitDef {}
|
|||
impl ast::AttrsOwner for TraitDef {}
|
||||
impl ast::DocCommentsOwner for TraitDef {}
|
||||
impl ast::TypeParamsOwner for TraitDef {}
|
||||
impl TraitDef {}
|
||||
impl TraitDef {
|
||||
pub fn item_list(&self) -> Option<&ItemList> {
|
||||
super::child_opt(self)
|
||||
}
|
||||
}
|
||||
|
||||
// TrueKw
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
|
|
|
@ -292,7 +292,10 @@ Grammar(
|
|||
], options: [["variant_list", "EnumVariantList"]] ),
|
||||
"EnumVariantList": ( collections: [["variants", "EnumVariant"]] ),
|
||||
"EnumVariant": ( traits: ["NameOwner", "DocCommentsOwner", "AttrsOwner"], options: ["Expr"] ),
|
||||
"TraitDef": ( traits: ["VisibilityOwner", "NameOwner", "AttrsOwner", "DocCommentsOwner", "TypeParamsOwner"] ),
|
||||
"TraitDef": (
|
||||
traits: ["VisibilityOwner", "NameOwner", "AttrsOwner", "DocCommentsOwner", "TypeParamsOwner"],
|
||||
options: ["ItemList"]
|
||||
),
|
||||
"Module": (
|
||||
traits: ["VisibilityOwner", "NameOwner", "AttrsOwner", "DocCommentsOwner" ],
|
||||
options: [ "ItemList" ]
|
||||
|
|
Loading…
Add table
Reference in a new issue