2019-04-20 10:34:36 +00:00
|
|
|
//! Unification and canonicalization logic.
|
|
|
|
|
2019-07-08 19:43:52 +00:00
|
|
|
use super::{InferenceContext, Obligation};
|
2019-07-04 20:05:17 +00:00
|
|
|
use crate::db::HirDatabase;
|
2019-06-29 15:40:00 +00:00
|
|
|
use crate::ty::{
|
2019-09-26 19:37:03 +00:00
|
|
|
Canonical, InEnvironment, InferTy, ProjectionPredicate, ProjectionTy, Substs, TraitRef, Ty,
|
|
|
|
TypeWalk,
|
2019-06-29 15:40:00 +00:00
|
|
|
};
|
2019-04-20 10:34:36 +00:00
|
|
|
|
|
|
|
impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|
|
|
pub(super) fn canonicalizer<'b>(&'b mut self) -> Canonicalizer<'a, 'b, D>
|
|
|
|
where
|
|
|
|
'a: 'b,
|
|
|
|
{
|
2019-05-04 16:25:07 +00:00
|
|
|
Canonicalizer { ctx: self, free_vars: Vec::new(), var_stack: Vec::new() }
|
2019-04-20 10:34:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(super) struct Canonicalizer<'a, 'b, D: HirDatabase>
|
|
|
|
where
|
|
|
|
'a: 'b,
|
|
|
|
{
|
2019-05-04 13:42:00 +00:00
|
|
|
ctx: &'b mut InferenceContext<'a, D>,
|
|
|
|
free_vars: Vec<InferTy>,
|
2019-05-04 16:25:07 +00:00
|
|
|
/// A stack of type variables that is used to detect recursive types (which
|
|
|
|
/// are an error, but we need to protect against them to avoid stack
|
|
|
|
/// overflows).
|
|
|
|
var_stack: Vec<super::TypeVarId>,
|
2019-05-04 13:42:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub(super) struct Canonicalized<T> {
|
|
|
|
pub value: Canonical<T>,
|
|
|
|
free_vars: Vec<InferTy>,
|
2019-04-20 10:34:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, 'b, D: HirDatabase> Canonicalizer<'a, 'b, D>
|
|
|
|
where
|
|
|
|
'a: 'b,
|
|
|
|
{
|
|
|
|
fn add(&mut self, free_var: InferTy) -> usize {
|
|
|
|
self.free_vars.iter().position(|&v| v == free_var).unwrap_or_else(|| {
|
|
|
|
let next_index = self.free_vars.len();
|
|
|
|
self.free_vars.push(free_var);
|
|
|
|
next_index
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-05-04 13:42:00 +00:00
|
|
|
fn do_canonicalize_ty(&mut self, ty: Ty) -> Ty {
|
|
|
|
ty.fold(&mut |ty| match ty {
|
2019-04-20 10:34:36 +00:00
|
|
|
Ty::Infer(tv) => {
|
|
|
|
let inner = tv.to_inner();
|
2019-05-04 16:25:07 +00:00
|
|
|
if self.var_stack.contains(&inner) {
|
|
|
|
// recursive type
|
|
|
|
return tv.fallback_value();
|
|
|
|
}
|
2019-04-20 10:34:36 +00:00
|
|
|
if let Some(known_ty) = self.ctx.var_unification_table.probe_value(inner).known() {
|
2019-05-04 16:25:07 +00:00
|
|
|
self.var_stack.push(inner);
|
|
|
|
let result = self.do_canonicalize_ty(known_ty.clone());
|
|
|
|
self.var_stack.pop();
|
|
|
|
result
|
2019-04-20 10:34:36 +00:00
|
|
|
} else {
|
2019-05-20 16:31:41 +00:00
|
|
|
let root = self.ctx.var_unification_table.find(inner);
|
|
|
|
let free_var = match tv {
|
|
|
|
InferTy::TypeVar(_) => InferTy::TypeVar(root),
|
|
|
|
InferTy::IntVar(_) => InferTy::IntVar(root),
|
|
|
|
InferTy::FloatVar(_) => InferTy::FloatVar(root),
|
2019-09-17 19:59:51 +00:00
|
|
|
InferTy::MaybeNeverTypeVar(_) => InferTy::MaybeNeverTypeVar(root),
|
2019-05-20 16:31:41 +00:00
|
|
|
};
|
2019-04-20 10:34:36 +00:00
|
|
|
let position = self.add(free_var);
|
|
|
|
Ty::Bound(position as u32)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => ty,
|
2019-05-04 13:42:00 +00:00
|
|
|
})
|
2019-04-20 10:34:36 +00:00
|
|
|
}
|
|
|
|
|
2019-05-04 13:42:00 +00:00
|
|
|
fn do_canonicalize_trait_ref(&mut self, trait_ref: TraitRef) -> TraitRef {
|
2019-09-26 19:37:03 +00:00
|
|
|
let substs =
|
|
|
|
trait_ref.substs.iter().map(|ty| self.do_canonicalize_ty(ty.clone())).collect();
|
|
|
|
TraitRef { trait_: trait_ref.trait_, substs: Substs(substs) }
|
2019-04-20 10:34:36 +00:00
|
|
|
}
|
|
|
|
|
2019-05-04 13:42:00 +00:00
|
|
|
fn into_canonicalized<T>(self, result: T) -> Canonicalized<T> {
|
|
|
|
Canonicalized {
|
|
|
|
value: Canonical { value: result, num_vars: self.free_vars.len() },
|
|
|
|
free_vars: self.free_vars,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-07 07:31:09 +00:00
|
|
|
fn do_canonicalize_projection_ty(&mut self, projection_ty: ProjectionTy) -> ProjectionTy {
|
2019-09-26 19:37:03 +00:00
|
|
|
let params =
|
|
|
|
projection_ty.parameters.iter().map(|ty| self.do_canonicalize_ty(ty.clone())).collect();
|
|
|
|
ProjectionTy { associated_ty: projection_ty.associated_ty, parameters: Substs(params) }
|
2019-07-07 07:31:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn do_canonicalize_projection_predicate(
|
|
|
|
&mut self,
|
|
|
|
projection: ProjectionPredicate,
|
|
|
|
) -> ProjectionPredicate {
|
|
|
|
let ty = self.do_canonicalize_ty(projection.ty);
|
|
|
|
let projection_ty = self.do_canonicalize_projection_ty(projection.projection_ty);
|
|
|
|
|
|
|
|
ProjectionPredicate { ty, projection_ty }
|
|
|
|
}
|
|
|
|
|
2019-06-29 15:40:00 +00:00
|
|
|
// FIXME: add some point, we need to introduce a `Fold` trait that abstracts
|
|
|
|
// over all the things that can be canonicalized (like Chalk and rustc have)
|
|
|
|
|
2019-07-08 19:43:52 +00:00
|
|
|
pub(crate) fn canonicalize_ty(mut self, ty: Ty) -> Canonicalized<Ty> {
|
2019-05-04 13:42:00 +00:00
|
|
|
let result = self.do_canonicalize_ty(ty);
|
|
|
|
self.into_canonicalized(result)
|
|
|
|
}
|
|
|
|
|
2019-07-08 19:43:52 +00:00
|
|
|
pub(crate) fn canonicalize_obligation(
|
2019-06-29 15:40:00 +00:00
|
|
|
mut self,
|
2019-07-08 19:43:52 +00:00
|
|
|
obligation: InEnvironment<Obligation>,
|
|
|
|
) -> Canonicalized<InEnvironment<Obligation>> {
|
|
|
|
let result = match obligation.value {
|
|
|
|
Obligation::Trait(tr) => Obligation::Trait(self.do_canonicalize_trait_ref(tr)),
|
|
|
|
Obligation::Projection(pr) => {
|
|
|
|
Obligation::Projection(self.do_canonicalize_projection_predicate(pr))
|
|
|
|
}
|
|
|
|
};
|
2019-07-07 16:14:56 +00:00
|
|
|
self.into_canonicalized(InEnvironment {
|
|
|
|
value: result,
|
2019-07-08 19:43:52 +00:00
|
|
|
environment: obligation.environment,
|
2019-07-07 16:14:56 +00:00
|
|
|
})
|
2019-07-07 07:31:09 +00:00
|
|
|
}
|
2019-05-04 13:42:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> Canonicalized<T> {
|
2019-05-01 15:57:56 +00:00
|
|
|
pub fn decanonicalize_ty(&self, ty: Ty) -> Ty {
|
|
|
|
ty.fold(&mut |ty| match ty {
|
|
|
|
Ty::Bound(idx) => {
|
|
|
|
if (idx as usize) < self.free_vars.len() {
|
2019-07-04 17:26:44 +00:00
|
|
|
Ty::Infer(self.free_vars[idx as usize])
|
2019-05-01 15:57:56 +00:00
|
|
|
} else {
|
|
|
|
Ty::Bound(idx)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ty => ty,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-05-04 13:42:00 +00:00
|
|
|
pub fn apply_solution(
|
|
|
|
&self,
|
|
|
|
ctx: &mut InferenceContext<'_, impl HirDatabase>,
|
|
|
|
solution: Canonical<Vec<Ty>>,
|
|
|
|
) {
|
2019-04-20 10:34:36 +00:00
|
|
|
// the solution may contain new variables, which we need to convert to new inference vars
|
2019-09-26 19:37:03 +00:00
|
|
|
let new_vars = Substs((0..solution.num_vars).map(|_| ctx.new_type_var()).collect());
|
2019-04-20 10:34:36 +00:00
|
|
|
for (i, ty) in solution.value.into_iter().enumerate() {
|
2019-07-04 17:26:44 +00:00
|
|
|
let var = self.free_vars[i];
|
2019-05-04 13:42:00 +00:00
|
|
|
ctx.unify(&Ty::Infer(var), &ty.subst_bound_vars(&new_vars));
|
2019-04-20 10:34:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|