//! # Lattice variables //! //! Generic code for operating on [lattices] of inference variables //! that are characterized by an upper- and lower-bound. //! //! The code is defined quite generically so that it can be //! applied both to type variables, which represent types being inferred, //! and fn variables, which represent function types being inferred. //! (It may eventually be applied to their types as well.) //! In some cases, the functions are also generic with respect to the //! operation on the lattice (GLB vs LUB). //! //! ## Note //! //! Although all the functions are generic, for simplicity, comments in the source code //! generally refer to type variables and the LUB operation. //! //! [lattices]: https://en.wikipedia.org/wiki/Lattice_(order) use rustc_middle::traits::solve::Goal; use rustc_middle::ty::relate::combine::{super_combine_consts, super_combine_tys}; use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; use rustc_middle::ty::{self, Ty, TyCtxt, TyVar, TypeVisitableExt}; use rustc_span::Span; use tracing::{debug, instrument}; use super::StructurallyRelateAliases; use super::combine::PredicateEmittingRelation; use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin, TypeTrace}; use crate::traits::{Obligation, PredicateObligations}; #[derive(Clone, Copy)] pub(crate) enum LatticeOpKind { Glb, Lub, } impl LatticeOpKind { fn invert(self) -> Self { match self { LatticeOpKind::Glb => LatticeOpKind::Lub, LatticeOpKind::Lub => LatticeOpKind::Glb, } } } /// A greatest lower bound" (common subtype) or least upper bound (common supertype). pub(crate) struct LatticeOp<'infcx, 'tcx> { infcx: &'infcx InferCtxt<'tcx>, // Immutable fields trace: TypeTrace<'tcx>, param_env: ty::ParamEnv<'tcx>, // Mutable fields kind: LatticeOpKind, obligations: PredicateObligations<'tcx>, } impl<'infcx, 'tcx> LatticeOp<'infcx, 'tcx> { pub(crate) fn new( infcx: &'infcx InferCtxt<'tcx>, trace: TypeTrace<'tcx>, param_env: ty::ParamEnv<'tcx>, kind: LatticeOpKind, ) -> LatticeOp<'infcx, 'tcx> { LatticeOp { infcx, trace, param_env, kind, obligations: PredicateObligations::new() } } pub(crate) fn into_obligations(self) -> PredicateObligations<'tcx> { self.obligations } } impl<'tcx> TypeRelation> for LatticeOp<'_, 'tcx> { fn cx(&self) -> TyCtxt<'tcx> { self.infcx.tcx } fn relate_with_variance>>( &mut self, variance: ty::Variance, _info: ty::VarianceDiagInfo>, a: T, b: T, ) -> RelateResult<'tcx, T> { match variance { ty::Invariant => { self.obligations.extend( self.infcx .at(&self.trace.cause, self.param_env) .eq_trace(DefineOpaqueTypes::Yes, self.trace.clone(), a, b)? .into_obligations(), ); Ok(a) } ty::Covariant => self.relate(a, b), // FIXME(#41044) -- not correct, need test ty::Bivariant => Ok(a), ty::Contravariant => { self.kind = self.kind.invert(); let res = self.relate(a, b); self.kind = self.kind.invert(); res } } } /// Relates two types using a given lattice. #[instrument(skip(self), level = "trace")] fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { if a == b { return Ok(a); } let infcx = self.infcx; let a = infcx.shallow_resolve(a); let b = infcx.shallow_resolve(b); match (a.kind(), b.kind()) { // If one side is known to be a variable and one is not, // create a variable (`v`) to represent the LUB. Make sure to // relate `v` to the non-type-variable first (by passing it // first to `relate_bound`). Otherwise, we would produce a // subtype obligation that must then be processed. // // Example: if the LHS is a type variable, and RHS is // `Box`, then we current compare `v` to the RHS first, // which will instantiate `v` with `Box`. Then when `v` // is compared to the LHS, we instantiate LHS with `Box`. // But if we did in reverse order, we would create a `v <: // LHS` (or vice versa) constraint and then instantiate // `v`. This would require further processing to achieve same // end-result; in particular, this screws up some of the logic // in coercion, which expects LUB to figure out that the LHS // is (e.g.) `Box`. A more obvious solution might be to // iterate on the subtype obligations that are returned, but I // think this suffices. -nmatsakis (&ty::Infer(TyVar(..)), _) => { let v = infcx.next_ty_var(self.trace.cause.span); self.relate_bound(v, b, a)?; Ok(v) } (_, &ty::Infer(TyVar(..))) => { let v = infcx.next_ty_var(self.trace.cause.span); self.relate_bound(v, a, b)?; Ok(v) } ( &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }), &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }), ) if a_def_id == b_def_id => super_combine_tys(infcx, self, a, b), (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _) | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) if def_id.is_local() && !infcx.next_trait_solver() => { self.register_goals(infcx.handle_opaque_type( a, b, self.span(), self.param_env(), )?); Ok(a) } _ => super_combine_tys(infcx, self, a, b), } } #[instrument(skip(self), level = "trace")] fn regions( &mut self, a: ty::Region<'tcx>, b: ty::Region<'tcx>, ) -> RelateResult<'tcx, ty::Region<'tcx>> { let origin = SubregionOrigin::Subtype(Box::new(self.trace.clone())); let mut inner = self.infcx.inner.borrow_mut(); let mut constraints = inner.unwrap_region_constraints(); Ok(match self.kind { // GLB(&'static u8, &'a u8) == &RegionLUB('static, 'a) u8 == &'static u8 LatticeOpKind::Glb => constraints.lub_regions(self.cx(), origin, a, b), // LUB(&'static u8, &'a u8) == &RegionGLB('static, 'a) u8 == &'a u8 LatticeOpKind::Lub => constraints.glb_regions(self.cx(), origin, a, b), }) } #[instrument(skip(self), level = "trace")] fn consts( &mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>, ) -> RelateResult<'tcx, ty::Const<'tcx>> { super_combine_consts(self.infcx, self, a, b) } fn binders( &mut self, a: ty::Binder<'tcx, T>, b: ty::Binder<'tcx, T>, ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> where T: Relate>, { // GLB/LUB of a binder and itself is just itself if a == b { return Ok(a); } debug!("binders(a={:?}, b={:?})", a, b); if a.skip_binder().has_escaping_bound_vars() || b.skip_binder().has_escaping_bound_vars() { // When higher-ranked types are involved, computing the GLB/LUB is // very challenging, switch to invariance. This is obviously // overly conservative but works ok in practice. self.relate_with_variance(ty::Invariant, ty::VarianceDiagInfo::default(), a, b)?; Ok(a) } else { Ok(ty::Binder::dummy(self.relate(a.skip_binder(), b.skip_binder())?)) } } } impl<'infcx, 'tcx> LatticeOp<'infcx, 'tcx> { // Relates the type `v` to `a` and `b` such that `v` represents // the LUB/GLB of `a` and `b` as appropriate. // // Subtle hack: ordering *may* be significant here. This method // relates `v` to `a` first, which may help us to avoid unnecessary // type variable obligations. See caller for details. fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> { let at = self.infcx.at(&self.trace.cause, self.param_env); match self.kind { LatticeOpKind::Glb => { self.obligations.extend(at.sub(DefineOpaqueTypes::Yes, v, a)?.into_obligations()); self.obligations.extend(at.sub(DefineOpaqueTypes::Yes, v, b)?.into_obligations()); } LatticeOpKind::Lub => { self.obligations.extend(at.sub(DefineOpaqueTypes::Yes, a, v)?.into_obligations()); self.obligations.extend(at.sub(DefineOpaqueTypes::Yes, b, v)?.into_obligations()); } } Ok(()) } } impl<'tcx> PredicateEmittingRelation> for LatticeOp<'_, 'tcx> { fn span(&self) -> Span { self.trace.span() } fn structurally_relate_aliases(&self) -> StructurallyRelateAliases { StructurallyRelateAliases::No } fn param_env(&self) -> ty::ParamEnv<'tcx> { self.param_env } fn register_predicates( &mut self, preds: impl IntoIterator, ty::Predicate<'tcx>>>, ) { self.obligations.extend(preds.into_iter().map(|pred| { Obligation::new(self.infcx.tcx, self.trace.cause.clone(), self.param_env, pred) })) } fn register_goals(&mut self, goals: impl IntoIterator>>) { self.obligations.extend(goals.into_iter().map(|goal| { Obligation::new( self.infcx.tcx, self.trace.cause.clone(), goal.param_env, goal.predicate, ) })) } fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { self.register_predicates([ty::Binder::dummy(ty::PredicateKind::AliasRelate( a.into(), b.into(), // FIXME(deferred_projection_equality): This isn't right, I think? ty::AliasRelationDirection::Equate, ))]); } }