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:
Florian Diebold 2019-03-31 20:02:16 +02:00
parent 413c87f155
commit a1ed53a4f1
11 changed files with 333 additions and 51 deletions

View File

@ -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 {

View File

@ -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]

View File

@ -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),

View File

@ -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> {

View File

@ -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()
}
}

View File

@ -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)]

View File

@ -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;

View File

@ -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 })
}

View File

@ -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()
}

View File

@ -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

View 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()))
}