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
@ -194,7 +194,7 @@ pub(crate) fn resolver(&self, db: &impl HirDatabase) -> Resolver {
|
||||
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);
|
||||
def_map[self.module_id]
|
||||
.scope
|
||||
@ -547,13 +547,20 @@ pub fn impl_block(&self, db: &impl DefDatabase) -> Option<ImplBlock> {
|
||||
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
|
||||
/// Builds a resolver for code inside this item.
|
||||
pub(crate) fn resolver(&self, db: &impl HirDatabase) -> Resolver {
|
||||
// take the outer scope...
|
||||
// FIXME abstract over containers (trait/impl)
|
||||
let r = self
|
||||
.impl_block(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));
|
||||
// ...and add generic params, if present
|
||||
let p = self.generic_params(db);
|
||||
@ -699,6 +706,14 @@ pub fn items(self, db: &impl DefDatabase) -> Vec<TraitItem> {
|
||||
pub(crate) fn trait_data(self, db: &impl DefDatabase) -> Arc<TraitData> {
|
||||
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 {
|
||||
|
@ -53,6 +53,9 @@ pub trait DefDatabase: SourceDatabase {
|
||||
#[salsa::invoke(crate::traits::TraitData::trait_data_query)]
|
||||
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)]
|
||||
fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>;
|
||||
|
||||
@ -128,8 +131,8 @@ fn body_with_source_map(
|
||||
#[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;
|
||||
#[salsa::invoke(crate::ty::traits::implements)]
|
||||
fn implements(&self, trait_ref: TraitRef) -> Option<crate::ty::traits::Solution>;
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -45,12 +45,16 @@ pub(crate) fn generic_params_query(
|
||||
) -> Arc<GenericParams> {
|
||||
let mut generics = GenericParams::default();
|
||||
let parent = match def {
|
||||
GenericDef::Function(it) => it.impl_block(db),
|
||||
GenericDef::TypeAlias(it) => it.impl_block(db),
|
||||
// FIXME abstract over containers (trait/impl)
|
||||
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::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;
|
||||
match def {
|
||||
GenericDef::Function(it) => generics.fill(&*it.source(db).1, start),
|
||||
|
@ -84,7 +84,8 @@ pub fn target_ty(&self, db: &impl HirDatabase) -> Ty {
|
||||
}
|
||||
|
||||
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> {
|
||||
|
@ -1,10 +1,11 @@
|
||||
//! HIR for trait definitions.
|
||||
|
||||
use std::sync::Arc;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
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)]
|
||||
pub struct TraitData {
|
||||
@ -49,4 +50,34 @@ pub enum TraitItem {
|
||||
TypeAlias(TypeAlias),
|
||||
// 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);
|
||||
|
||||
#[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 @@
|
||||
pub(crate) mod primitive;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
pub(crate) mod traits;
|
||||
pub(crate) mod method_resolution;
|
||||
mod op;
|
||||
mod lower;
|
||||
@ -145,6 +146,10 @@ pub fn single(ty: Ty) -> Substs {
|
||||
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> {
|
||||
self.0.iter()
|
||||
}
|
||||
@ -170,6 +175,12 @@ pub fn as_single(&self) -> &Ty {
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
/// Name to be bikeshedded: TraitBound? TraitImplements?
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
|
@ -41,7 +41,7 @@
|
||||
ty::infer::diagnostics::InferenceDiagnostic,
|
||||
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.
|
||||
pub fn infer(db: &impl HirDatabase, def: DefWithBody) -> Arc<InferenceResult> {
|
||||
@ -153,6 +153,7 @@ struct InferenceContext<'a, D: HirDatabase> {
|
||||
body: Arc<Body>,
|
||||
resolver: Resolver,
|
||||
var_unification_table: InPlaceUnificationTable<TypeVarId>,
|
||||
obligations: Vec<Obligation>,
|
||||
method_resolutions: FxHashMap<ExprId, Function>,
|
||||
field_resolutions: FxHashMap<ExprId, StructField>,
|
||||
assoc_resolutions: FxHashMap<ExprOrPatId, ImplItem>,
|
||||
@ -173,6 +174,7 @@ fn new(db: &'a D, body: Arc<Body>, resolver: Resolver) -> Self {
|
||||
type_of_pat: ArenaMap::default(),
|
||||
diagnostics: Vec::default(),
|
||||
var_unification_table: InPlaceUnificationTable::new(),
|
||||
obligations: Vec::default(),
|
||||
return_ty: Ty::Unknown, // set in collect_fn_signature
|
||||
db,
|
||||
body,
|
||||
@ -181,6 +183,7 @@ fn new(db: &'a D, body: Arc<Body>, resolver: Resolver) -> Self {
|
||||
}
|
||||
|
||||
fn resolve_all(mut self) -> InferenceResult {
|
||||
// FIXME resolve obligations as well (use Guidance if necessary)
|
||||
let mut tv_stack = Vec::new();
|
||||
let mut expr_types = mem::replace(&mut self.type_of_expr, ArenaMap::default());
|
||||
for ty in expr_types.values_mut() {
|
||||
@ -311,11 +314,49 @@ fn insert_type_vars(&mut self, ty: Ty) -> 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
|
||||
/// 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
|
||||
/// known type.
|
||||
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::Infer(tv) => {
|
||||
let inner = tv.to_inner();
|
||||
@ -710,12 +751,19 @@ fn substs_for_method_call(
|
||||
&mut self,
|
||||
def_generics: Option<Arc<GenericParams>>,
|
||||
generic_args: &Option<GenericArgs>,
|
||||
receiver_ty: &Ty,
|
||||
) -> Substs {
|
||||
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);
|
||||
for _ in 0..parent_param_count {
|
||||
substs.push(Ty::Unknown);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
// handle provided type arguments
|
||||
if let Some(generic_args) = generic_args {
|
||||
@ -817,6 +865,7 @@ fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
|
||||
(Vec::new(), Ty::Unknown)
|
||||
}
|
||||
};
|
||||
// FIXME register obligations from where clauses from the function
|
||||
let param_iter = param_tys.into_iter().chain(repeat(Ty::Unknown));
|
||||
for (arg, param) in args.iter().zip(param_iter) {
|
||||
self.infer_expr(*arg, &Expectation::has_type(param));
|
||||
@ -838,7 +887,11 @@ fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
|
||||
}
|
||||
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 = self.insert_type_vars(method_ty);
|
||||
let (expected_receiver_ty, param_tys, ret_ty) = match &method_ty {
|
||||
@ -859,6 +912,24 @@ fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
|
||||
let sig = self.db.callable_item_signature(def);
|
||||
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() {
|
||||
let mut params_iter = sig
|
||||
.params()
|
||||
@ -875,6 +946,7 @@ fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
|
||||
_ => (Ty::Unknown, Vec::new(), Ty::Unknown),
|
||||
};
|
||||
// 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() {
|
||||
Some((_, mutability)) => {
|
||||
Ty::apply_one(TypeCtor::Ref(mutability), derefed_receiver_ty)
|
||||
@ -1180,7 +1252,7 @@ fn infer_body(&mut self) {
|
||||
|
||||
/// The ID of a type variable.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct TypeVarId(u32);
|
||||
pub struct TypeVarId(pub(super) u32);
|
||||
|
||||
impl UnifyKey for TypeVarId {
|
||||
type Value = TypeVarValue;
|
||||
|
@ -206,6 +206,7 @@ pub(crate) fn from_hir(
|
||||
db: &impl HirDatabase,
|
||||
resolver: &Resolver,
|
||||
type_ref: &TypeRef,
|
||||
explicit_self_ty: Option<Ty>,
|
||||
) -> Option<Self> {
|
||||
let path = match type_ref {
|
||||
TypeRef::Path(path) => path,
|
||||
@ -215,7 +216,13 @@ pub(crate) fn from_hir(
|
||||
Resolution::Def(ModuleDef::Trait(tr)) => tr,
|
||||
_ => 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 })
|
||||
}
|
||||
|
||||
|
@ -108,20 +108,6 @@ pub(crate) fn impls_in_crate_query(
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
@ -142,6 +128,7 @@ pub(crate) fn lookup_method(
|
||||
resolver: &Resolver,
|
||||
) -> Option<(Ty, Function)> {
|
||||
// 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 sig = f.signature(db);
|
||||
if sig.name() == name && sig.has_self_param() {
|
||||
@ -174,24 +161,15 @@ fn lookup_trait_method(
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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)| {
|
||||
// FIXME construct substs of the correct length for the trait
|
||||
// - check in rustc whether it does anything smarter than putting variables for everything
|
||||
let trait_ref = TraitRef { trait_: *t, substs: Substs::single(self.clone()) };
|
||||
db.implements(trait_ref)
|
||||
let trait_ref =
|
||||
TraitRef { trait_: *t, substs: fresh_substs_for_trait(db, *t, self.clone()) };
|
||||
let (trait_ref, _) = super::traits::canonicalize(trait_ref);
|
||||
db.implements(trait_ref).is_some()
|
||||
});
|
||||
// FIXME if there's multiple candidates here, that's an ambiguity error
|
||||
let (_chosen_trait, chosen_method) = candidates.first()?;
|
||||
// FIXME return correct receiver type
|
||||
Some((self.clone(), *chosen_method))
|
||||
}
|
||||
|
||||
@ -254,3 +232,16 @@ pub fn iterate_impl_items<T>(
|
||||
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###"
|
||||
[31; 35) 'self': &{unknown}
|
||||
[110; 114) 'self': &{unknown}
|
||||
[31; 35) 'self': &Self
|
||||
[110; 114) 'self': &Self
|
||||
[170; 228) '{ ...i128 }': ()
|
||||
[176; 178) 'S1': S1
|
||||
[176; 187) 'S1.method()': u32
|
||||
@ -1972,8 +1972,8 @@ fn test() {
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
[63; 67) 'self': &{unknown}
|
||||
[169; 173) 'self': &{unknown}
|
||||
[63; 67) 'self': &Self
|
||||
[169; 173) 'self': &Self
|
||||
[300; 337) '{ ... }': ()
|
||||
[310; 311) 'S': S
|
||||
[310; 320) 'S.method()': u32
|
||||
@ -1998,10 +1998,45 @@ fn test() {
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
[33; 37) 'self': &{unknown}
|
||||
[33; 37) 'self': &Self
|
||||
[92; 111) '{ ...d(); }': ()
|
||||
[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###"
|
||||
[33; 37) 'self': &{unknown}
|
||||
[33; 37) 'self': &Self
|
||||
[102; 127) '{ ...d(); }': ()
|
||||
[108; 109) 'S': S<u32>(T) -> S<T>
|
||||
[108; 115) 'S(1u32)': S<u32>
|
||||
@ -2168,7 +2203,7 @@ fn test() {
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
[29; 33) 'self': {unknown}
|
||||
[29; 33) 'self': Self
|
||||
[107; 198) '{ ...(S); }': ()
|
||||
[117; 118) 'x': u32
|
||||
[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…
Reference in New Issue
Block a user