blob: e2f3e7b841c1be54f45d12029e46a6a0db720e27 [file] [log] [blame]
use super::var::*;
use super::*;
use crate::debug_span;
use crate::infer::instantiate::IntoBindersAndValue;
use chalk_ir::cast::Cast;
use chalk_ir::fold::{Fold, Folder};
use chalk_ir::interner::{HasInterner, Interner};
use chalk_ir::zip::{Zip, Zipper};
use std::fmt::Debug;
impl<I: Interner> InferenceTable<I> {
#[instrument(level = "debug", skip(self, interner, environment))]
pub(crate) fn unify<T>(
&mut self,
interner: &I,
environment: &Environment<I>,
a: &T,
b: &T,
) -> Fallible<UnificationResult<I>>
where
T: ?Sized + Zip<I>,
{
let snapshot = self.snapshot();
match Unifier::new(interner, self, environment).unify(a, b) {
Ok(r) => {
self.commit(snapshot);
Ok(r)
}
Err(e) => {
self.rollback_to(snapshot);
Err(e)
}
}
}
}
struct Unifier<'t, I: Interner> {
table: &'t mut InferenceTable<I>,
environment: &'t Environment<I>,
goals: Vec<InEnvironment<Goal<I>>>,
interner: &'t I,
}
#[derive(Debug)]
pub(crate) struct UnificationResult<I: Interner> {
pub(crate) goals: Vec<InEnvironment<Goal<I>>>,
}
impl<'t, I: Interner> Unifier<'t, I> {
fn new(
interner: &'t I,
table: &'t mut InferenceTable<I>,
environment: &'t Environment<I>,
) -> Self {
Unifier {
environment: environment,
table: table,
goals: vec![],
interner,
}
}
/// The main entry point for the `Unifier` type and really the
/// only type meant to be called externally. Performs a
/// unification of `a` and `b` and returns the Unification Result.
fn unify<T>(mut self, a: &T, b: &T) -> Fallible<UnificationResult<I>>
where
T: ?Sized + Zip<I>,
{
Zip::zip_with(&mut self, a, b)?;
Ok(UnificationResult { goals: self.goals })
}
fn unify_ty_ty(&mut self, a: &Ty<I>, b: &Ty<I>) -> Fallible<()> {
let interner = self.interner;
let n_a = self.table.normalize_ty_shallow(interner, a);
let n_b = self.table.normalize_ty_shallow(interner, b);
let a = n_a.as_ref().unwrap_or(a);
let b = n_b.as_ref().unwrap_or(b);
debug_span!("unify_ty_ty", ?a, ?b);
// let _s = span.enter();
match (a.data(interner), b.data(interner)) {
// Unifying two inference variables: unify them in the underlying
// ena table.
(
&TyData::InferenceVar(var1, kind1),
&TyData::InferenceVar(var2, kind2),
) => {
if kind1 == kind2 {
self.unify_var_var(var1, var2)
} else if kind1 == TyKind::General {
self.unify_general_var_specific_ty(var1, b.clone())
} else if kind2 == TyKind::General {
self.unify_general_var_specific_ty(var2, a.clone())
} else {
debug!(
"Tried to unify mis-matching inference variables: {:?} and {:?}",
kind1, kind2
);
Err(NoSolution)
}
}
// Unifying an inference variable with a non-inference variable.
(&TyData::InferenceVar(var, kind), ty_data @ &TyData::Apply(_))
| (&TyData::InferenceVar(var, kind), ty_data @ &TyData::Placeholder(_))
| (&TyData::InferenceVar(var, kind), ty_data @ &TyData::Dyn(_))
| (&TyData::InferenceVar(var, kind), ty_data @ &TyData::Function(_))
// The reflexive matches
| (ty_data @ &TyData::Apply(_), &TyData::InferenceVar(var, kind))
| (ty_data @ &TyData::Placeholder(_), &TyData::InferenceVar(var, kind))
| (ty_data @ &TyData::Dyn(_), &TyData::InferenceVar(var, kind))
| (ty_data @ &TyData::Function(_), &TyData::InferenceVar(var, kind))
=> {
let ty = ty_data.clone().intern(interner);
match (kind, ty.is_integer(interner), ty.is_float(interner)) {
// General inference variables can unify with any type
(TyKind::General, _, _)
// Integer inference variables can only unify with integer types
| (TyKind::Integer, true, _)
// Float inference variables can only unify with float types
| (TyKind::Float, _, true) => self.unify_var_ty(var, &ty),
_ => Err(NoSolution),
}
}
// Unifying `forall<X> { T }` with some other forall type `forall<X> { U }`
(&TyData::Function(ref fn1), &TyData::Function(ref fn2)) => {
self.unify_binders(fn1, fn2)
}
// This would correspond to unifying a `fn` type with a non-fn
// type in Rust; error.
(&TyData::Function(_), &TyData::Apply(_))
| (&TyData::Function(_), &TyData::Dyn(_))
| (&TyData::Function(_), &TyData::Placeholder(_))
| (&TyData::Apply(_), &TyData::Function(_))
| (&TyData::Placeholder(_), &TyData::Function(_))
| (&TyData::Dyn(_), &TyData::Function(_)) => Err(NoSolution),
(&TyData::Placeholder(ref p1), &TyData::Placeholder(ref p2)) => {
Zip::zip_with(self, p1, p2)
}
(&TyData::Apply(ref apply1), &TyData::Apply(ref apply2)) => {
Zip::zip_with(self, apply1, apply2)
}
// Cannot unify (e.g.) some struct type `Foo` and a placeholder like `T`
(&TyData::Apply(_), &TyData::Placeholder(_))
| (&TyData::Placeholder(_), &TyData::Apply(_)) => Err(NoSolution),
// Cannot unify `dyn Trait` with things like structs or placeholders
(&TyData::Placeholder(_), &TyData::Dyn(_))
| (&TyData::Dyn(_), &TyData::Placeholder(_))
| (&TyData::Apply(_), &TyData::Dyn(_))
| (&TyData::Dyn(_), &TyData::Apply(_)) => Err(NoSolution),
// Unifying two dyn is possible if they have the same bounds.
(&TyData::Dyn(ref qwc1), &TyData::Dyn(ref qwc2)) => Zip::zip_with(self, qwc1, qwc2),
// Unifying an alias type with some other type `U`.
(&TyData::Apply(_), &TyData::Alias(ref alias))
| (&TyData::Placeholder(_), &TyData::Alias(ref alias))
| (&TyData::Function(_), &TyData::Alias(ref alias))
| (&TyData::InferenceVar(_, _), &TyData::Alias(ref alias))
| (&TyData::Dyn(_), &TyData::Alias(ref alias)) => self.unify_alias_ty(alias, a),
(&TyData::Alias(ref alias), &TyData::Alias(_))
| (&TyData::Alias(ref alias), &TyData::Apply(_))
| (&TyData::Alias(ref alias), &TyData::Placeholder(_))
| (&TyData::Alias(ref alias), &TyData::Function(_))
| (&TyData::Alias(ref alias), &TyData::InferenceVar(_, _))
| (&TyData::Alias(ref alias), &TyData::Dyn(_)) => self.unify_alias_ty(alias, b),
(TyData::BoundVar(_), _) | (_, TyData::BoundVar(_)) => panic!(
"unification encountered bound variable: a={:?} b={:?}",
a, b
),
}
}
/// Unify two inference variables
fn unify_var_var(&mut self, a: InferenceVar, b: InferenceVar) -> Fallible<()> {
debug!("unify_var_var({:?}, {:?})", a, b);
let var1 = EnaVariable::from(a);
let var2 = EnaVariable::from(b);
Ok(self
.table
.unify
.unify_var_var(var1, var2)
.expect("unification of two unbound variables cannot fail"))
}
/// Unify a general inference variable with a specific inference variable
/// (type kind is not `General`). For example, unify a `TyKind::General`
/// inference variable with a `TyKind::Integer` variable, resulting in the
/// general inference variable narrowing to an integer variable.
fn unify_general_var_specific_ty(
&mut self,
general_var: InferenceVar,
specific_ty: Ty<I>,
) -> Fallible<()> {
debug!(
"unify_general_var_specific_var({:?}, {:?})",
general_var, specific_ty
);
self.table
.unify
.unify_var_value(
general_var,
InferenceValue::from_ty(self.interner, specific_ty),
)
.unwrap();
Ok(())
}
fn unify_binders<'a, T, R>(
&mut self,
a: impl IntoBindersAndValue<'a, I, Value = T> + Copy + Debug,
b: impl IntoBindersAndValue<'a, I, Value = T> + Copy + Debug,
) -> Fallible<()>
where
T: Fold<I, Result = R>,
R: Zip<I> + Fold<I, Result = R>,
't: 'a,
{
// for<'a...> T == for<'b...> U
//
// if:
//
// for<'a...> exists<'b...> T == U &&
// for<'b...> exists<'a...> T == U
debug!("unify_binders({:?}, {:?})", a, b);
let interner = self.interner;
{
let a_universal = self.table.instantiate_binders_universally(interner, a);
let b_existential = self.table.instantiate_binders_existentially(interner, b);
Zip::zip_with(self, &a_universal, &b_existential)?;
}
{
let b_universal = self.table.instantiate_binders_universally(interner, b);
let a_existential = self.table.instantiate_binders_existentially(interner, a);
Zip::zip_with(self, &a_existential, &b_universal)
}
}
/// Unify an alias like `<T as Trait>::Item` or `impl Trait` with some other
/// type `ty` (which might also be an alias). Creates a goal like
///
/// ```notrust
/// AliasEq(<T as Trait>::Item = U) // associated type projection
/// AliasEq(impl Trait = U) // impl trait
/// ```
fn unify_alias_ty(&mut self, alias: &AliasTy<I>, ty: &Ty<I>) -> Fallible<()> {
let interner = self.interner;
Ok(self.goals.push(InEnvironment::new(
self.environment,
AliasEq {
alias: alias.clone(),
ty: ty.clone(),
}
.cast(interner),
)))
}
/// Unify an inference variable `var` with some non-inference
/// variable `ty`, just bind `var` to `ty`. But we must enforce two conditions:
///
/// - `var` does not appear inside of `ty` (the standard `OccursCheck`)
/// - `ty` does not reference anything in a lifetime that could not be named in `var`
/// (the extended `OccursCheck` created to handle universes)
fn unify_var_ty(&mut self, var: InferenceVar, ty: &Ty<I>) -> Fallible<()> {
debug_span!("unify_var_ty", ?var, ?ty);
let interner = self.interner;
let var = EnaVariable::from(var);
// Determine the universe index associated with this
// variable. This is basically a count of the number of
// `forall` binders that had been introduced at the point
// this variable was created -- though it may change over time
// as the variable is unified.
let universe_index = self.table.universe_of_unbound_var(var);
let ty1 = ty.fold_with(
&mut OccursCheck::new(self, var, universe_index),
DebruijnIndex::INNERMOST,
)?;
self.table
.unify
.unify_var_value(var, InferenceValue::from_ty(interner, ty1.clone()))
.unwrap();
debug!("unify_var_ty: var {:?} set to {:?}", var, ty1);
Ok(())
}
fn unify_lifetime_lifetime(&mut self, a: &Lifetime<I>, b: &Lifetime<I>) -> Fallible<()> {
let interner = self.interner;
let n_a = self.table.normalize_lifetime_shallow(interner, a);
let n_b = self.table.normalize_lifetime_shallow(interner, b);
let a = n_a.as_ref().unwrap_or(a);
let b = n_b.as_ref().unwrap_or(b);
debug_span!("unify_lifetime_lifetime", ?a, ?b);
match (a.data(interner), b.data(interner)) {
(&LifetimeData::InferenceVar(var_a), &LifetimeData::InferenceVar(var_b)) => {
let var_a = EnaVariable::from(var_a);
let var_b = EnaVariable::from(var_b);
debug!(
"unify_lifetime_lifetime: var_a={:?} var_b={:?}",
var_a, var_b
);
self.table.unify.unify_var_var(var_a, var_b).unwrap();
Ok(())
}
(&LifetimeData::InferenceVar(a_var), &LifetimeData::Placeholder(b_idx)) => {
self.unify_lifetime_var(a, b, a_var, b, b_idx.ui)
}
(&LifetimeData::Placeholder(a_idx), &LifetimeData::InferenceVar(b_var)) => {
self.unify_lifetime_var(a, b, b_var, a, a_idx.ui)
}
(&LifetimeData::Placeholder(_), &LifetimeData::Placeholder(_)) => {
if a != b {
Ok(self.push_lifetime_eq_subgoal(a.clone(), b.clone()))
} else {
Ok(())
}
}
(LifetimeData::BoundVar(_), _) | (_, LifetimeData::BoundVar(_)) => panic!(
"unification encountered bound variable: a={:?} b={:?}",
a, b
),
(LifetimeData::Phantom(..), _) | (_, LifetimeData::Phantom(..)) => unreachable!(),
}
}
#[instrument(level = "debug", skip(self, a, b))]
fn unify_lifetime_var(
&mut self,
a: &Lifetime<I>,
b: &Lifetime<I>,
var: InferenceVar,
value: &Lifetime<I>,
value_ui: UniverseIndex,
) -> Fallible<()> {
let var = EnaVariable::from(var);
let var_ui = self.table.universe_of_unbound_var(var);
if var_ui.can_see(value_ui) {
debug!(
"unify_lifetime_var: {:?} in {:?} can see {:?}; unifying",
var, var_ui, value_ui
);
self.table
.unify
.unify_var_value(
var,
InferenceValue::from_lifetime(&self.interner, value.clone()),
)
.unwrap();
Ok(())
} else {
debug!(
"unify_lifetime_var: {:?} in {:?} cannot see {:?}; pushing constraint",
var, var_ui, value_ui
);
Ok(self.push_lifetime_eq_subgoal(a.clone(), b.clone()))
}
}
fn unify_const_const<'a>(&mut self, a: &'a Const<I>, b: &'a Const<I>) -> Fallible<()> {
let interner = self.interner;
let n_a = self.table.normalize_const_shallow(interner, a);
let n_b = self.table.normalize_const_shallow(interner, b);
let a = n_a.as_ref().unwrap_or(a);
let b = n_b.as_ref().unwrap_or(b);
debug_span!("unify_const_const", ?a, ?b);
let ConstData {
ty: a_ty,
value: a_val,
} = a.data(interner);
let ConstData {
ty: b_ty,
value: b_val,
} = b.data(interner);
self.unify_ty_ty(a_ty, b_ty)?;
match (a_val, b_val) {
// Unifying two inference variables: unify them in the underlying
// ena table.
(&ConstValue::InferenceVar(var1), &ConstValue::InferenceVar(var2)) => {
// debug!("unify_ty_ty: unify_var_var({:?}, {:?})", var1, var2);
let var1 = EnaVariable::from(var1);
let var2 = EnaVariable::from(var2);
Ok(self
.table
.unify
.unify_var_var(var1, var2)
.expect("unification of two unbound variables cannot fail"))
}
// Unifying an inference variables with a non-inference variable.
(&ConstValue::InferenceVar(var), &ConstValue::Concrete(_))
| (&ConstValue::InferenceVar(var), &ConstValue::Placeholder(_)) => {
debug!("unify_var_ty(var={:?}, ty={:?})", var, b);
self.unify_var_const(var, b)
}
(&ConstValue::Concrete(_), &ConstValue::InferenceVar(var))
| (&ConstValue::Placeholder(_), &ConstValue::InferenceVar(var)) => {
debug!("unify_var_ty(var={:?}, ty={:?})", var, a);
self.unify_var_const(var, a)
}
(&ConstValue::Placeholder(p1), &ConstValue::Placeholder(p2)) => {
Zip::zip_with(self, &p1, &p2)
}
(&ConstValue::Concrete(ref ev1), &ConstValue::Concrete(ref ev2)) => {
if ev1.const_eq(a_ty, ev2, interner) {
Ok(())
} else {
Err(NoSolution)
}
}
(&ConstValue::Concrete(_), &ConstValue::Placeholder(_))
| (&ConstValue::Placeholder(_), &ConstValue::Concrete(_)) => Err(NoSolution),
(ConstValue::BoundVar(_), _) | (_, ConstValue::BoundVar(_)) => panic!(
"unification encountered bound variable: a={:?} b={:?}",
a, b
),
}
}
fn unify_var_const(&mut self, var: InferenceVar, c: &Const<I>) -> Fallible<()> {
debug!("unify_var_const(var={:?}, c={:?})", var, c);
let interner = self.interner;
let var = EnaVariable::from(var);
self.table
.unify
.unify_var_value(var, InferenceValue::from_const(interner, c.clone()))
.unwrap();
debug!("unify_var_const: var {:?} set to {:?}", var, c);
Ok(())
}
fn push_lifetime_eq_subgoal(&mut self, a: Lifetime<I>, b: Lifetime<I>) {
let interner = self.interner;
let b_outlives_a = GoalData::AddRegionConstraint(b.clone(), a.clone()).intern(interner);
self.goals
.push(InEnvironment::new(self.environment, b_outlives_a));
let a_outlives_b = GoalData::AddRegionConstraint(a, b).intern(interner);
self.goals
.push(InEnvironment::new(self.environment, a_outlives_b));
}
}
impl<'i, I: Interner> Zipper<'i, I> for Unifier<'i, I> {
fn zip_tys(&mut self, a: &Ty<I>, b: &Ty<I>) -> Fallible<()> {
self.unify_ty_ty(a, b)
}
fn zip_lifetimes(&mut self, a: &Lifetime<I>, b: &Lifetime<I>) -> Fallible<()> {
self.unify_lifetime_lifetime(a, b)
}
fn zip_consts(&mut self, a: &Const<I>, b: &Const<I>) -> Fallible<()> {
self.unify_const_const(a, b)
}
fn zip_binders<T>(&mut self, a: &Binders<T>, b: &Binders<T>) -> Fallible<()>
where
T: HasInterner<Interner = I> + Zip<I> + Fold<I, Result = T>,
{
// The binders that appear in types (apart from quantified types, which are
// handled in `unify_ty`) appear as part of `dyn Trait` and `impl Trait` types.
//
// They come in two varieties:
//
// * The existential binder from `dyn Trait` / `impl Trait`
// (representing the hidden "self" type)
// * The `for<..>` binders from higher-ranked traits.
//
// In both cases we can use the same `unify_binders` routine.
self.unify_binders(a, b)
}
fn interner(&self) -> &'i I {
self.interner
}
}
struct OccursCheck<'u, 't, I: Interner> {
unifier: &'u mut Unifier<'t, I>,
var: EnaVariable<I>,
universe_index: UniverseIndex,
}
impl<'u, 't, I: Interner> OccursCheck<'u, 't, I> {
fn new(
unifier: &'u mut Unifier<'t, I>,
var: EnaVariable<I>,
universe_index: UniverseIndex,
) -> Self {
OccursCheck {
unifier,
var,
universe_index,
}
}
}
impl<'i, I: Interner> Folder<'i, I> for OccursCheck<'_, 'i, I>
where
I: 'i,
{
fn as_dyn(&mut self) -> &mut dyn Folder<'i, I> {
self
}
fn fold_free_placeholder_ty(
&mut self,
universe: PlaceholderIndex,
_outer_binder: DebruijnIndex,
) -> Fallible<Ty<I>> {
let interner = self.interner();
if self.universe_index < universe.ui {
Err(NoSolution)
} else {
Ok(universe.to_ty(interner)) // no need to shift, not relative to depth
}
}
fn fold_free_placeholder_lifetime(
&mut self,
ui: PlaceholderIndex,
_outer_binder: DebruijnIndex,
) -> Fallible<Lifetime<I>> {
let interner = self.interner();
if self.universe_index < ui.ui {
// Scenario is like:
//
// exists<T> forall<'b> ?T = Foo<'b>
//
// unlike with a type variable, this **might** be
// ok. Ultimately it depends on whether the
// `forall` also introduced relations to lifetimes
// nameable in T. To handle that, we introduce a
// fresh region variable `'x` in same universe as `T`
// and add a side-constraint that `'x = 'b`:
//
// exists<'x> forall<'b> ?T = Foo<'x>, where 'x = 'b
let tick_x = self.unifier.table.new_variable(self.universe_index);
self.unifier
.push_lifetime_eq_subgoal(tick_x.to_lifetime(interner), ui.to_lifetime(interner));
Ok(tick_x.to_lifetime(interner))
} else {
// If the `ui` is higher than `self.universe_index`, then we can name
// this lifetime, no problem.
Ok(ui.to_lifetime(interner)) // no need to shift, not relative to depth
}
}
fn fold_inference_ty(
&mut self,
var: InferenceVar,
kind: TyKind,
_outer_binder: DebruijnIndex,
) -> Fallible<Ty<I>> {
let interner = self.interner();
let var = EnaVariable::from(var);
match self.unifier.table.unify.probe_value(var) {
// If this variable already has a value, fold over that value instead.
InferenceValue::Bound(normalized_ty) => {
let normalized_ty = normalized_ty.assert_ty_ref(interner);
let normalized_ty = normalized_ty.fold_with(self, DebruijnIndex::INNERMOST)?;
assert!(!normalized_ty.needs_shift(interner));
Ok(normalized_ty)
}
// Otherwise, check the universe of the variable, and also
// check for cycles with `self.var` (which this will soon
// become the value of).
InferenceValue::Unbound(ui) => {
if self.unifier.table.unify.unioned(var, self.var) {
return Err(NoSolution);
}
if self.universe_index < ui {
// Scenario is like:
//
// ?A = foo(?B)
//
// where ?A is in universe 0 and ?B is in universe 1.
// This is OK, if ?B is promoted to universe 0.
self.unifier
.table
.unify
.unify_var_value(var, InferenceValue::Unbound(self.universe_index))
.unwrap();
}
Ok(var.to_ty_with_kind(interner, kind))
}
}
}
fn fold_inference_lifetime(
&mut self,
var: InferenceVar,
outer_binder: DebruijnIndex,
) -> Fallible<Lifetime<I>> {
// a free existentially bound region; find the
// inference variable it corresponds to
let interner = self.interner();
let var = EnaVariable::from(var);
match self.unifier.table.unify.probe_value(var) {
InferenceValue::Unbound(ui) => {
if self.universe_index < ui {
// Scenario is like:
//
// exists<T> forall<'b> exists<'a> ?T = Foo<'a>
//
// where ?A is in universe 0 and `'b` is in universe 1.
// This is OK, if `'b` is promoted to universe 0.
self.unifier
.table
.unify
.unify_var_value(var, InferenceValue::Unbound(self.universe_index))
.unwrap();
}
Ok(var.to_lifetime(interner))
}
InferenceValue::Bound(l) => {
let l = l.assert_lifetime_ref(interner);
let l = l.fold_with(self, outer_binder)?;
assert!(!l.needs_shift(interner));
Ok(l)
}
}
}
fn forbid_free_vars(&self) -> bool {
true
}
fn interner(&self) -> &'i I {
self.unifier.interner
}
fn target_interner(&self) -> &'i I {
self.interner()
}
}