| use super::var::*; |
| use super::*; |
| use crate::debug_span; |
| use chalk_ir::cast::Cast; |
| use chalk_ir::fold::{FallibleTypeFolder, TypeFoldable}; |
| use chalk_ir::interner::{HasInterner, Interner}; |
| use chalk_ir::zip::{Zip, Zipper}; |
| use chalk_ir::UnificationDatabase; |
| use std::fmt::Debug; |
| use tracing::{debug, instrument}; |
| |
| impl<I: Interner> InferenceTable<I> { |
| pub fn relate<T>( |
| &mut self, |
| interner: I, |
| db: &dyn UnificationDatabase<I>, |
| environment: &Environment<I>, |
| variance: Variance, |
| a: &T, |
| b: &T, |
| ) -> Fallible<RelationResult<I>> |
| where |
| T: ?Sized + Zip<I>, |
| { |
| let snapshot = self.snapshot(); |
| match Unifier::new(interner, db, self, environment).relate(variance, 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: I, |
| db: &'t dyn UnificationDatabase<I>, |
| } |
| |
| #[derive(Debug)] |
| pub struct RelationResult<I: Interner> { |
| pub goals: Vec<InEnvironment<Goal<I>>>, |
| } |
| |
| impl<'t, I: Interner> Unifier<'t, I> { |
| fn new( |
| interner: I, |
| db: &'t dyn UnificationDatabase<I>, |
| table: &'t mut InferenceTable<I>, |
| environment: &'t Environment<I>, |
| ) -> Self { |
| Unifier { |
| environment, |
| table, |
| goals: vec![], |
| interner, |
| db, |
| } |
| } |
| |
| /// The main entry point for the `Unifier` type and really the |
| /// only type meant to be called externally. Performs a |
| /// relation of `a` and `b` and returns the Unification Result. |
| #[instrument(level = "debug", skip(self))] |
| fn relate<T>(mut self, variance: Variance, a: &T, b: &T) -> Fallible<RelationResult<I>> |
| where |
| T: ?Sized + Zip<I>, |
| { |
| Zip::zip_with(&mut self, variance, a, b)?; |
| let interner = self.interner(); |
| let mut goals = self.goals; |
| let table = self.table; |
| // Sometimes we'll produce a lifetime outlives goal which we later solve by unification |
| // Technically, these *will* get canonicalized to the same bound var and so that will end up |
| // as a goal like `^0.0 <: ^0.0`, which is trivially true. But, we remove those *here*, which |
| // might help caching. |
| goals.retain(|g| match g.goal.data(interner) { |
| GoalData::SubtypeGoal(SubtypeGoal { a, b }) => { |
| let n_a = table.ty_root(interner, a); |
| let n_b = table.ty_root(interner, b); |
| let a = n_a.as_ref().unwrap_or(a); |
| let b = n_b.as_ref().unwrap_or(b); |
| a != b |
| } |
| _ => true, |
| }); |
| Ok(RelationResult { goals }) |
| } |
| |
| /// Relate `a`, `b` with the variance such that if `variance = Covariant`, `a` is |
| /// a subtype of `b`. |
| fn relate_ty_ty(&mut self, variance: Variance, 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!("relate_ty_ty", ?variance, ?a, ?b); |
| |
| if a.kind(interner) == b.kind(interner) { |
| return Ok(()); |
| } |
| |
| match (a.kind(interner), b.kind(interner)) { |
| // Relating two inference variables: |
| // First, if either variable is a float or int kind, then we always |
| // unify if they match. This is because float and ints don't have |
| // subtype relationships. |
| // If both kinds are general then: |
| // If `Invariant`, unify them in the underlying ena table. |
| // If `Covariant` or `Contravariant`, push `SubtypeGoal` |
| (&TyKind::InferenceVar(var1, kind1), &TyKind::InferenceVar(var2, kind2)) => { |
| if matches!(kind1, TyVariableKind::General) |
| && matches!(kind2, TyVariableKind::General) |
| { |
| // Both variable kinds are general; so unify if invariant, otherwise push subtype goal |
| match variance { |
| Variance::Invariant => self.unify_var_var(var1, var2), |
| Variance::Covariant => { |
| self.push_subtype_goal(a.clone(), b.clone()); |
| Ok(()) |
| } |
| Variance::Contravariant => { |
| self.push_subtype_goal(b.clone(), a.clone()); |
| Ok(()) |
| } |
| } |
| } else if kind1 == kind2 { |
| // At least one kind is not general, but they match, so unify |
| self.unify_var_var(var1, var2) |
| } else if kind1 == TyVariableKind::General { |
| // First kind is general, second isn't, unify |
| self.unify_general_var_specific_ty(var1, b.clone()) |
| } else if kind2 == TyVariableKind::General { |
| // Second kind is general, first isn't, unify |
| self.unify_general_var_specific_ty(var2, a.clone()) |
| } else { |
| debug!( |
| "Tried to unify mis-matching inference variables: {:?} and {:?}", |
| kind1, kind2 |
| ); |
| Err(NoSolution) |
| } |
| } |
| |
| // Unifying `forall<X> { T }` with some other forall type `forall<X> { U }` |
| (&TyKind::Function(ref fn1), &TyKind::Function(ref fn2)) => { |
| if fn1.sig == fn2.sig { |
| Zip::zip_with( |
| self, |
| variance, |
| &fn1.clone().into_binders(interner), |
| &fn2.clone().into_binders(interner), |
| ) |
| } else { |
| Err(NoSolution) |
| } |
| } |
| |
| (&TyKind::Placeholder(ref p1), &TyKind::Placeholder(ref p2)) => { |
| Zip::zip_with(self, variance, p1, p2) |
| } |
| |
| // Unifying two dyn is possible if they have the same bounds. |
| (&TyKind::Dyn(ref qwc1), &TyKind::Dyn(ref qwc2)) => { |
| Zip::zip_with(self, variance, qwc1, qwc2) |
| } |
| |
| (TyKind::BoundVar(_), _) | (_, TyKind::BoundVar(_)) => panic!( |
| "unification encountered bound variable: a={:?} b={:?}", |
| a, b |
| ), |
| |
| // Unifying an alias type with some other type `U`. |
| (_, &TyKind::Alias(ref alias)) => self.relate_alias_ty(variance.invert(), alias, a), |
| (&TyKind::Alias(ref alias), _) => self.relate_alias_ty(variance, alias, b), |
| |
| (&TyKind::InferenceVar(var, kind), ty_data) => { |
| let ty = ty_data.clone().intern(interner); |
| self.relate_var_ty(variance, var, kind, &ty) |
| } |
| (ty_data, &TyKind::InferenceVar(var, kind)) => { |
| // We need to invert the variance if inference var is `b` because we pass it in |
| // as `a` to relate_var_ty |
| let ty = ty_data.clone().intern(interner); |
| self.relate_var_ty(variance.invert(), var, kind, &ty) |
| } |
| |
| // This would correspond to unifying a `fn` type with a non-fn |
| // type in Rust; error. |
| (&TyKind::Function(_), _) | (_, &TyKind::Function(_)) => Err(NoSolution), |
| |
| // Cannot unify (e.g.) some struct type `Foo` and a placeholder like `T` |
| (_, &TyKind::Placeholder(_)) | (&TyKind::Placeholder(_), _) => Err(NoSolution), |
| |
| // Cannot unify `dyn Trait` with things like structs or placeholders |
| (_, &TyKind::Dyn(_)) | (&TyKind::Dyn(_), _) => Err(NoSolution), |
| |
| (TyKind::Adt(id_a, substitution_a), TyKind::Adt(id_b, substitution_b)) => { |
| if id_a != id_b { |
| return Err(NoSolution); |
| } |
| self.zip_substs( |
| variance, |
| Some(self.unification_database().adt_variance(*id_a)), |
| substitution_a.as_slice(interner), |
| substitution_b.as_slice(interner), |
| ) |
| } |
| ( |
| TyKind::AssociatedType(id_a, substitution_a), |
| TyKind::AssociatedType(id_b, substitution_b), |
| ) => { |
| if id_a != id_b { |
| return Err(NoSolution); |
| } |
| self.zip_substs( |
| variance, |
| None, // TODO: AssociatedType variances? |
| substitution_a.as_slice(interner), |
| substitution_b.as_slice(interner), |
| ) |
| } |
| (TyKind::Scalar(scalar_a), TyKind::Scalar(scalar_b)) => { |
| Zip::zip_with(self, variance, scalar_a, scalar_b) |
| } |
| (TyKind::Str, TyKind::Str) => Ok(()), |
| (TyKind::Tuple(arity_a, substitution_a), TyKind::Tuple(arity_b, substitution_b)) => { |
| if arity_a != arity_b { |
| return Err(NoSolution); |
| } |
| self.zip_substs( |
| variance, |
| Some(Variances::from_iter( |
| self.interner, |
| std::iter::repeat(Variance::Covariant).take(*arity_a), |
| )), |
| substitution_a.as_slice(interner), |
| substitution_b.as_slice(interner), |
| ) |
| } |
| ( |
| TyKind::OpaqueType(id_a, substitution_a), |
| TyKind::OpaqueType(id_b, substitution_b), |
| ) => { |
| if id_a != id_b { |
| return Err(NoSolution); |
| } |
| self.zip_substs( |
| variance, |
| None, |
| substitution_a.as_slice(interner), |
| substitution_b.as_slice(interner), |
| ) |
| } |
| (TyKind::Slice(ty_a), TyKind::Slice(ty_b)) => Zip::zip_with(self, variance, ty_a, ty_b), |
| (TyKind::FnDef(id_a, substitution_a), TyKind::FnDef(id_b, substitution_b)) => { |
| if id_a != id_b { |
| return Err(NoSolution); |
| } |
| self.zip_substs( |
| variance, |
| Some(self.unification_database().fn_def_variance(*id_a)), |
| substitution_a.as_slice(interner), |
| substitution_b.as_slice(interner), |
| ) |
| } |
| ( |
| TyKind::Ref(mutability_a, lifetime_a, ty_a), |
| TyKind::Ref(mutability_b, lifetime_b, ty_b), |
| ) => { |
| if mutability_a != mutability_b { |
| return Err(NoSolution); |
| } |
| // The lifetime is `Contravariant` |
| Zip::zip_with( |
| self, |
| variance.xform(Variance::Contravariant), |
| lifetime_a, |
| lifetime_b, |
| )?; |
| // The type is `Covariant` when not mut, `Invariant` otherwise |
| let output_variance = match mutability_a { |
| Mutability::Not => Variance::Covariant, |
| Mutability::Mut => Variance::Invariant, |
| }; |
| Zip::zip_with(self, variance.xform(output_variance), ty_a, ty_b) |
| } |
| (TyKind::Raw(mutability_a, ty_a), TyKind::Raw(mutability_b, ty_b)) => { |
| if mutability_a != mutability_b { |
| return Err(NoSolution); |
| } |
| let ty_variance = match mutability_a { |
| Mutability::Not => Variance::Covariant, |
| Mutability::Mut => Variance::Invariant, |
| }; |
| Zip::zip_with(self, variance.xform(ty_variance), ty_a, ty_b) |
| } |
| (TyKind::Never, TyKind::Never) => Ok(()), |
| (TyKind::Array(ty_a, const_a), TyKind::Array(ty_b, const_b)) => { |
| Zip::zip_with(self, variance, ty_a, ty_b)?; |
| Zip::zip_with(self, variance, const_a, const_b) |
| } |
| (TyKind::Closure(id_a, substitution_a), TyKind::Closure(id_b, substitution_b)) => { |
| if id_a != id_b { |
| return Err(NoSolution); |
| } |
| self.zip_substs( |
| variance, |
| None, |
| substitution_a.as_slice(interner), |
| substitution_b.as_slice(interner), |
| ) |
| } |
| (TyKind::Generator(id_a, substitution_a), TyKind::Generator(id_b, substitution_b)) => { |
| if id_a != id_b { |
| return Err(NoSolution); |
| } |
| self.zip_substs( |
| variance, |
| None, |
| substitution_a.as_slice(interner), |
| substitution_b.as_slice(interner), |
| ) |
| } |
| ( |
| TyKind::GeneratorWitness(id_a, substitution_a), |
| TyKind::GeneratorWitness(id_b, substitution_b), |
| ) => { |
| if id_a != id_b { |
| return Err(NoSolution); |
| } |
| self.zip_substs( |
| variance, |
| None, |
| substitution_a.as_slice(interner), |
| substitution_b.as_slice(interner), |
| ) |
| } |
| (TyKind::Foreign(id_a), TyKind::Foreign(id_b)) => { |
| Zip::zip_with(self, variance, id_a, id_b) |
| } |
| (TyKind::Error, TyKind::Error) => Ok(()), |
| |
| (_, _) => Err(NoSolution), |
| } |
| } |
| |
| /// Unify two inference variables |
| #[instrument(level = "debug", skip(self))] |
| fn unify_var_var(&mut self, a: InferenceVar, b: InferenceVar) -> Fallible<()> { |
| let var1 = EnaVariable::from(a); |
| let var2 = EnaVariable::from(b); |
| self.table |
| .unify |
| .unify_var_var(var1, var2) |
| .expect("unification of two unbound variables cannot fail"); |
| Ok(()) |
| } |
| |
| /// Unify a general inference variable with a specific inference variable |
| /// (type kind is not `General`). For example, unify a `TyVariableKind::General` |
| /// inference variable with a `TyVariableKind::Integer` variable, resulting in the |
| /// general inference variable narrowing to an integer variable. |
| |
| #[instrument(level = "debug", skip(self))] |
| fn unify_general_var_specific_ty( |
| &mut self, |
| general_var: InferenceVar, |
| specific_ty: Ty<I>, |
| ) -> Fallible<()> { |
| self.table |
| .unify |
| .unify_var_value( |
| general_var, |
| InferenceValue::from_ty(self.interner, specific_ty), |
| ) |
| .unwrap(); |
| |
| Ok(()) |
| } |
| |
| #[instrument(level = "debug", skip(self))] |
| fn relate_binders<'a, T>( |
| &mut self, |
| variance: Variance, |
| a: &Binders<T>, |
| b: &Binders<T>, |
| ) -> Fallible<()> |
| where |
| T: Clone + TypeFoldable<I> + HasInterner<Interner = I> + Zip<I>, |
| 't: 'a, |
| { |
| // for<'a...> T == for<'b...> U |
| // |
| // if: |
| // |
| // for<'a...> exists<'b...> T == U && |
| // for<'b...> exists<'a...> T == U |
| |
| // for<'a...> T <: for<'b...> U |
| // |
| // if |
| // |
| // for<'b...> exists<'a...> T <: U |
| |
| let interner = self.interner; |
| |
| if let Variance::Invariant | Variance::Contravariant = variance { |
| let a_universal = self |
| .table |
| .instantiate_binders_universally(interner, a.clone()); |
| let b_existential = self |
| .table |
| .instantiate_binders_existentially(interner, b.clone()); |
| Zip::zip_with(self, Variance::Contravariant, &a_universal, &b_existential)?; |
| } |
| |
| if let Variance::Invariant | Variance::Covariant = variance { |
| let b_universal = self |
| .table |
| .instantiate_binders_universally(interner, b.clone()); |
| let a_existential = self |
| .table |
| .instantiate_binders_existentially(interner, a.clone()); |
| Zip::zip_with(self, Variance::Covariant, &a_existential, &b_universal)?; |
| } |
| |
| Ok(()) |
| } |
| |
| /// Relate an alias like `<T as Trait>::Item` or `impl Trait` with some other |
| /// type `ty`. If the variance is `Invariant`, creates a goal like |
| /// |
| /// ```notrust |
| /// AliasEq(<T as Trait>::Item = U) // associated type projection |
| /// AliasEq(impl Trait = U) // impl trait |
| /// ``` |
| /// Otherwise, this creates a new variable `?X`, creates a goal like |
| /// ```notrust |
| /// AliasEq(Alias = ?X) |
| /// ``` |
| /// and relates `?X` and `ty`. |
| #[instrument(level = "debug", skip(self))] |
| fn relate_alias_ty( |
| &mut self, |
| variance: Variance, |
| alias: &AliasTy<I>, |
| ty: &Ty<I>, |
| ) -> Fallible<()> { |
| let interner = self.interner; |
| match variance { |
| Variance::Invariant => { |
| self.goals.push(InEnvironment::new( |
| self.environment, |
| AliasEq { |
| alias: alias.clone(), |
| ty: ty.clone(), |
| } |
| .cast(interner), |
| )); |
| Ok(()) |
| } |
| Variance::Covariant | Variance::Contravariant => { |
| let var = self |
| .table |
| .new_variable(UniverseIndex::root()) |
| .to_ty(interner); |
| self.goals.push(InEnvironment::new( |
| self.environment, |
| AliasEq { |
| alias: alias.clone(), |
| ty: var.clone(), |
| } |
| .cast(interner), |
| )); |
| self.relate_ty_ty(variance, &var, ty) |
| } |
| } |
| } |
| |
| #[instrument(level = "debug", skip(self))] |
| fn generalize_ty( |
| &mut self, |
| ty: &Ty<I>, |
| universe_index: UniverseIndex, |
| variance: Variance, |
| ) -> Ty<I> { |
| let interner = self.interner; |
| match ty.kind(interner) { |
| TyKind::Adt(id, substitution) => { |
| let variances = if matches!(variance, Variance::Invariant) { |
| None |
| } else { |
| Some(self.unification_database().adt_variance(*id)) |
| }; |
| let get_variance = |i| { |
| variances |
| .as_ref() |
| .map(|v| v.as_slice(interner)[i]) |
| .unwrap_or(Variance::Invariant) |
| }; |
| TyKind::Adt( |
| *id, |
| self.generalize_substitution(substitution, universe_index, get_variance), |
| ) |
| .intern(interner) |
| } |
| TyKind::AssociatedType(id, substitution) => TyKind::AssociatedType( |
| *id, |
| self.generalize_substitution(substitution, universe_index, |_| variance), |
| ) |
| .intern(interner), |
| TyKind::Scalar(scalar) => TyKind::Scalar(*scalar).intern(interner), |
| TyKind::Str => TyKind::Str.intern(interner), |
| TyKind::Tuple(arity, substitution) => TyKind::Tuple( |
| *arity, |
| self.generalize_substitution(substitution, universe_index, |_| variance), |
| ) |
| .intern(interner), |
| TyKind::OpaqueType(id, substitution) => TyKind::OpaqueType( |
| *id, |
| self.generalize_substitution(substitution, universe_index, |_| variance), |
| ) |
| .intern(interner), |
| TyKind::Slice(ty) => { |
| TyKind::Slice(self.generalize_ty(ty, universe_index, variance)).intern(interner) |
| } |
| TyKind::FnDef(id, substitution) => { |
| let variances = if matches!(variance, Variance::Invariant) { |
| None |
| } else { |
| Some(self.unification_database().fn_def_variance(*id)) |
| }; |
| let get_variance = |i| { |
| variances |
| .as_ref() |
| .map(|v| v.as_slice(interner)[i]) |
| .unwrap_or(Variance::Invariant) |
| }; |
| TyKind::FnDef( |
| *id, |
| self.generalize_substitution(substitution, universe_index, get_variance), |
| ) |
| .intern(interner) |
| } |
| TyKind::Ref(mutability, lifetime, ty) => { |
| let lifetime_variance = variance.xform(Variance::Contravariant); |
| let ty_variance = match mutability { |
| Mutability::Not => Variance::Covariant, |
| Mutability::Mut => Variance::Invariant, |
| }; |
| TyKind::Ref( |
| *mutability, |
| self.generalize_lifetime(lifetime, universe_index, lifetime_variance), |
| self.generalize_ty(ty, universe_index, ty_variance), |
| ) |
| .intern(interner) |
| } |
| TyKind::Raw(mutability, ty) => { |
| let ty_variance = match mutability { |
| Mutability::Not => Variance::Covariant, |
| Mutability::Mut => Variance::Invariant, |
| }; |
| TyKind::Raw( |
| *mutability, |
| self.generalize_ty(ty, universe_index, ty_variance), |
| ) |
| .intern(interner) |
| } |
| TyKind::Never => TyKind::Never.intern(interner), |
| TyKind::Array(ty, const_) => TyKind::Array( |
| self.generalize_ty(ty, universe_index, variance), |
| self.generalize_const(const_, universe_index), |
| ) |
| .intern(interner), |
| TyKind::Closure(id, substitution) => TyKind::Closure( |
| *id, |
| self.generalize_substitution(substitution, universe_index, |_| variance), |
| ) |
| .intern(interner), |
| TyKind::Generator(id, substitution) => TyKind::Generator( |
| *id, |
| self.generalize_substitution(substitution, universe_index, |_| variance), |
| ) |
| .intern(interner), |
| TyKind::GeneratorWitness(id, substitution) => TyKind::GeneratorWitness( |
| *id, |
| self.generalize_substitution(substitution, universe_index, |_| variance), |
| ) |
| .intern(interner), |
| TyKind::Foreign(id) => TyKind::Foreign(*id).intern(interner), |
| TyKind::Error => TyKind::Error.intern(interner), |
| TyKind::Dyn(dyn_ty) => { |
| let DynTy { bounds, lifetime } = dyn_ty; |
| let lifetime = self.generalize_lifetime( |
| lifetime, |
| universe_index, |
| variance.xform(Variance::Contravariant), |
| ); |
| |
| let bounds = bounds.map_ref(|value| { |
| let iter = value.iter(interner).map(|sub_var| { |
| sub_var.map_ref(|clause| { |
| match clause { |
| WhereClause::Implemented(trait_ref) => { |
| let TraitRef { |
| ref substitution, |
| trait_id, |
| } = *trait_ref; |
| let substitution = self.generalize_substitution_skip_self( |
| substitution, |
| universe_index, |
| |_| Some(variance), |
| ); |
| WhereClause::Implemented(TraitRef { |
| substitution, |
| trait_id, |
| }) |
| } |
| WhereClause::AliasEq(alias_eq) => { |
| let AliasEq { alias, ty: _ } = alias_eq; |
| let alias = match alias { |
| AliasTy::Opaque(opaque_ty) => { |
| let OpaqueTy { |
| ref substitution, |
| opaque_ty_id, |
| } = *opaque_ty; |
| let substitution = self.generalize_substitution( |
| substitution, |
| universe_index, |
| |_| variance, |
| ); |
| AliasTy::Opaque(OpaqueTy { |
| substitution, |
| opaque_ty_id, |
| }) |
| } |
| AliasTy::Projection(projection_ty) => { |
| let ProjectionTy { |
| ref substitution, |
| associated_ty_id, |
| } = *projection_ty; |
| // TODO: We should be skipping "self", which |
| // would be the first element of |
| // "trait_params" if we had a |
| // `RustIrDatabase` to call |
| // `split_projection` on... |
| // let (assoc_ty_datum, trait_params, assoc_type_params) = s.db().split_projection(&self); |
| let substitution = self.generalize_substitution( |
| substitution, |
| universe_index, |
| |_| variance, |
| ); |
| AliasTy::Projection(ProjectionTy { |
| substitution, |
| associated_ty_id, |
| }) |
| } |
| }; |
| let ty = |
| self.table.new_variable(universe_index).to_ty(interner); |
| WhereClause::AliasEq(AliasEq { alias, ty }) |
| } |
| WhereClause::TypeOutlives(_) => { |
| let lifetime_var = self.table.new_variable(universe_index); |
| let lifetime = lifetime_var.to_lifetime(interner); |
| let ty_var = self.table.new_variable(universe_index); |
| let ty = ty_var.to_ty(interner); |
| WhereClause::TypeOutlives(TypeOutlives { ty, lifetime }) |
| } |
| WhereClause::LifetimeOutlives(_) => { |
| unreachable!("dyn Trait never contains LifetimeOutlive bounds") |
| } |
| } |
| }) |
| }); |
| QuantifiedWhereClauses::from_iter(interner, iter) |
| }); |
| |
| TyKind::Dyn(DynTy { bounds, lifetime }).intern(interner) |
| } |
| TyKind::Function(fn_ptr) => { |
| let FnPointer { |
| num_binders, |
| sig, |
| ref substitution, |
| } = *fn_ptr; |
| |
| let len = substitution.0.len(interner); |
| let vars = substitution.0.iter(interner).enumerate().map(|(i, var)| { |
| if i < len - 1 { |
| self.generalize_generic_var( |
| var, |
| universe_index, |
| variance.xform(Variance::Contravariant), |
| ) |
| } else { |
| self.generalize_generic_var( |
| substitution.0.as_slice(interner).last().unwrap(), |
| universe_index, |
| variance, |
| ) |
| } |
| }); |
| |
| let substitution = FnSubst(Substitution::from_iter(interner, vars)); |
| |
| TyKind::Function(FnPointer { |
| num_binders, |
| sig, |
| substitution, |
| }) |
| .intern(interner) |
| } |
| TyKind::Placeholder(_) | TyKind::BoundVar(_) => { |
| debug!("just generalizing to the ty itself: {:?}", ty); |
| // BoundVar and PlaceHolder have no internal values to be |
| // generic over, so we just relate directly to it |
| ty.clone() |
| } |
| TyKind::Alias(_) => { |
| let ena_var = self.table.new_variable(universe_index); |
| ena_var.to_ty(interner) |
| } |
| TyKind::InferenceVar(_var, kind) => { |
| if matches!(kind, TyVariableKind::Integer | TyVariableKind::Float) { |
| ty.clone() |
| } else if let Some(ty) = self.table.normalize_ty_shallow(interner, ty) { |
| self.generalize_ty(&ty, universe_index, variance) |
| } else if matches!(variance, Variance::Invariant) { |
| ty.clone() |
| } else { |
| let ena_var = self.table.new_variable(universe_index); |
| ena_var.to_ty(interner) |
| } |
| } |
| } |
| } |
| |
| #[instrument(level = "debug", skip(self))] |
| fn generalize_lifetime( |
| &mut self, |
| lifetime: &Lifetime<I>, |
| universe_index: UniverseIndex, |
| variance: Variance, |
| ) -> Lifetime<I> { |
| if matches!(lifetime.data(self.interner), LifetimeData::BoundVar(_)) |
| || matches!(variance, Variance::Invariant) |
| { |
| lifetime.clone() |
| } else { |
| self.table |
| .new_variable(universe_index) |
| .to_lifetime(self.interner) |
| } |
| } |
| |
| #[instrument(level = "debug", skip(self))] |
| fn generalize_const(&mut self, const_: &Const<I>, universe_index: UniverseIndex) -> Const<I> { |
| let data = const_.data(self.interner); |
| if matches!(data.value, ConstValue::BoundVar(_)) { |
| const_.clone() |
| } else { |
| self.table |
| .new_variable(universe_index) |
| .to_const(self.interner, data.ty.clone()) |
| } |
| } |
| |
| fn generalize_generic_var( |
| &mut self, |
| sub_var: &GenericArg<I>, |
| universe_index: UniverseIndex, |
| variance: Variance, |
| ) -> GenericArg<I> { |
| let interner = self.interner; |
| (match sub_var.data(interner) { |
| GenericArgData::Ty(ty) => { |
| GenericArgData::Ty(self.generalize_ty(ty, universe_index, variance)) |
| } |
| GenericArgData::Lifetime(lifetime) => GenericArgData::Lifetime( |
| self.generalize_lifetime(lifetime, universe_index, variance), |
| ), |
| GenericArgData::Const(const_value) => { |
| GenericArgData::Const(self.generalize_const(const_value, universe_index)) |
| } |
| }) |
| .intern(interner) |
| } |
| |
| /// Generalizes all but the first |
| #[instrument(level = "debug", skip(self, get_variance))] |
| fn generalize_substitution_skip_self<F: Fn(usize) -> Option<Variance>>( |
| &mut self, |
| substitution: &Substitution<I>, |
| universe_index: UniverseIndex, |
| get_variance: F, |
| ) -> Substitution<I> { |
| let interner = self.interner; |
| let vars = substitution.iter(interner).enumerate().map(|(i, sub_var)| { |
| if i == 0 { |
| sub_var.clone() |
| } else { |
| let variance = get_variance(i).unwrap_or(Variance::Invariant); |
| self.generalize_generic_var(sub_var, universe_index, variance) |
| } |
| }); |
| Substitution::from_iter(interner, vars) |
| } |
| |
| #[instrument(level = "debug", skip(self, get_variance))] |
| fn generalize_substitution<F: Fn(usize) -> Variance>( |
| &mut self, |
| substitution: &Substitution<I>, |
| universe_index: UniverseIndex, |
| get_variance: F, |
| ) -> Substitution<I> { |
| let interner = self.interner; |
| let vars = substitution.iter(interner).enumerate().map(|(i, sub_var)| { |
| let variance = get_variance(i); |
| self.generalize_generic_var(sub_var, universe_index, variance) |
| }); |
| |
| Substitution::from_iter(interner, vars) |
| } |
| |
| /// 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) |
| #[instrument(level = "debug", skip(self))] |
| fn relate_var_ty( |
| &mut self, |
| variance: Variance, |
| var: InferenceVar, |
| var_kind: TyVariableKind, |
| ty: &Ty<I>, |
| ) -> Fallible<()> { |
| let interner = self.interner; |
| |
| match (var_kind, ty.is_integer(interner), ty.is_float(interner)) { |
| // General inference variables can unify with any type |
| (TyVariableKind::General, _, _) |
| // Integer inference variables can only unify with integer types |
| | (TyVariableKind::Integer, true, _) |
| // Float inference variables can only unify with float types |
| | (TyVariableKind::Float, _, true) => { |
| }, |
| _ => return Err(NoSolution), |
| } |
| |
| 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 universe_index = self.table.max_universe(); |
| |
| debug!("relate_var_ty: universe index of var: {:?}", universe_index); |
| |
| debug!("trying fold_with on {:?}", ty); |
| let ty1 = ty |
| .clone() |
| .try_fold_with( |
| &mut OccursCheck::new(self, var, universe_index), |
| DebruijnIndex::INNERMOST, |
| ) |
| .map_err(|e| { |
| debug!("failed to fold {:?}", ty); |
| e |
| })?; |
| |
| // "Generalize" types. This ensures that we aren't accidentally forcing |
| // too much onto `var`. Instead of directly setting `var` equal to `ty`, |
| // we just take the outermost structure we _know_ `var` holds, and then |
| // apply that to `ty`. This involves creating new inference vars for |
| // everything inside `var`, then calling `relate_ty_ty` to relate those |
| // inference vars to the things they generalized with the correct |
| // variance. |
| |
| // The main problem this solves is that lifetime relationships are |
| // relationships, not just eq ones. So when solving &'a u32 <: U, |
| // generalizing we would end up with U = &'a u32. Instead, we want |
| // U = &'b u32, with a lifetime constraint 'a <: 'b. This matters |
| // especially when solving multiple constraints - for example, &'a u32 |
| // <: U, &'b u32 <: U (where without generalizing, we'd end up with 'a |
| // <: 'b, where we really want 'a <: 'c, 'b <: 'c for some 'c). |
| |
| // Example operation: consider `ty` as `&'x SomeType`. To generalize |
| // this, we create two new vars `'0` and `1`. Then we relate `var` with |
| // `&'0 1` and `&'0 1` with `&'x SomeType`. The second relation will |
| // recurse, and we'll end up relating `'0` with `'x` and `1` with `SomeType`. |
| let generalized_val = self.generalize_ty(&ty1, universe_index, variance); |
| |
| debug!("var {:?} generalized to {:?}", var, generalized_val); |
| |
| self.table |
| .unify |
| .unify_var_value( |
| var, |
| InferenceValue::from_ty(interner, generalized_val.clone()), |
| ) |
| .unwrap(); |
| debug!("var {:?} set to {:?}", var, generalized_val); |
| |
| self.relate_ty_ty(variance, &generalized_val, &ty1)?; |
| |
| debug!( |
| "generalized version {:?} related to original {:?}", |
| generalized_val, ty1 |
| ); |
| |
| Ok(()) |
| } |
| |
| fn relate_lifetime_lifetime( |
| &mut self, |
| variance: Variance, |
| 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!("relate_lifetime_lifetime", ?variance, ?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!(?var_a, ?var_b); |
| self.table.unify.unify_var_var(var_a, var_b).unwrap(); |
| Ok(()) |
| } |
| |
| ( |
| &LifetimeData::InferenceVar(a_var), |
| &LifetimeData::Placeholder(PlaceholderIndex { ui, .. }), |
| ) |
| | (&LifetimeData::InferenceVar(a_var), &LifetimeData::Empty(ui)) => { |
| self.unify_lifetime_var(variance, a_var, b, ui) |
| } |
| |
| ( |
| &LifetimeData::Placeholder(PlaceholderIndex { ui, .. }), |
| &LifetimeData::InferenceVar(b_var), |
| ) |
| | (&LifetimeData::Empty(ui), &LifetimeData::InferenceVar(b_var)) => { |
| self.unify_lifetime_var(variance.invert(), b_var, a, ui) |
| } |
| |
| (&LifetimeData::InferenceVar(a_var), &LifetimeData::Erased) |
| | (&LifetimeData::InferenceVar(a_var), &LifetimeData::Static) => { |
| self.unify_lifetime_var(variance, a_var, b, UniverseIndex::root()) |
| } |
| |
| (&LifetimeData::Erased, &LifetimeData::InferenceVar(b_var)) |
| | (&LifetimeData::Static, &LifetimeData::InferenceVar(b_var)) => { |
| self.unify_lifetime_var(variance.invert(), b_var, a, UniverseIndex::root()) |
| } |
| |
| (&LifetimeData::Static, &LifetimeData::Static) |
| | (&LifetimeData::Erased, &LifetimeData::Erased) => Ok(()), |
| |
| (&LifetimeData::Static, &LifetimeData::Placeholder(_)) |
| | (&LifetimeData::Static, &LifetimeData::Empty(_)) |
| | (&LifetimeData::Static, &LifetimeData::Erased) |
| | (&LifetimeData::Placeholder(_), &LifetimeData::Static) |
| | (&LifetimeData::Placeholder(_), &LifetimeData::Placeholder(_)) |
| | (&LifetimeData::Placeholder(_), &LifetimeData::Empty(_)) |
| | (&LifetimeData::Placeholder(_), &LifetimeData::Erased) |
| | (&LifetimeData::Empty(_), &LifetimeData::Static) |
| | (&LifetimeData::Empty(_), &LifetimeData::Placeholder(_)) |
| | (&LifetimeData::Empty(_), &LifetimeData::Empty(_)) |
| | (&LifetimeData::Empty(_), &LifetimeData::Erased) |
| | (&LifetimeData::Erased, &LifetimeData::Static) |
| | (&LifetimeData::Erased, &LifetimeData::Placeholder(_)) |
| | (&LifetimeData::Erased, &LifetimeData::Empty(_)) => { |
| if a != b { |
| self.push_lifetime_outlives_goals(variance, a.clone(), b.clone()); |
| Ok(()) |
| } 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))] |
| fn unify_lifetime_var( |
| &mut self, |
| variance: Variance, |
| 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) && matches!(variance, Variance::Invariant) { |
| debug!("{:?} 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!( |
| "{:?} in {:?} cannot see {:?}; pushing constraint", |
| var, var_ui, value_ui |
| ); |
| self.push_lifetime_outlives_goals( |
| variance, |
| var.to_lifetime(self.interner), |
| value.clone(), |
| ); |
| Ok(()) |
| } |
| } |
| |
| fn relate_const_const<'a>( |
| &mut self, |
| variance: Variance, |
| 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!("relate_const_const", ?variance, ?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.relate_ty_ty(variance, 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!(?var1, ?var2, "relate_ty_ty"); |
| let var1 = EnaVariable::from(var1); |
| let var2 = EnaVariable::from(var2); |
| self.table |
| .unify |
| .unify_var_var(var1, var2) |
| .expect("unification of two unbound variables cannot fail"); |
| Ok(()) |
| } |
| |
| // Unifying an inference variables with a non-inference variable. |
| (&ConstValue::InferenceVar(var), &ConstValue::Concrete(_)) |
| | (&ConstValue::InferenceVar(var), &ConstValue::Placeholder(_)) => { |
| debug!(?var, ty=?b, "unify_var_ty"); |
| self.unify_var_const(var, b) |
| } |
| |
| (&ConstValue::Concrete(_), &ConstValue::InferenceVar(var)) |
| | (&ConstValue::Placeholder(_), &ConstValue::InferenceVar(var)) => { |
| debug!(?var, ty=?a, "unify_var_ty"); |
| self.unify_var_const(var, a) |
| } |
| |
| (&ConstValue::Placeholder(p1), &ConstValue::Placeholder(p2)) => { |
| Zip::zip_with(self, variance, &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 |
| ), |
| } |
| } |
| |
| #[instrument(level = "debug", skip(self))] |
| fn unify_var_const(&mut self, var: InferenceVar, c: &Const<I>) -> Fallible<()> { |
| 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 c1 = c.clone().try_fold_with( |
| &mut OccursCheck::new(self, var, universe_index), |
| DebruijnIndex::INNERMOST, |
| )?; |
| |
| debug!("unify_var_const: var {:?} set to {:?}", var, c1); |
| self.table |
| .unify |
| .unify_var_value(var, InferenceValue::from_const(interner, c1)) |
| .unwrap(); |
| |
| Ok(()) |
| } |
| |
| /// Relate `a`, `b` such that if `variance = Covariant`, `a` is a subtype of |
| /// `b` and thus `a` must outlive `b`. |
| fn push_lifetime_outlives_goals(&mut self, variance: Variance, a: Lifetime<I>, b: Lifetime<I>) { |
| debug!( |
| "pushing lifetime outlives goals for a={:?} b={:?} with variance {:?}", |
| a, b, variance |
| ); |
| if matches!(variance, Variance::Invariant | Variance::Contravariant) { |
| self.goals.push(InEnvironment::new( |
| self.environment, |
| WhereClause::LifetimeOutlives(LifetimeOutlives { |
| a: a.clone(), |
| b: b.clone(), |
| }) |
| .cast(self.interner), |
| )); |
| } |
| if matches!(variance, Variance::Invariant | Variance::Covariant) { |
| self.goals.push(InEnvironment::new( |
| self.environment, |
| WhereClause::LifetimeOutlives(LifetimeOutlives { a: b, b: a }).cast(self.interner), |
| )); |
| } |
| } |
| |
| /// Pushes a goal of `a` being a subtype of `b`. |
| fn push_subtype_goal(&mut self, a: Ty<I>, b: Ty<I>) { |
| let subtype_goal = GoalData::SubtypeGoal(SubtypeGoal { a, b }).intern(self.interner()); |
| self.goals |
| .push(InEnvironment::new(self.environment, subtype_goal)); |
| } |
| } |
| |
| impl<'i, I: Interner> Zipper<I> for Unifier<'i, I> { |
| fn zip_tys(&mut self, variance: Variance, a: &Ty<I>, b: &Ty<I>) -> Fallible<()> { |
| debug!("zip_tys {:?}, {:?}, {:?}", variance, a, b); |
| self.relate_ty_ty(variance, a, b) |
| } |
| |
| fn zip_lifetimes( |
| &mut self, |
| variance: Variance, |
| a: &Lifetime<I>, |
| b: &Lifetime<I>, |
| ) -> Fallible<()> { |
| self.relate_lifetime_lifetime(variance, a, b) |
| } |
| |
| fn zip_consts(&mut self, variance: Variance, a: &Const<I>, b: &Const<I>) -> Fallible<()> { |
| self.relate_const_const(variance, a, b) |
| } |
| |
| fn zip_binders<T>(&mut self, variance: Variance, a: &Binders<T>, b: &Binders<T>) -> Fallible<()> |
| where |
| T: Clone + HasInterner<Interner = I> + Zip<I> + TypeFoldable<I>, |
| { |
| // 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 `relate_binders` routine. |
| |
| self.relate_binders(variance, a, b) |
| } |
| |
| fn interner(&self) -> I { |
| self.interner |
| } |
| |
| fn unification_database(&self) -> &dyn UnificationDatabase<I> { |
| self.db |
| } |
| } |
| |
| 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> FallibleTypeFolder<I> for OccursCheck<'_, 'i, I> { |
| type Error = NoSolution; |
| |
| fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder<I, Error = Self::Error> { |
| self |
| } |
| |
| fn try_fold_free_placeholder_ty( |
| &mut self, |
| universe: PlaceholderIndex, |
| _outer_binder: DebruijnIndex, |
| ) -> Fallible<Ty<I>> { |
| let interner = self.interner(); |
| if self.universe_index < universe.ui { |
| debug!( |
| "OccursCheck aborting because self.universe_index ({:?}) < universe.ui ({:?})", |
| self.universe_index, universe.ui |
| ); |
| Err(NoSolution) |
| } else { |
| Ok(universe.to_ty(interner)) // no need to shift, not relative to depth |
| } |
| } |
| |
| fn try_fold_free_placeholder_const( |
| &mut self, |
| ty: Ty<I>, |
| universe: PlaceholderIndex, |
| _outer_binder: DebruijnIndex, |
| ) -> Fallible<Const<I>> { |
| let interner = self.interner(); |
| if self.universe_index < universe.ui { |
| Err(NoSolution) |
| } else { |
| Ok(universe.to_const(interner, ty)) // no need to shift, not relative to depth |
| } |
| } |
| |
| #[instrument(level = "debug", skip(self))] |
| fn try_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_outlives_goals( |
| Variance::Invariant, |
| 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 try_fold_inference_ty( |
| &mut self, |
| var: InferenceVar, |
| kind: TyVariableKind, |
| _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 |
| .clone() |
| .try_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) { |
| debug!( |
| "OccursCheck aborting because {:?} unioned with {:?}", |
| 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 try_fold_inference_const( |
| &mut self, |
| ty: Ty<I>, |
| var: InferenceVar, |
| _outer_binder: DebruijnIndex, |
| ) -> Fallible<Const<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_const) => { |
| let normalized_const = normalized_const.assert_const_ref(interner); |
| let normalized_const = normalized_const |
| .clone() |
| .try_fold_with(self, DebruijnIndex::INNERMOST)?; |
| assert!(!normalized_const.needs_shift(interner)); |
| Ok(normalized_const) |
| } |
| |
| // 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: |
| // |
| // forall<const A> exists<const B> ?C = 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_const(interner, ty)) |
| } |
| } |
| } |
| |
| fn try_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.clone().try_fold_with(self, outer_binder)?; |
| assert!(!l.needs_shift(interner)); |
| Ok(l) |
| } |
| } |
| } |
| |
| fn forbid_free_vars(&self) -> bool { |
| true |
| } |
| |
| fn interner(&self) -> I { |
| self.unifier.interner |
| } |
| } |