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 InferenceTable { pub fn relate( &mut self, interner: I, db: &dyn UnificationDatabase, environment: &Environment, variance: Variance, a: &T, b: &T, ) -> Fallible> where T: ?Sized + Zip, { 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, environment: &'t Environment, goals: Vec>>, interner: I, db: &'t dyn UnificationDatabase, } #[derive(Debug)] pub struct RelationResult { pub goals: Vec>>, } impl<'t, I: Interner> Unifier<'t, I> { fn new( interner: I, db: &'t dyn UnificationDatabase, table: &'t mut InferenceTable, environment: &'t Environment, ) -> 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(mut self, variance: Variance, a: &T, b: &T) -> Fallible> where T: ?Sized + Zip, { 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, b: &Ty) -> 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 { T }` with some other forall type `forall { 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, ) -> 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, b: &Binders, ) -> Fallible<()> where T: Clone + TypeFoldable + HasInterner + Zip, '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 `::Item` or `impl Trait` with some other /// type `ty`. If the variance is `Invariant`, creates a goal like /// /// ```notrust /// AliasEq(::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, ty: &Ty, ) -> 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, universe_index: UniverseIndex, variance: Variance, ) -> Ty { 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, universe_index: UniverseIndex, variance: Variance, ) -> Lifetime { 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, universe_index: UniverseIndex) -> Const { 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, universe_index: UniverseIndex, variance: Variance, ) -> GenericArg { 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 Option>( &mut self, substitution: &Substitution, universe_index: UniverseIndex, get_variance: F, ) -> Substitution { 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 Variance>( &mut self, substitution: &Substitution, universe_index: UniverseIndex, get_variance: F, ) -> Substitution { 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, ) -> 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, b: &Lifetime, ) -> 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, .. }), ) => self.unify_lifetime_var(variance, a_var, b, ui), ( &LifetimeData::Placeholder(PlaceholderIndex { 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::Erased) | (&LifetimeData::Placeholder(_), &LifetimeData::Static) | (&LifetimeData::Placeholder(_), &LifetimeData::Placeholder(_)) | (&LifetimeData::Placeholder(_), &LifetimeData::Erased) | (&LifetimeData::Erased, &LifetimeData::Static) | (&LifetimeData::Erased, &LifetimeData::Placeholder(_)) => { 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, 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, b: &'a Const, ) -> 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) -> 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, b: Lifetime) { 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, b: Ty) { 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 for Unifier<'i, I> { fn zip_tys(&mut self, variance: Variance, a: &Ty, b: &Ty) -> Fallible<()> { debug!("zip_tys {:?}, {:?}, {:?}", variance, a, b); self.relate_ty_ty(variance, a, b) } fn zip_lifetimes( &mut self, variance: Variance, a: &Lifetime, b: &Lifetime, ) -> Fallible<()> { self.relate_lifetime_lifetime(variance, a, b) } fn zip_consts(&mut self, variance: Variance, a: &Const, b: &Const) -> Fallible<()> { self.relate_const_const(variance, a, b) } fn zip_binders(&mut self, variance: Variance, a: &Binders, b: &Binders) -> Fallible<()> where T: Clone + HasInterner + Zip + TypeFoldable, { // 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 { self.db } } struct OccursCheck<'u, 't, I: Interner> { unifier: &'u mut Unifier<'t, I>, var: EnaVariable, universe_index: UniverseIndex, } impl<'u, 't, I: Interner> OccursCheck<'u, 't, I> { fn new( unifier: &'u mut Unifier<'t, I>, var: EnaVariable, universe_index: UniverseIndex, ) -> Self { OccursCheck { unifier, var, universe_index, } } } impl<'i, I: Interner> FallibleTypeFolder for OccursCheck<'_, 'i, I> { type Error = NoSolution; fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder { self } fn try_fold_free_placeholder_ty( &mut self, universe: PlaceholderIndex, _outer_binder: DebruijnIndex, ) -> Fallible> { 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, universe: PlaceholderIndex, _outer_binder: DebruijnIndex, ) -> Fallible> { 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> { let interner = self.interner(); if self.universe_index < ui.ui { // Scenario is like: // // exists 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> { 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, var: InferenceVar, _outer_binder: DebruijnIndex, ) -> Fallible> { 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 exists ?C = Foo // // 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> { // 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 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 } }