mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-28 04:44:57 +00:00
More trait infrastructure
- make it possible to get parent trait from method - add 'obligation' machinery for checking that a type implements a trait (and inferring facts about type variables from that) - handle type parameters of traits (to a certain degree) - improve the hacky implements check to cover enough cases to exercise the handling of traits with type parameters - basic canonicalization (will probably also be done by Chalk)
This commit is contained in:
parent
413c87f155
commit
a1ed53a4f1
11 changed files with 333 additions and 51 deletions
|
@ -194,7 +194,7 @@ impl Module {
|
||||||
Resolver::default().push_module_scope(def_map, self.module_id)
|
Resolver::default().push_module_scope(def_map, self.module_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn declarations(self, db: &impl HirDatabase) -> Vec<ModuleDef> {
|
pub fn declarations(self, db: &impl DefDatabase) -> Vec<ModuleDef> {
|
||||||
let def_map = db.crate_def_map(self.krate);
|
let def_map = db.crate_def_map(self.krate);
|
||||||
def_map[self.module_id]
|
def_map[self.module_id]
|
||||||
.scope
|
.scope
|
||||||
|
@ -547,13 +547,20 @@ impl Function {
|
||||||
ImplBlock::containing(module_impls, (*self).into())
|
ImplBlock::containing(module_impls, (*self).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The containing trait, if this is a trait method definition.
|
||||||
|
pub fn parent_trait(&self, db: &impl DefDatabase) -> Option<Trait> {
|
||||||
|
db.trait_items_index(self.module(db)).get_parent_trait((*self).into())
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: move to a more general type for 'body-having' items
|
// FIXME: move to a more general type for 'body-having' items
|
||||||
/// Builds a resolver for code inside this item.
|
/// Builds a resolver for code inside this item.
|
||||||
pub(crate) fn resolver(&self, db: &impl HirDatabase) -> Resolver {
|
pub(crate) fn resolver(&self, db: &impl HirDatabase) -> Resolver {
|
||||||
// take the outer scope...
|
// take the outer scope...
|
||||||
|
// FIXME abstract over containers (trait/impl)
|
||||||
let r = self
|
let r = self
|
||||||
.impl_block(db)
|
.impl_block(db)
|
||||||
.map(|ib| ib.resolver(db))
|
.map(|ib| ib.resolver(db))
|
||||||
|
.or_else(|| self.parent_trait(db).map(|tr| tr.resolver(db)))
|
||||||
.unwrap_or_else(|| self.module(db).resolver(db));
|
.unwrap_or_else(|| self.module(db).resolver(db));
|
||||||
// ...and add generic params, if present
|
// ...and add generic params, if present
|
||||||
let p = self.generic_params(db);
|
let p = self.generic_params(db);
|
||||||
|
@ -699,6 +706,14 @@ impl Trait {
|
||||||
pub(crate) fn trait_data(self, db: &impl DefDatabase) -> Arc<TraitData> {
|
pub(crate) fn trait_data(self, db: &impl DefDatabase) -> Arc<TraitData> {
|
||||||
db.trait_data(self)
|
db.trait_data(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resolver(&self, db: &impl HirDatabase) -> Resolver {
|
||||||
|
let r = self.module(db).resolver(db);
|
||||||
|
// add generic params, if present
|
||||||
|
let p = self.generic_params(db);
|
||||||
|
let r = if !p.params.is_empty() { r.push_generic_params_scope(p) } else { r };
|
||||||
|
r
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Docs for Trait {
|
impl Docs for Trait {
|
||||||
|
|
|
@ -53,6 +53,9 @@ pub trait DefDatabase: SourceDatabase {
|
||||||
#[salsa::invoke(crate::traits::TraitData::trait_data_query)]
|
#[salsa::invoke(crate::traits::TraitData::trait_data_query)]
|
||||||
fn trait_data(&self, t: Trait) -> Arc<TraitData>;
|
fn trait_data(&self, t: Trait) -> Arc<TraitData>;
|
||||||
|
|
||||||
|
#[salsa::invoke(crate::traits::TraitItemsIndex::trait_items_index)]
|
||||||
|
fn trait_items_index(&self, module: Module) -> crate::traits::TraitItemsIndex;
|
||||||
|
|
||||||
#[salsa::invoke(crate::source_id::AstIdMap::ast_id_map_query)]
|
#[salsa::invoke(crate::source_id::AstIdMap::ast_id_map_query)]
|
||||||
fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>;
|
fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>;
|
||||||
|
|
||||||
|
@ -128,8 +131,8 @@ pub trait HirDatabase: DefDatabase {
|
||||||
#[salsa::invoke(crate::ty::method_resolution::CrateImplBlocks::impls_in_crate_query)]
|
#[salsa::invoke(crate::ty::method_resolution::CrateImplBlocks::impls_in_crate_query)]
|
||||||
fn impls_in_crate(&self, krate: Crate) -> Arc<CrateImplBlocks>;
|
fn impls_in_crate(&self, krate: Crate) -> Arc<CrateImplBlocks>;
|
||||||
|
|
||||||
#[salsa::invoke(crate::ty::method_resolution::implements)]
|
#[salsa::invoke(crate::ty::traits::implements)]
|
||||||
fn implements(&self, trait_ref: TraitRef) -> bool;
|
fn implements(&self, trait_ref: TraitRef) -> Option<crate::ty::traits::Solution>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -45,12 +45,16 @@ impl GenericParams {
|
||||||
) -> Arc<GenericParams> {
|
) -> Arc<GenericParams> {
|
||||||
let mut generics = GenericParams::default();
|
let mut generics = GenericParams::default();
|
||||||
let parent = match def {
|
let parent = match def {
|
||||||
GenericDef::Function(it) => it.impl_block(db),
|
// FIXME abstract over containers (trait/impl)
|
||||||
GenericDef::TypeAlias(it) => it.impl_block(db),
|
GenericDef::Function(it) => it
|
||||||
|
.impl_block(db)
|
||||||
|
.map(GenericDef::from)
|
||||||
|
.or_else(|| it.parent_trait(db).map(GenericDef::from)),
|
||||||
|
GenericDef::TypeAlias(it) => it.impl_block(db).map(GenericDef::from),
|
||||||
GenericDef::Struct(_) | GenericDef::Enum(_) | GenericDef::Trait(_) => None,
|
GenericDef::Struct(_) | GenericDef::Enum(_) | GenericDef::Trait(_) => None,
|
||||||
GenericDef::ImplBlock(_) => None,
|
GenericDef::ImplBlock(_) => None,
|
||||||
};
|
};
|
||||||
generics.parent_params = parent.map(|p| p.generic_params(db));
|
generics.parent_params = parent.map(|p| db.generic_params(p));
|
||||||
let start = generics.parent_params.as_ref().map(|p| p.params.len()).unwrap_or(0) as u32;
|
let start = generics.parent_params.as_ref().map(|p| p.params.len()).unwrap_or(0) as u32;
|
||||||
match def {
|
match def {
|
||||||
GenericDef::Function(it) => generics.fill(&*it.source(db).1, start),
|
GenericDef::Function(it) => generics.fill(&*it.source(db).1, start),
|
||||||
|
|
|
@ -84,7 +84,8 @@ impl ImplBlock {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn target_trait_ref(&self, db: &impl HirDatabase) -> Option<TraitRef> {
|
pub fn target_trait_ref(&self, db: &impl HirDatabase) -> Option<TraitRef> {
|
||||||
TraitRef::from_hir(db, &self.resolver(db), &self.target_trait(db)?)
|
let target_ty = self.target_ty(db);
|
||||||
|
TraitRef::from_hir(db, &self.resolver(db), &self.target_trait(db)?, Some(target_ty))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn items(&self, db: &impl DefDatabase) -> Vec<ImplItem> {
|
pub fn items(&self, db: &impl DefDatabase) -> Vec<ImplItem> {
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
//! HIR for trait definitions.
|
//! HIR for trait definitions.
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use ra_syntax::ast::{self, NameOwner};
|
use ra_syntax::ast::{self, NameOwner};
|
||||||
|
|
||||||
use crate::{Function, Const, TypeAlias, Name, DefDatabase, Trait, ids::LocationCtx, name::AsName};
|
use crate::{Function, Const, TypeAlias, Name, DefDatabase, Trait, ids::LocationCtx, name::AsName, Module};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct TraitData {
|
pub struct TraitData {
|
||||||
|
@ -49,4 +50,34 @@ pub enum TraitItem {
|
||||||
TypeAlias(TypeAlias),
|
TypeAlias(TypeAlias),
|
||||||
// Existential
|
// Existential
|
||||||
}
|
}
|
||||||
|
// FIXME: not every function, ... is actually a trait item. maybe we should make
|
||||||
|
// sure that you can only turn actual trait items into TraitItems. This would
|
||||||
|
// require not implementing From, and instead having some checked way of
|
||||||
|
// casting them.
|
||||||
impl_froms!(TraitItem: Function, Const, TypeAlias);
|
impl_froms!(TraitItem: Function, Const, TypeAlias);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct TraitItemsIndex {
|
||||||
|
traits_by_def: FxHashMap<TraitItem, Trait>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TraitItemsIndex {
|
||||||
|
pub(crate) fn trait_items_index(db: &impl DefDatabase, module: Module) -> TraitItemsIndex {
|
||||||
|
let mut index = TraitItemsIndex { traits_by_def: FxHashMap::default() };
|
||||||
|
for decl in module.declarations(db) {
|
||||||
|
match decl {
|
||||||
|
crate::ModuleDef::Trait(tr) => {
|
||||||
|
for item in tr.trait_data(db).items() {
|
||||||
|
index.traits_by_def.insert(*item, tr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_parent_trait(&self, item: TraitItem) -> Option<Trait> {
|
||||||
|
self.traits_by_def.get(&item).cloned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ mod autoderef;
|
||||||
pub(crate) mod primitive;
|
pub(crate) mod primitive;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
pub(crate) mod traits;
|
||||||
pub(crate) mod method_resolution;
|
pub(crate) mod method_resolution;
|
||||||
mod op;
|
mod op;
|
||||||
mod lower;
|
mod lower;
|
||||||
|
@ -145,6 +146,10 @@ impl Substs {
|
||||||
Substs(Arc::new([ty]))
|
Substs(Arc::new([ty]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn prefix(&self, n: usize) -> Substs {
|
||||||
|
Substs(self.0.iter().cloned().take(n).collect::<Vec<_>>().into())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &Ty> {
|
pub fn iter(&self) -> impl Iterator<Item = &Ty> {
|
||||||
self.0.iter()
|
self.0.iter()
|
||||||
}
|
}
|
||||||
|
@ -170,6 +175,12 @@ impl Substs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Vec<Ty>> for Substs {
|
||||||
|
fn from(v: Vec<Ty>) -> Self {
|
||||||
|
Substs(v.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A trait with type parameters. This includes the `Self`, so this represents a concrete type implementing the trait.
|
/// A trait with type parameters. This includes the `Self`, so this represents a concrete type implementing the trait.
|
||||||
/// Name to be bikeshedded: TraitBound? TraitImplements?
|
/// Name to be bikeshedded: TraitBound? TraitImplements?
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||||
|
|
|
@ -41,7 +41,7 @@ use crate::{
|
||||||
ty::infer::diagnostics::InferenceDiagnostic,
|
ty::infer::diagnostics::InferenceDiagnostic,
|
||||||
diagnostics::DiagnosticSink,
|
diagnostics::DiagnosticSink,
|
||||||
};
|
};
|
||||||
use super::{Ty, TypableDef, Substs, primitive, op, FnSig, ApplicationTy, TypeCtor};
|
use super::{Ty, TypableDef, Substs, primitive, op, FnSig, ApplicationTy, TypeCtor, traits::{ Solution, Obligation, Guidance}, CallableDef, TraitRef};
|
||||||
|
|
||||||
/// The entry point of type inference.
|
/// The entry point of type inference.
|
||||||
pub fn infer(db: &impl HirDatabase, def: DefWithBody) -> Arc<InferenceResult> {
|
pub fn infer(db: &impl HirDatabase, def: DefWithBody) -> Arc<InferenceResult> {
|
||||||
|
@ -153,6 +153,7 @@ struct InferenceContext<'a, D: HirDatabase> {
|
||||||
body: Arc<Body>,
|
body: Arc<Body>,
|
||||||
resolver: Resolver,
|
resolver: Resolver,
|
||||||
var_unification_table: InPlaceUnificationTable<TypeVarId>,
|
var_unification_table: InPlaceUnificationTable<TypeVarId>,
|
||||||
|
obligations: Vec<Obligation>,
|
||||||
method_resolutions: FxHashMap<ExprId, Function>,
|
method_resolutions: FxHashMap<ExprId, Function>,
|
||||||
field_resolutions: FxHashMap<ExprId, StructField>,
|
field_resolutions: FxHashMap<ExprId, StructField>,
|
||||||
assoc_resolutions: FxHashMap<ExprOrPatId, ImplItem>,
|
assoc_resolutions: FxHashMap<ExprOrPatId, ImplItem>,
|
||||||
|
@ -173,6 +174,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
type_of_pat: ArenaMap::default(),
|
type_of_pat: ArenaMap::default(),
|
||||||
diagnostics: Vec::default(),
|
diagnostics: Vec::default(),
|
||||||
var_unification_table: InPlaceUnificationTable::new(),
|
var_unification_table: InPlaceUnificationTable::new(),
|
||||||
|
obligations: Vec::default(),
|
||||||
return_ty: Ty::Unknown, // set in collect_fn_signature
|
return_ty: Ty::Unknown, // set in collect_fn_signature
|
||||||
db,
|
db,
|
||||||
body,
|
body,
|
||||||
|
@ -181,6 +183,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_all(mut self) -> InferenceResult {
|
fn resolve_all(mut self) -> InferenceResult {
|
||||||
|
// FIXME resolve obligations as well (use Guidance if necessary)
|
||||||
let mut tv_stack = Vec::new();
|
let mut tv_stack = Vec::new();
|
||||||
let mut expr_types = mem::replace(&mut self.type_of_expr, ArenaMap::default());
|
let mut expr_types = mem::replace(&mut self.type_of_expr, ArenaMap::default());
|
||||||
for ty in expr_types.values_mut() {
|
for ty in expr_types.values_mut() {
|
||||||
|
@ -311,11 +314,49 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
ty.fold(&mut |ty| self.insert_type_vars_shallow(ty))
|
ty.fold(&mut |ty| self.insert_type_vars_shallow(ty))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_obligations_as_possible(&mut self) {
|
||||||
|
let obligations = mem::replace(&mut self.obligations, Vec::new());
|
||||||
|
for obligation in obligations {
|
||||||
|
// FIXME resolve types in the obligation first
|
||||||
|
let (solution, var_mapping) = match &obligation {
|
||||||
|
Obligation::Trait(tr) => {
|
||||||
|
let (tr, var_mapping) = super::traits::canonicalize(tr.clone());
|
||||||
|
(self.db.implements(tr), var_mapping)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match solution {
|
||||||
|
Some(Solution::Unique(substs)) => {
|
||||||
|
for (i, subst) in substs.0.iter().enumerate() {
|
||||||
|
let uncanonical = var_mapping[i];
|
||||||
|
// FIXME the subst may contain type variables, which would need to be mapped back as well
|
||||||
|
self.unify(&Ty::Infer(InferTy::TypeVar(uncanonical)), subst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(Solution::Ambig(Guidance::Definite(substs))) => {
|
||||||
|
for (i, subst) in substs.0.iter().enumerate() {
|
||||||
|
let uncanonical = var_mapping[i];
|
||||||
|
// FIXME the subst may contain type variables, which would need to be mapped back as well
|
||||||
|
self.unify(&Ty::Infer(InferTy::TypeVar(uncanonical)), subst);
|
||||||
|
}
|
||||||
|
self.obligations.push(obligation);
|
||||||
|
}
|
||||||
|
Some(_) => {
|
||||||
|
self.obligations.push(obligation);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// FIXME obligation cannot be fulfilled => diagnostic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Resolves the type as far as currently possible, replacing type variables
|
/// Resolves the type as far as currently possible, replacing type variables
|
||||||
/// by their known types. All types returned by the infer_* functions should
|
/// by their known types. All types returned by the infer_* functions should
|
||||||
/// be resolved as far as possible, i.e. contain no type variables with
|
/// be resolved as far as possible, i.e. contain no type variables with
|
||||||
/// known type.
|
/// known type.
|
||||||
fn resolve_ty_as_possible(&mut self, tv_stack: &mut Vec<TypeVarId>, ty: Ty) -> Ty {
|
fn resolve_ty_as_possible(&mut self, tv_stack: &mut Vec<TypeVarId>, ty: Ty) -> Ty {
|
||||||
|
self.resolve_obligations_as_possible();
|
||||||
|
|
||||||
ty.fold(&mut |ty| match ty {
|
ty.fold(&mut |ty| match ty {
|
||||||
Ty::Infer(tv) => {
|
Ty::Infer(tv) => {
|
||||||
let inner = tv.to_inner();
|
let inner = tv.to_inner();
|
||||||
|
@ -710,13 +751,20 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
&mut self,
|
&mut self,
|
||||||
def_generics: Option<Arc<GenericParams>>,
|
def_generics: Option<Arc<GenericParams>>,
|
||||||
generic_args: &Option<GenericArgs>,
|
generic_args: &Option<GenericArgs>,
|
||||||
|
receiver_ty: &Ty,
|
||||||
) -> Substs {
|
) -> Substs {
|
||||||
let (parent_param_count, param_count) =
|
let (parent_param_count, param_count) =
|
||||||
def_generics.map_or((0, 0), |g| (g.count_parent_params(), g.params.len()));
|
def_generics.as_ref().map_or((0, 0), |g| (g.count_parent_params(), g.params.len()));
|
||||||
let mut substs = Vec::with_capacity(parent_param_count + param_count);
|
let mut substs = Vec::with_capacity(parent_param_count + param_count);
|
||||||
for _ in 0..parent_param_count {
|
if let Some(parent_generics) = def_generics.and_then(|p| p.parent_params.clone()) {
|
||||||
|
for param in &parent_generics.params {
|
||||||
|
if param.name.as_known_name() == Some(crate::KnownName::SelfType) {
|
||||||
|
substs.push(receiver_ty.clone());
|
||||||
|
} else {
|
||||||
substs.push(Ty::Unknown);
|
substs.push(Ty::Unknown);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// handle provided type arguments
|
// handle provided type arguments
|
||||||
if let Some(generic_args) = generic_args {
|
if let Some(generic_args) = generic_args {
|
||||||
// if args are provided, it should be all of them, but we can't rely on that
|
// if args are provided, it should be all of them, but we can't rely on that
|
||||||
|
@ -817,6 +865,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
(Vec::new(), Ty::Unknown)
|
(Vec::new(), Ty::Unknown)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
// FIXME register obligations from where clauses from the function
|
||||||
let param_iter = param_tys.into_iter().chain(repeat(Ty::Unknown));
|
let param_iter = param_tys.into_iter().chain(repeat(Ty::Unknown));
|
||||||
for (arg, param) in args.iter().zip(param_iter) {
|
for (arg, param) in args.iter().zip(param_iter) {
|
||||||
self.infer_expr(*arg, &Expectation::has_type(param));
|
self.infer_expr(*arg, &Expectation::has_type(param));
|
||||||
|
@ -838,7 +887,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
}
|
}
|
||||||
None => (receiver_ty, Ty::Unknown, None),
|
None => (receiver_ty, Ty::Unknown, None),
|
||||||
};
|
};
|
||||||
let substs = self.substs_for_method_call(def_generics, generic_args);
|
let substs = self.substs_for_method_call(
|
||||||
|
def_generics.clone(),
|
||||||
|
generic_args,
|
||||||
|
&derefed_receiver_ty,
|
||||||
|
);
|
||||||
let method_ty = method_ty.apply_substs(substs);
|
let method_ty = method_ty.apply_substs(substs);
|
||||||
let method_ty = self.insert_type_vars(method_ty);
|
let method_ty = self.insert_type_vars(method_ty);
|
||||||
let (expected_receiver_ty, param_tys, ret_ty) = match &method_ty {
|
let (expected_receiver_ty, param_tys, ret_ty) = match &method_ty {
|
||||||
|
@ -859,6 +912,24 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
let sig = self.db.callable_item_signature(def);
|
let sig = self.db.callable_item_signature(def);
|
||||||
let ret_ty = sig.ret().clone().subst(&a_ty.parameters);
|
let ret_ty = sig.ret().clone().subst(&a_ty.parameters);
|
||||||
|
|
||||||
|
// add obligation for trait implementation, if this is a trait method
|
||||||
|
// FIXME also register obligations from where clauses from the trait or impl and method
|
||||||
|
match def {
|
||||||
|
CallableDef::Function(f) => {
|
||||||
|
if let Some(trait_) = f.parent_trait(self.db) {
|
||||||
|
// construct a TraitDef
|
||||||
|
let substs = a_ty.parameters.prefix(
|
||||||
|
def_generics
|
||||||
|
.expect("trait parent should always have generics")
|
||||||
|
.count_parent_params(),
|
||||||
|
);
|
||||||
|
self.obligations
|
||||||
|
.push(Obligation::Trait(TraitRef { trait_, substs }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CallableDef::Struct(_) | CallableDef::EnumVariant(_) => {}
|
||||||
|
}
|
||||||
|
|
||||||
if !sig.params().is_empty() {
|
if !sig.params().is_empty() {
|
||||||
let mut params_iter = sig
|
let mut params_iter = sig
|
||||||
.params()
|
.params()
|
||||||
|
@ -875,6 +946,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
_ => (Ty::Unknown, Vec::new(), Ty::Unknown),
|
_ => (Ty::Unknown, Vec::new(), Ty::Unknown),
|
||||||
};
|
};
|
||||||
// Apply autoref so the below unification works correctly
|
// Apply autoref so the below unification works correctly
|
||||||
|
// FIXME: return correct autorefs/derefs from lookup_method
|
||||||
let actual_receiver_ty = match expected_receiver_ty.as_reference() {
|
let actual_receiver_ty = match expected_receiver_ty.as_reference() {
|
||||||
Some((_, mutability)) => {
|
Some((_, mutability)) => {
|
||||||
Ty::apply_one(TypeCtor::Ref(mutability), derefed_receiver_ty)
|
Ty::apply_one(TypeCtor::Ref(mutability), derefed_receiver_ty)
|
||||||
|
@ -1180,7 +1252,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
|
|
||||||
/// The ID of a type variable.
|
/// The ID of a type variable.
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
pub struct TypeVarId(u32);
|
pub struct TypeVarId(pub(super) u32);
|
||||||
|
|
||||||
impl UnifyKey for TypeVarId {
|
impl UnifyKey for TypeVarId {
|
||||||
type Value = TypeVarValue;
|
type Value = TypeVarValue;
|
||||||
|
|
|
@ -206,6 +206,7 @@ impl TraitRef {
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
resolver: &Resolver,
|
resolver: &Resolver,
|
||||||
type_ref: &TypeRef,
|
type_ref: &TypeRef,
|
||||||
|
explicit_self_ty: Option<Ty>,
|
||||||
) -> Option<Self> {
|
) -> Option<Self> {
|
||||||
let path = match type_ref {
|
let path = match type_ref {
|
||||||
TypeRef::Path(path) => path,
|
TypeRef::Path(path) => path,
|
||||||
|
@ -215,7 +216,13 @@ impl TraitRef {
|
||||||
Resolution::Def(ModuleDef::Trait(tr)) => tr,
|
Resolution::Def(ModuleDef::Trait(tr)) => tr,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
let substs = Self::substs_from_path(db, resolver, path, resolved);
|
let mut substs = Self::substs_from_path(db, resolver, path, resolved);
|
||||||
|
if let Some(self_ty) = explicit_self_ty {
|
||||||
|
// FIXME this could be nicer
|
||||||
|
let mut substs_vec = substs.0.to_vec();
|
||||||
|
substs_vec[0] = self_ty;
|
||||||
|
substs.0 = substs_vec.into();
|
||||||
|
}
|
||||||
Some(TraitRef { trait_: resolved, substs })
|
Some(TraitRef { trait_: resolved, substs })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -108,20 +108,6 @@ 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> {
|
fn def_crate(db: &impl HirDatabase, ty: &Ty) -> Option<Crate> {
|
||||||
match ty {
|
match ty {
|
||||||
Ty::Apply(a_ty) => match a_ty.ctor {
|
Ty::Apply(a_ty) => match a_ty.ctor {
|
||||||
|
@ -142,6 +128,7 @@ impl Ty {
|
||||||
resolver: &Resolver,
|
resolver: &Resolver,
|
||||||
) -> Option<(Ty, Function)> {
|
) -> Option<(Ty, Function)> {
|
||||||
// FIXME: trait methods should be used before autoderefs
|
// FIXME: trait methods should be used before autoderefs
|
||||||
|
// (and we need to do autoderefs for trait method calls as well)
|
||||||
let inherent_method = self.clone().iterate_methods(db, |ty, f| {
|
let inherent_method = self.clone().iterate_methods(db, |ty, f| {
|
||||||
let sig = f.signature(db);
|
let sig = f.signature(db);
|
||||||
if sig.name() == name && sig.has_self_param() {
|
if sig.name() == name && sig.has_self_param() {
|
||||||
|
@ -174,24 +161,15 @@ impl Ty {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 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)| {
|
candidates.retain(|(t, _m)| {
|
||||||
// FIXME construct substs of the correct length for the trait
|
let trait_ref =
|
||||||
// - check in rustc whether it does anything smarter than putting variables for everything
|
TraitRef { trait_: *t, substs: fresh_substs_for_trait(db, *t, self.clone()) };
|
||||||
let trait_ref = TraitRef { trait_: *t, substs: Substs::single(self.clone()) };
|
let (trait_ref, _) = super::traits::canonicalize(trait_ref);
|
||||||
db.implements(trait_ref)
|
db.implements(trait_ref).is_some()
|
||||||
});
|
});
|
||||||
// FIXME if there's multiple candidates here, that's an ambiguity error
|
// FIXME if there's multiple candidates here, that's an ambiguity error
|
||||||
let (_chosen_trait, chosen_method) = candidates.first()?;
|
let (_chosen_trait, chosen_method) = candidates.first()?;
|
||||||
|
// FIXME return correct receiver type
|
||||||
Some((self.clone(), *chosen_method))
|
Some((self.clone(), *chosen_method))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,3 +232,16 @@ impl Ty {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fresh_substs_for_trait(db: &impl HirDatabase, tr: Trait, self_ty: Ty) -> Substs {
|
||||||
|
let mut substs = Vec::new();
|
||||||
|
let mut counter = 0;
|
||||||
|
let generics = tr.generic_params(db);
|
||||||
|
substs.push(self_ty);
|
||||||
|
substs.extend(generics.params_including_parent().into_iter().skip(1).map(|_p| {
|
||||||
|
let fresh_var = Ty::Infer(super::infer::InferTy::TypeVar(super::infer::TypeVarId(counter)));
|
||||||
|
counter += 1;
|
||||||
|
fresh_var
|
||||||
|
}));
|
||||||
|
substs.into()
|
||||||
|
}
|
||||||
|
|
|
@ -1926,8 +1926,8 @@ fn test() {
|
||||||
}
|
}
|
||||||
"#),
|
"#),
|
||||||
@r###"
|
@r###"
|
||||||
[31; 35) 'self': &{unknown}
|
[31; 35) 'self': &Self
|
||||||
[110; 114) 'self': &{unknown}
|
[110; 114) 'self': &Self
|
||||||
[170; 228) '{ ...i128 }': ()
|
[170; 228) '{ ...i128 }': ()
|
||||||
[176; 178) 'S1': S1
|
[176; 178) 'S1': S1
|
||||||
[176; 187) 'S1.method()': u32
|
[176; 187) 'S1.method()': u32
|
||||||
|
@ -1972,8 +1972,8 @@ mod bar_test {
|
||||||
}
|
}
|
||||||
"#),
|
"#),
|
||||||
@r###"
|
@r###"
|
||||||
[63; 67) 'self': &{unknown}
|
[63; 67) 'self': &Self
|
||||||
[169; 173) 'self': &{unknown}
|
[169; 173) 'self': &Self
|
||||||
[300; 337) '{ ... }': ()
|
[300; 337) '{ ... }': ()
|
||||||
[310; 311) 'S': S
|
[310; 311) 'S': S
|
||||||
[310; 320) 'S.method()': u32
|
[310; 320) 'S.method()': u32
|
||||||
|
@ -1998,10 +1998,45 @@ fn test() {
|
||||||
}
|
}
|
||||||
"#),
|
"#),
|
||||||
@r###"
|
@r###"
|
||||||
[33; 37) 'self': &{unknown}
|
[33; 37) 'self': &Self
|
||||||
[92; 111) '{ ...d(); }': ()
|
[92; 111) '{ ...d(); }': ()
|
||||||
[98; 99) 'S': S
|
[98; 99) 'S': S
|
||||||
[98; 108) 'S.method()': {unknown}"###
|
[98; 108) 'S.method()': u32"###
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn infer_trait_method_generic_more_params() {
|
||||||
|
// the trait implementation is intentionally incomplete -- it shouldn't matter
|
||||||
|
assert_snapshot_matches!(
|
||||||
|
infer(r#"
|
||||||
|
trait Trait<T1, T2, T3> {
|
||||||
|
fn method1(&self) -> (T1, T2, T3);
|
||||||
|
fn method2(&self) -> (T3, T2, T1);
|
||||||
|
}
|
||||||
|
struct S1;
|
||||||
|
impl Trait<u8, u16, u32> for S1 {}
|
||||||
|
struct S2;
|
||||||
|
impl<T> Trait<i8, i16, T> for S2 {}
|
||||||
|
fn test() {
|
||||||
|
S1.method1(); // u8, u16, u32
|
||||||
|
S1.method2(); // u32, u16, u8
|
||||||
|
S2.method1(); // i8, i16, {unknown}
|
||||||
|
S2.method2(); // {unknown}, i16, i8
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
@r###"
|
||||||
|
[43; 47) 'self': &Self
|
||||||
|
[82; 86) 'self': &Self
|
||||||
|
[210; 361) '{ ..., i8 }': ()
|
||||||
|
[216; 218) 'S1': S1
|
||||||
|
[216; 228) 'S1.method1()': (u8, u16, u32)
|
||||||
|
[250; 252) 'S1': S1
|
||||||
|
[250; 262) 'S1.method2()': (u32, u16, u8)
|
||||||
|
[284; 286) 'S2': S2
|
||||||
|
[284; 296) 'S2.method1()': (i8, i16, {unknown})
|
||||||
|
[324; 326) 'S2': S2
|
||||||
|
[324; 336) 'S2.method2()': ({unknown}, i16, i8)"###
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2020,7 +2055,7 @@ fn test() {
|
||||||
}
|
}
|
||||||
"#),
|
"#),
|
||||||
@r###"
|
@r###"
|
||||||
[33; 37) 'self': &{unknown}
|
[33; 37) 'self': &Self
|
||||||
[102; 127) '{ ...d(); }': ()
|
[102; 127) '{ ...d(); }': ()
|
||||||
[108; 109) 'S': S<u32>(T) -> S<T>
|
[108; 109) 'S': S<u32>(T) -> S<T>
|
||||||
[108; 115) 'S(1u32)': S<u32>
|
[108; 115) 'S(1u32)': S<u32>
|
||||||
|
@ -2168,7 +2203,7 @@ fn test() {
|
||||||
}
|
}
|
||||||
"#),
|
"#),
|
||||||
@r###"
|
@r###"
|
||||||
[29; 33) 'self': {unknown}
|
[29; 33) 'self': Self
|
||||||
[107; 198) '{ ...(S); }': ()
|
[107; 198) '{ ...(S); }': ()
|
||||||
[117; 118) 'x': u32
|
[117; 118) 'x': u32
|
||||||
[126; 127) 'S': S
|
[126; 127) 'S': S
|
||||||
|
|
112
crates/ra_hir/src/ty/traits.rs
Normal file
112
crates/ra_hir/src/ty/traits.rs
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
//! Stuff that will probably mostly replaced by Chalk.
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::db::HirDatabase;
|
||||||
|
use super::{ TraitRef, Substs, infer::{ TypeVarId, InferTy}, Ty};
|
||||||
|
|
||||||
|
// Copied (and simplified) from Chalk
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
/// A (possible) solution for a proposed goal. Usually packaged in a `Result`,
|
||||||
|
/// where `Err` represents definite *failure* to prove a goal.
|
||||||
|
pub enum Solution {
|
||||||
|
/// The goal indeed holds, and there is a unique value for all existential
|
||||||
|
/// variables.
|
||||||
|
Unique(Substs),
|
||||||
|
|
||||||
|
/// The goal may be provable in multiple ways, but regardless we may have some guidance
|
||||||
|
/// for type inference.
|
||||||
|
Ambig(Guidance),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
/// When a goal holds ambiguously (e.g., because there are multiple possible
|
||||||
|
/// solutions), we issue a set of *guidance* back to type inference.
|
||||||
|
pub enum Guidance {
|
||||||
|
/// The existential variables *must* have the given values if the goal is
|
||||||
|
/// ever to hold, but that alone isn't enough to guarantee the goal will
|
||||||
|
/// actually hold.
|
||||||
|
Definite(Substs),
|
||||||
|
|
||||||
|
/// There are multiple plausible values for the existentials, but the ones
|
||||||
|
/// here are suggested as the preferred choice heuristically. These should
|
||||||
|
/// be used for inference fallback only.
|
||||||
|
Suggested(Substs),
|
||||||
|
|
||||||
|
/// There's no useful information to feed back to type inference
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Something that needs to be proven (by Chalk) during type checking, e.g. that
|
||||||
|
/// a certain type implements a certain trait. Proving the Obligation might
|
||||||
|
/// result in additional information about inference variables.
|
||||||
|
///
|
||||||
|
/// This might be handled by Chalk when we integrate it?
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum Obligation {
|
||||||
|
/// Prove that a certain type implements a trait (the type is the `Self` type
|
||||||
|
/// parameter to the `TraitRef`).
|
||||||
|
Trait(TraitRef),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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) -> Option<Solution> {
|
||||||
|
// 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 None,
|
||||||
|
};
|
||||||
|
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
|
||||||
|
.find_map(|impl_block| unify_trait_refs(&trait_ref, &impl_block.target_trait_ref(db)?))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn canonicalize(trait_ref: TraitRef) -> (TraitRef, Vec<TypeVarId>) {
|
||||||
|
let mut canonical = HashMap::new(); // mapping uncanonical -> canonical
|
||||||
|
let mut uncanonical = Vec::new(); // mapping canonical -> uncanonical (which is dense)
|
||||||
|
let mut substs = trait_ref.substs.0.to_vec();
|
||||||
|
for ty in &mut substs {
|
||||||
|
ty.walk_mut(&mut |ty| match ty {
|
||||||
|
Ty::Infer(InferTy::TypeVar(tv)) => {
|
||||||
|
let tv: &mut TypeVarId = tv;
|
||||||
|
*tv = *canonical.entry(*tv).or_insert_with(|| {
|
||||||
|
let i = uncanonical.len();
|
||||||
|
uncanonical.push(*tv);
|
||||||
|
TypeVarId(i as u32)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
(TraitRef { substs: substs.into(), ..trait_ref }, uncanonical)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unify_trait_refs(tr1: &TraitRef, tr2: &TraitRef) -> Option<Solution> {
|
||||||
|
if tr1.trait_ != tr2.trait_ {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let mut solution_substs = Vec::new();
|
||||||
|
for (t1, t2) in tr1.substs.0.iter().zip(tr2.substs.0.iter()) {
|
||||||
|
// this is very bad / hacky 'unification' logic, just enough to make the simple tests pass
|
||||||
|
match (t1, t2) {
|
||||||
|
(_, Ty::Infer(InferTy::TypeVar(_))) | (_, Ty::Unknown) | (_, Ty::Param { .. }) => {
|
||||||
|
// type variable (or similar) in the impl, we just assume it works
|
||||||
|
}
|
||||||
|
(Ty::Infer(InferTy::TypeVar(v1)), _) => {
|
||||||
|
// type variable in the query and fixed type in the impl, record its value
|
||||||
|
solution_substs.resize_with(v1.0 as usize + 1, || Ty::Unknown);
|
||||||
|
solution_substs[v1.0 as usize] = t2.clone();
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// check that they're equal (actually we'd have to recurse etc.)
|
||||||
|
if t1 != t2 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(Solution::Unique(solution_substs.into()))
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue