From 2415d433ba2f6578fc8f4c8d5d346a43c255799f Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 20 Feb 2024 17:45:45 +0000 Subject: [PATCH] Inline NllTypeRelating into its only usage site --- .../src/type_check/relate_tys.rs | 470 +++++++++++++- compiler/rustc_infer/src/infer/mod.rs | 1 - .../src/infer/relate/generalize.rs | 6 +- compiler/rustc_infer/src/infer/relate/mod.rs | 1 - compiler/rustc_infer/src/infer/relate/nll.rs | 573 ------------------ 5 files changed, 452 insertions(+), 599 deletions(-) delete mode 100644 compiler/rustc_infer/src/infer/relate/nll.rs diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs index 34cec4caea18c..123437d34a443 100644 --- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs +++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs @@ -1,11 +1,14 @@ +use rustc_data_structures::fx::FxHashMap; use rustc_errors::ErrorGuaranteed; -use rustc_infer::infer::nll_relate::{NllTypeRelating, NllTypeRelatingDelegate}; -use rustc_infer::infer::NllRegionVariableOrigin; -use rustc_infer::traits::PredicateObligations; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::{NllRegionVariableOrigin, ObligationEmittingRelation}; +use rustc_infer::traits::{Obligation, PredicateObligations}; use rustc_middle::mir::ConstraintCategory; use rustc_middle::traits::query::NoSolution; -use rustc_middle::ty::relate::TypeRelation; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::traits::ObligationCause; +use rustc_middle::ty::fold::FnMutDelegate; +use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; @@ -32,12 +35,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { locations: Locations, category: ConstraintCategory<'tcx>, ) -> Result<(), NoSolution> { - NllTypeRelating::new( - self.infcx, - NllTypeRelatingDelegate::new(self, locations, category, UniverseInfo::relate(a, b)), - v, - ) - .relate(a, b)?; + NllTypeRelating::new(self, locations, category, UniverseInfo::relate(a, b), v) + .relate(a, b)?; Ok(()) } @@ -50,8 +49,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { category: ConstraintCategory<'tcx>, ) -> Result<(), NoSolution> { NllTypeRelating::new( - self.infcx, - NllTypeRelatingDelegate::new(self, locations, category, UniverseInfo::other()), + self, + locations, + category, + UniverseInfo::other(), ty::Variance::Invariant, ) .relate(a, b)?; @@ -59,7 +60,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } -struct NllTypeRelatingDelegate<'me, 'bccx, 'tcx> { +pub struct NllTypeRelating<'me, 'bccx, 'tcx> { type_checker: &'me mut TypeChecker<'bccx, 'tcx>, /// Where (and why) is this relation taking place? @@ -71,26 +72,177 @@ struct NllTypeRelatingDelegate<'me, 'bccx, 'tcx> { /// Information so that error reporting knows what types we are relating /// when reporting a bound region error. universe_info: UniverseInfo<'tcx>, + + /// How are we relating `a` and `b`? + /// + /// - Covariant means `a <: b`. + /// - Contravariant means `b <: a`. + /// - Invariant means `a == b`. + /// - Bivariant means that it doesn't matter. + ambient_variance: ty::Variance, + + ambient_variance_info: ty::VarianceDiagInfo<'tcx>, } -impl<'me, 'bccx, 'tcx> NllTypeRelatingDelegate<'me, 'bccx, 'tcx> { - fn new( +impl<'me, 'bccx, 'tcx> NllTypeRelating<'me, 'bccx, 'tcx> { + pub fn new( type_checker: &'me mut TypeChecker<'bccx, 'tcx>, locations: Locations, category: ConstraintCategory<'tcx>, universe_info: UniverseInfo<'tcx>, + ambient_variance: ty::Variance, ) -> Self { - Self { type_checker, locations, category, universe_info } + Self { + type_checker, + locations, + category, + universe_info, + ambient_variance, + ambient_variance_info: ty::VarianceDiagInfo::default(), + } } -} -impl<'tcx> NllTypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx> { - fn span(&self) -> Span { - self.locations.span(self.type_checker.body) + fn ambient_covariance(&self) -> bool { + match self.ambient_variance { + ty::Variance::Covariant | ty::Variance::Invariant => true, + ty::Variance::Contravariant | ty::Variance::Bivariant => false, + } } - fn param_env(&self) -> ty::ParamEnv<'tcx> { - self.type_checker.param_env + fn ambient_contravariance(&self) -> bool { + match self.ambient_variance { + ty::Variance::Contravariant | ty::Variance::Invariant => true, + ty::Variance::Covariant | ty::Variance::Bivariant => false, + } + } + + fn relate_opaques(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> { + let infcx = self.type_checker.infcx; + debug_assert!(!infcx.next_trait_solver()); + let (a, b) = if self.a_is_expected() { (a, b) } else { (b, a) }; + // `handle_opaque_type` cannot handle subtyping, so to support subtyping + // we instead eagerly generalize here. This is a bit of a mess but will go + // away once we're using the new solver. + let mut enable_subtyping = |ty, ty_is_expected| { + let ty_vid = infcx.next_ty_var_id_in_universe( + TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: self.span(), + }, + ty::UniverseIndex::ROOT, + ); + + let variance = if ty_is_expected { + self.ambient_variance + } else { + self.ambient_variance.xform(ty::Contravariant) + }; + + self.type_checker.infcx.instantiate_ty_var( + self, + ty_is_expected, + ty_vid, + variance, + ty, + )?; + Ok(infcx.resolve_vars_if_possible(Ty::new_infer(infcx.tcx, ty::TyVar(ty_vid)))) + }; + + let (a, b) = match (a.kind(), b.kind()) { + (&ty::Alias(ty::Opaque, ..), _) => (a, enable_subtyping(b, false)?), + (_, &ty::Alias(ty::Opaque, ..)) => (enable_subtyping(a, true)?, b), + _ => unreachable!( + "expected at least one opaque type in `relate_opaques`, got {a} and {b}." + ), + }; + let cause = ObligationCause::dummy_with_span(self.span()); + let obligations = + infcx.handle_opaque_type(a, b, true, &cause, self.param_env())?.obligations; + self.register_obligations(obligations); + Ok(()) + } + + fn enter_forall( + &mut self, + binder: ty::Binder<'tcx, T>, + f: impl FnOnce(&mut Self, T) -> U, + ) -> U + where + T: ty::TypeFoldable> + Copy, + { + let value = if let Some(inner) = binder.no_bound_vars() { + inner + } else { + let infcx = self.type_checker.infcx; + let mut lazy_universe = None; + let delegate = FnMutDelegate { + regions: &mut |br: ty::BoundRegion| { + // The first time this closure is called, create a + // new universe for the placeholders we will make + // from here out. + let universe = lazy_universe.unwrap_or_else(|| { + let universe = self.create_next_universe(); + lazy_universe = Some(universe); + universe + }); + + let placeholder = ty::PlaceholderRegion { universe, bound: br }; + debug!(?placeholder); + let placeholder_reg = self.next_placeholder_region(placeholder); + debug!(?placeholder_reg); + + placeholder_reg + }, + types: &mut |_bound_ty: ty::BoundTy| { + unreachable!("we only replace regions in nll_relate, not types") + }, + consts: &mut |_bound_var: ty::BoundVar, _ty| { + unreachable!("we only replace regions in nll_relate, not consts") + }, + }; + + infcx.tcx.replace_bound_vars_uncached(binder, delegate) + }; + + debug!(?value); + f(self, value) + } + + #[instrument(skip(self), level = "debug")] + fn instantiate_binder_with_existentials(&mut self, binder: ty::Binder<'tcx, T>) -> T + where + T: ty::TypeFoldable> + Copy, + { + if let Some(inner) = binder.no_bound_vars() { + return inner; + } + + let infcx = self.type_checker.infcx; + let mut reg_map = FxHashMap::default(); + let delegate = FnMutDelegate { + regions: &mut |br: ty::BoundRegion| { + if let Some(ex_reg_var) = reg_map.get(&br) { + return *ex_reg_var; + } else { + let ex_reg_var = self.next_existential_region_var(true, br.kind.get_name()); + debug!(?ex_reg_var); + reg_map.insert(br, ex_reg_var); + + ex_reg_var + } + }, + types: &mut |_bound_ty: ty::BoundTy| { + unreachable!("we only replace regions in nll_relate, not types") + }, + consts: &mut |_bound_var: ty::BoundVar, _ty| { + unreachable!("we only replace regions in nll_relate, not consts") + }, + }; + + let replaced = infcx.tcx.replace_bound_vars_uncached(binder, delegate); + debug!(?replaced); + + replaced } fn create_next_universe(&mut self) -> ty::UniverseIndex { @@ -163,6 +315,250 @@ impl<'tcx> NllTypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tc }, ); } +} + +impl<'bccx, 'tcx> TypeRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.type_checker.infcx.tcx + } + + fn tag(&self) -> &'static str { + "nll::subtype" + } + + fn a_is_expected(&self) -> bool { + true + } + + #[instrument(skip(self, info), level = "trace", ret)] + fn relate_with_variance>( + &mut self, + variance: ty::Variance, + info: ty::VarianceDiagInfo<'tcx>, + a: T, + b: T, + ) -> RelateResult<'tcx, T> { + let old_ambient_variance = self.ambient_variance; + self.ambient_variance = self.ambient_variance.xform(variance); + self.ambient_variance_info = self.ambient_variance_info.xform(info); + + debug!(?self.ambient_variance); + // In a bivariant context this always succeeds. + let r = + if self.ambient_variance == ty::Variance::Bivariant { a } else { self.relate(a, b)? }; + + self.ambient_variance = old_ambient_variance; + + Ok(r) + } + + #[instrument(skip(self), level = "debug")] + fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + let infcx = self.type_checker.infcx; + + let a = self.type_checker.infcx.shallow_resolve(a); + assert!(!b.has_non_region_infer(), "unexpected inference var {:?}", b); + + if a == b { + return Ok(a); + } + + match (a.kind(), b.kind()) { + (_, &ty::Infer(ty::TyVar(_))) => { + span_bug!( + self.span(), + "should not be relating type variables on the right in MIR typeck" + ); + } + + (&ty::Infer(ty::TyVar(a_vid)), _) => { + infcx.instantiate_ty_var(self, true, a_vid, self.ambient_variance, b)? + } + + ( + &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 || infcx.next_trait_solver() => { + infcx.super_combine_tys(self, a, b).map(|_| ()).or_else(|err| { + // This behavior is only there for the old solver, the new solver + // shouldn't ever fail. Instead, it unconditionally emits an + // alias-relate goal. + assert!(!self.type_checker.infcx.next_trait_solver()); + self.tcx().dcx().span_delayed_bug( + self.span(), + "failure to relate an opaque to itself should result in an error later on", + ); + if a_def_id.is_local() { self.relate_opaques(a, b) } else { Err(err) } + })?; + } + (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _) + | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) + if def_id.is_local() && !self.type_checker.infcx.next_trait_solver() => + { + self.relate_opaques(a, b)?; + } + + _ => { + debug!(?a, ?b, ?self.ambient_variance); + + // Will also handle unification of `IntVar` and `FloatVar`. + self.type_checker.infcx.super_combine_tys(self, a, b)?; + } + } + + Ok(a) + } + + #[instrument(skip(self), level = "trace")] + fn regions( + &mut self, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + debug!(?self.ambient_variance); + + if self.ambient_covariance() { + // Covariant: &'a u8 <: &'b u8. Hence, `'a: 'b`. + self.push_outlives(a, b, self.ambient_variance_info); + } + + if self.ambient_contravariance() { + // Contravariant: &'b u8 <: &'a u8. Hence, `'b: 'a`. + self.push_outlives(b, a, self.ambient_variance_info); + } + + Ok(a) + } + + fn consts( + &mut self, + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { + let a = self.type_checker.infcx.shallow_resolve(a); + assert!(!a.has_non_region_infer(), "unexpected inference var {:?}", a); + assert!(!b.has_non_region_infer(), "unexpected inference var {:?}", b); + + // FIXME(invariance): This doesn't consider variance correctly. + self.type_checker.infcx.super_combine_consts(self, a, b) + } + + #[instrument(skip(self), level = "trace")] + fn binders( + &mut self, + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> + where + T: Relate<'tcx>, + { + // We want that + // + // ``` + // for<'a> fn(&'a u32) -> &'a u32 <: + // fn(&'b u32) -> &'b u32 + // ``` + // + // but not + // + // ``` + // fn(&'a u32) -> &'a u32 <: + // for<'b> fn(&'b u32) -> &'b u32 + // ``` + // + // We therefore proceed as follows: + // + // - Instantiate binders on `b` universally, yielding a universe U1. + // - Instantiate binders on `a` existentially in U1. + + debug!(?self.ambient_variance); + + if let (Some(a), Some(b)) = (a.no_bound_vars(), b.no_bound_vars()) { + // Fast path for the common case. + self.relate(a, b)?; + return Ok(ty::Binder::dummy(a)); + } + + if self.ambient_covariance() { + // Covariance, so we want `for<..> A <: for<..> B` -- + // therefore we compare any instantiation of A (i.e., A + // instantiated with existentials) against every + // instantiation of B (i.e., B instantiated with + // universals). + + // Reset the ambient variance to covariant. This is needed + // to correctly handle cases like + // + // for<'a> fn(&'a u32, &'a u32) == for<'b, 'c> fn(&'b u32, &'c u32) + // + // Somewhat surprisingly, these two types are actually + // **equal**, even though the one on the right looks more + // polymorphic. The reason is due to subtyping. To see it, + // consider that each function can call the other: + // + // - The left function can call the right with `'b` and + // `'c` both equal to `'a` + // + // - The right function can call the left with `'a` set to + // `{P}`, where P is the point in the CFG where the call + // itself occurs. Note that `'b` and `'c` must both + // include P. At the point, the call works because of + // subtyping (i.e., `&'b u32 <: &{P} u32`). + let variance = std::mem::replace(&mut self.ambient_variance, ty::Variance::Covariant); + + // Note: the order here is important. Create the placeholders first, otherwise + // we assign the wrong universe to the existential! + self.enter_forall(b, |this, b| { + let a = this.instantiate_binder_with_existentials(a); + this.relate(a, b) + })?; + + self.ambient_variance = variance; + } + + if self.ambient_contravariance() { + // Contravariance, so we want `for<..> A :> for<..> B` + // -- therefore we compare every instantiation of A (i.e., + // A instantiated with universals) against any + // instantiation of B (i.e., B instantiated with + // existentials). Opposite of above. + + // Reset ambient variance to contravariance. See the + // covariant case above for an explanation. + let variance = + std::mem::replace(&mut self.ambient_variance, ty::Variance::Contravariant); + + self.enter_forall(a, |this, a| { + let b = this.instantiate_binder_with_existentials(b); + this.relate(a, b) + })?; + + self.ambient_variance = variance; + } + + Ok(a) + } +} + +impl<'bccx, 'tcx> ObligationEmittingRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> { + fn span(&self) -> Span { + self.locations.span(self.type_checker.body) + } + + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.type_checker.param_env + } + + fn register_predicates(&mut self, obligations: impl IntoIterator>) { + self.register_obligations( + obligations + .into_iter() + .map(|to_pred| { + Obligation::new(self.tcx(), ObligationCause::dummy(), self.param_env(), to_pred) + }) + .collect(), + ); + } fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { let _: Result<_, ErrorGuaranteed> = self.type_checker.fully_perform_op( @@ -176,4 +572,32 @@ impl<'tcx> NllTypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tc }, ); } + + fn alias_relate_direction(&self) -> ty::AliasRelationDirection { + unreachable!("manually overridden to handle ty::Variance::Contravariant ambient variance") + } + + fn register_type_relate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { + self.register_predicates([ty::Binder::dummy(match self.ambient_variance { + ty::Variance::Covariant => ty::PredicateKind::AliasRelate( + a.into(), + b.into(), + ty::AliasRelationDirection::Subtype, + ), + // a :> b is b <: a + ty::Variance::Contravariant => ty::PredicateKind::AliasRelate( + b.into(), + a.into(), + ty::AliasRelationDirection::Subtype, + ), + ty::Variance::Invariant => ty::PredicateKind::AliasRelate( + a.into(), + b.into(), + ty::AliasRelationDirection::Equate, + ), + ty::Variance::Bivariant => { + unreachable!("cannot defer an alias-relate goal with Bivariant variance (yet?)") + } + })]); + } } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 243558b11a863..60711ffeb2ca9 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -54,7 +54,6 @@ use self::region_constraints::{ RegionConstraintCollector, RegionConstraintStorage, RegionSnapshot, }; pub use self::relate::combine::CombineFields; -pub use self::relate::nll as nll_relate; use self::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; pub mod at; diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index 371340c5bbfdf..90f10a0eba9e5 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -22,8 +22,12 @@ impl<'tcx> InferCtxt<'tcx> { /// subtyping could occur. This also does the occurs checks, detecting whether /// instantiating `target_vid` would result in a cyclic type. We eagerly error /// in this case. + /// + /// This is *not* expected to be used anywhere except for an implementation of + /// `TypeRelation`. Do not use this, and instead please use `At::eq`, for all + /// other usecases (i.e. setting the value of a type var). #[instrument(level = "debug", skip(self, relation, target_is_expected))] - pub(super) fn instantiate_ty_var>( + pub fn instantiate_ty_var>( &self, relation: &mut R, target_is_expected: bool, diff --git a/compiler/rustc_infer/src/infer/relate/mod.rs b/compiler/rustc_infer/src/infer/relate/mod.rs index 26270c77f1ae4..1207377e85712 100644 --- a/compiler/rustc_infer/src/infer/relate/mod.rs +++ b/compiler/rustc_infer/src/infer/relate/mod.rs @@ -8,5 +8,4 @@ mod glb; mod higher_ranked; mod lattice; mod lub; -pub mod nll; mod sub; diff --git a/compiler/rustc_infer/src/infer/relate/nll.rs b/compiler/rustc_infer/src/infer/relate/nll.rs deleted file mode 100644 index a34c3313b1a61..0000000000000 --- a/compiler/rustc_infer/src/infer/relate/nll.rs +++ /dev/null @@ -1,573 +0,0 @@ -//! This code is kind of an alternate way of doing subtyping, -//! supertyping, and type equating, distinct from the `combine.rs` -//! code but very similar in its effect and design. Eventually the two -//! ought to be merged. This code is intended for use in NLL and chalk. -//! -//! Here are the key differences: -//! -//! - This code may choose to bypass some checks (e.g., the occurs check) -//! in the case where we know that there are no unbound type inference -//! variables. This is the case for NLL, because at NLL time types are fully -//! inferred up-to regions. -//! - This code uses "universes" to handle higher-ranked regions and -//! not the leak-check. This is "more correct" than what rustc does -//! and we are generally migrating in this direction, but NLL had to -//! get there first. -//! -//! Also, this code assumes that there are no bound types at all, not even -//! free ones. This is ok because: -//! - we are not relating anything quantified over some type variable -//! - we will have instantiated all the bound type vars already (the one -//! thing we relate in chalk are basically domain goals and their -//! constituents) - -use rustc_data_structures::fx::FxHashMap; -use rustc_middle::traits::ObligationCause; -use rustc_middle::ty::fold::FnMutDelegate; -use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; -use rustc_middle::ty::TypeVisitableExt; -use rustc_middle::ty::{self, InferConst, Ty, TyCtxt}; -use rustc_span::{Span, Symbol}; - -use super::combine::ObligationEmittingRelation; -use crate::infer::InferCtxt; -use crate::infer::{TypeVariableOrigin, TypeVariableOriginKind}; -use crate::traits::{Obligation, PredicateObligations}; - -pub struct NllTypeRelating<'me, 'tcx, D> -where - D: NllTypeRelatingDelegate<'tcx>, -{ - infcx: &'me InferCtxt<'tcx>, - - /// Callback to use when we deduce an outlives relationship. - delegate: D, - - /// How are we relating `a` and `b`? - /// - /// - Covariant means `a <: b`. - /// - Contravariant means `b <: a`. - /// - Invariant means `a == b`. - /// - Bivariant means that it doesn't matter. - ambient_variance: ty::Variance, - - ambient_variance_info: ty::VarianceDiagInfo<'tcx>, -} - -pub trait NllTypeRelatingDelegate<'tcx> { - fn param_env(&self) -> ty::ParamEnv<'tcx>; - fn span(&self) -> Span; - - /// Push a constraint `sup: sub` -- this constraint must be - /// satisfied for the two types to be related. `sub` and `sup` may - /// be regions from the type or new variables created through the - /// delegate. - fn push_outlives( - &mut self, - sup: ty::Region<'tcx>, - sub: ty::Region<'tcx>, - info: ty::VarianceDiagInfo<'tcx>, - ); - - fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>); - - /// Creates a new universe index. Used when instantiating placeholders. - fn create_next_universe(&mut self) -> ty::UniverseIndex; - - /// Creates a new region variable representing a higher-ranked - /// region that is instantiated existentially. This creates an - /// inference variable, typically. - /// - /// So e.g., if you have `for<'a> fn(..) <: for<'b> fn(..)`, then - /// we will invoke this method to instantiate `'a` with an - /// inference variable (though `'b` would be instantiated first, - /// as a placeholder). - fn next_existential_region_var( - &mut self, - was_placeholder: bool, - name: Option, - ) -> ty::Region<'tcx>; - - /// Creates a new region variable representing a - /// higher-ranked region that is instantiated universally. - /// This creates a new region placeholder, typically. - /// - /// So e.g., if you have `for<'a> fn(..) <: for<'b> fn(..)`, then - /// we will invoke this method to instantiate `'b` with a - /// placeholder region. - fn next_placeholder_region(&mut self, placeholder: ty::PlaceholderRegion) -> ty::Region<'tcx>; -} - -impl<'me, 'tcx, D> NllTypeRelating<'me, 'tcx, D> -where - D: NllTypeRelatingDelegate<'tcx>, -{ - pub fn new(infcx: &'me InferCtxt<'tcx>, delegate: D, ambient_variance: ty::Variance) -> Self { - Self { - infcx, - delegate, - ambient_variance, - ambient_variance_info: ty::VarianceDiagInfo::default(), - } - } - - fn ambient_covariance(&self) -> bool { - match self.ambient_variance { - ty::Variance::Covariant | ty::Variance::Invariant => true, - ty::Variance::Contravariant | ty::Variance::Bivariant => false, - } - } - - fn ambient_contravariance(&self) -> bool { - match self.ambient_variance { - ty::Variance::Contravariant | ty::Variance::Invariant => true, - ty::Variance::Covariant | ty::Variance::Bivariant => false, - } - } - - /// Push a new outlives requirement into our output set of - /// constraints. - fn push_outlives( - &mut self, - sup: ty::Region<'tcx>, - sub: ty::Region<'tcx>, - info: ty::VarianceDiagInfo<'tcx>, - ) { - debug!("push_outlives({:?}: {:?})", sup, sub); - - self.delegate.push_outlives(sup, sub, info); - } - - fn relate_opaques(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> { - let infcx = self.infcx; - debug_assert!(!infcx.next_trait_solver()); - let (a, b) = if self.a_is_expected() { (a, b) } else { (b, a) }; - // `handle_opaque_type` cannot handle subtyping, so to support subtyping - // we instead eagerly generalize here. This is a bit of a mess but will go - // away once we're using the new solver. - let mut enable_subtyping = |ty, ty_is_expected| { - let ty_vid = infcx.next_ty_var_id_in_universe( - TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: self.delegate.span(), - }, - ty::UniverseIndex::ROOT, - ); - - let variance = if ty_is_expected { - self.ambient_variance - } else { - self.ambient_variance.xform(ty::Contravariant) - }; - - self.infcx.instantiate_ty_var(self, ty_is_expected, ty_vid, variance, ty)?; - Ok(infcx.resolve_vars_if_possible(Ty::new_infer(infcx.tcx, ty::TyVar(ty_vid)))) - }; - - let (a, b) = match (a.kind(), b.kind()) { - (&ty::Alias(ty::Opaque, ..), _) => (a, enable_subtyping(b, false)?), - (_, &ty::Alias(ty::Opaque, ..)) => (enable_subtyping(a, true)?, b), - _ => unreachable!( - "expected at least one opaque type in `relate_opaques`, got {a} and {b}." - ), - }; - let cause = ObligationCause::dummy_with_span(self.delegate.span()); - let obligations = - infcx.handle_opaque_type(a, b, true, &cause, self.delegate.param_env())?.obligations; - self.delegate.register_obligations(obligations); - Ok(()) - } - - fn enter_forall( - &mut self, - binder: ty::Binder<'tcx, T>, - f: impl FnOnce(&mut Self, T) -> U, - ) -> U - where - T: ty::TypeFoldable> + Copy, - { - let value = if let Some(inner) = binder.no_bound_vars() { - inner - } else { - let mut next_region = { - let nll_delegate = &mut self.delegate; - let mut lazy_universe = None; - - move |br: ty::BoundRegion| { - // The first time this closure is called, create a - // new universe for the placeholders we will make - // from here out. - let universe = lazy_universe.unwrap_or_else(|| { - let universe = nll_delegate.create_next_universe(); - lazy_universe = Some(universe); - universe - }); - - let placeholder = ty::PlaceholderRegion { universe, bound: br }; - debug!(?placeholder); - let placeholder_reg = nll_delegate.next_placeholder_region(placeholder); - debug!(?placeholder_reg); - - placeholder_reg - } - }; - - let delegate = FnMutDelegate { - regions: &mut next_region, - types: &mut |_bound_ty: ty::BoundTy| { - unreachable!("we only replace regions in nll_relate, not types") - }, - consts: &mut |_bound_var: ty::BoundVar, _ty| { - unreachable!("we only replace regions in nll_relate, not consts") - }, - }; - - self.infcx.tcx.replace_bound_vars_uncached(binder, delegate) - }; - - debug!(?value); - f(self, value) - } - - #[instrument(skip(self), level = "debug")] - fn instantiate_binder_with_existentials(&mut self, binder: ty::Binder<'tcx, T>) -> T - where - T: ty::TypeFoldable> + Copy, - { - if let Some(inner) = binder.no_bound_vars() { - return inner; - } - - let mut next_region = { - let nll_delegate = &mut self.delegate; - let mut reg_map = FxHashMap::default(); - - move |br: ty::BoundRegion| { - if let Some(ex_reg_var) = reg_map.get(&br) { - return *ex_reg_var; - } else { - let ex_reg_var = - nll_delegate.next_existential_region_var(true, br.kind.get_name()); - debug!(?ex_reg_var); - reg_map.insert(br, ex_reg_var); - - ex_reg_var - } - } - }; - - let delegate = FnMutDelegate { - regions: &mut next_region, - types: &mut |_bound_ty: ty::BoundTy| { - unreachable!("we only replace regions in nll_relate, not types") - }, - consts: &mut |_bound_var: ty::BoundVar, _ty| { - unreachable!("we only replace regions in nll_relate, not consts") - }, - }; - - let replaced = self.infcx.tcx.replace_bound_vars_uncached(binder, delegate); - debug!(?replaced); - - replaced - } -} - -impl<'tcx, D> TypeRelation<'tcx> for NllTypeRelating<'_, 'tcx, D> -where - D: NllTypeRelatingDelegate<'tcx>, -{ - fn tcx(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - fn tag(&self) -> &'static str { - "nll::subtype" - } - - fn a_is_expected(&self) -> bool { - true - } - - #[instrument(skip(self, info), level = "trace", ret)] - fn relate_with_variance>( - &mut self, - variance: ty::Variance, - info: ty::VarianceDiagInfo<'tcx>, - a: T, - b: T, - ) -> RelateResult<'tcx, T> { - let old_ambient_variance = self.ambient_variance; - self.ambient_variance = self.ambient_variance.xform(variance); - self.ambient_variance_info = self.ambient_variance_info.xform(info); - - debug!(?self.ambient_variance); - // In a bivariant context this always succeeds. - let r = - if self.ambient_variance == ty::Variance::Bivariant { a } else { self.relate(a, b)? }; - - self.ambient_variance = old_ambient_variance; - - Ok(r) - } - - #[instrument(skip(self), level = "debug")] - fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { - let infcx = self.infcx; - - let a = self.infcx.shallow_resolve(a); - assert!(!b.has_non_region_infer(), "unexpected inference var {:?}", b); - - if a == b { - return Ok(a); - } - - match (a.kind(), b.kind()) { - (&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => { - match self.ambient_variance { - ty::Invariant => infcx.inner.borrow_mut().type_variables().equate(a_vid, b_vid), - _ => unimplemented!(), - } - } - - (&ty::Infer(ty::TyVar(a_vid)), _) => { - infcx.instantiate_ty_var(self, true, a_vid, self.ambient_variance, b)? - } - - (_, &ty::Infer(ty::TyVar(b_vid))) => infcx.instantiate_ty_var( - self, - false, - b_vid, - self.ambient_variance.xform(ty::Contravariant), - a, - )?, - - ( - &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 || infcx.next_trait_solver() => { - infcx.super_combine_tys(self, a, b).map(|_| ()).or_else(|err| { - // This behavior is only there for the old solver, the new solver - // shouldn't ever fail. Instead, it unconditionally emits an - // alias-relate goal. - assert!(!self.infcx.next_trait_solver()); - self.tcx().dcx().span_delayed_bug( - self.delegate.span(), - "failure to relate an opaque to itself should result in an error later on", - ); - if a_def_id.is_local() { self.relate_opaques(a, b) } else { Err(err) } - })?; - } - (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _) - | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) - if def_id.is_local() && !self.infcx.next_trait_solver() => - { - self.relate_opaques(a, b)?; - } - - _ => { - debug!(?a, ?b, ?self.ambient_variance); - - // Will also handle unification of `IntVar` and `FloatVar`. - self.infcx.super_combine_tys(self, a, b)?; - } - } - - Ok(a) - } - - #[instrument(skip(self), level = "trace")] - fn regions( - &mut self, - a: ty::Region<'tcx>, - b: ty::Region<'tcx>, - ) -> RelateResult<'tcx, ty::Region<'tcx>> { - debug!(?self.ambient_variance); - - if self.ambient_covariance() { - // Covariant: &'a u8 <: &'b u8. Hence, `'a: 'b`. - self.push_outlives(a, b, self.ambient_variance_info); - } - - if self.ambient_contravariance() { - // Contravariant: &'b u8 <: &'a u8. Hence, `'b: 'a`. - self.push_outlives(b, a, self.ambient_variance_info); - } - - Ok(a) - } - - fn consts( - &mut self, - a: ty::Const<'tcx>, - b: ty::Const<'tcx>, - ) -> RelateResult<'tcx, ty::Const<'tcx>> { - let a = self.infcx.shallow_resolve(a); - assert!(!b.has_non_region_infer(), "unexpected inference var {:?}", b); - - match b.kind() { - ty::ConstKind::Infer(InferConst::Var(_)) => { - // Forbid inference variables in the RHS. - self.infcx - .dcx() - .span_bug(self.delegate.span(), format!("unexpected inference var {b:?}")); - } - // FIXME(invariance): see the related FIXME above. - _ => self.infcx.super_combine_consts(self, a, b), - } - } - - #[instrument(skip(self), level = "trace")] - fn binders( - &mut self, - a: ty::Binder<'tcx, T>, - b: ty::Binder<'tcx, T>, - ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> - where - T: Relate<'tcx>, - { - // We want that - // - // ``` - // for<'a> fn(&'a u32) -> &'a u32 <: - // fn(&'b u32) -> &'b u32 - // ``` - // - // but not - // - // ``` - // fn(&'a u32) -> &'a u32 <: - // for<'b> fn(&'b u32) -> &'b u32 - // ``` - // - // We therefore proceed as follows: - // - // - Instantiate binders on `b` universally, yielding a universe U1. - // - Instantiate binders on `a` existentially in U1. - - debug!(?self.ambient_variance); - - if let (Some(a), Some(b)) = (a.no_bound_vars(), b.no_bound_vars()) { - // Fast path for the common case. - self.relate(a, b)?; - return Ok(ty::Binder::dummy(a)); - } - - if self.ambient_covariance() { - // Covariance, so we want `for<..> A <: for<..> B` -- - // therefore we compare any instantiation of A (i.e., A - // instantiated with existentials) against every - // instantiation of B (i.e., B instantiated with - // universals). - - // Reset the ambient variance to covariant. This is needed - // to correctly handle cases like - // - // for<'a> fn(&'a u32, &'a u32) == for<'b, 'c> fn(&'b u32, &'c u32) - // - // Somewhat surprisingly, these two types are actually - // **equal**, even though the one on the right looks more - // polymorphic. The reason is due to subtyping. To see it, - // consider that each function can call the other: - // - // - The left function can call the right with `'b` and - // `'c` both equal to `'a` - // - // - The right function can call the left with `'a` set to - // `{P}`, where P is the point in the CFG where the call - // itself occurs. Note that `'b` and `'c` must both - // include P. At the point, the call works because of - // subtyping (i.e., `&'b u32 <: &{P} u32`). - let variance = std::mem::replace(&mut self.ambient_variance, ty::Variance::Covariant); - - // Note: the order here is important. Create the placeholders first, otherwise - // we assign the wrong universe to the existential! - self.enter_forall(b, |this, b| { - let a = this.instantiate_binder_with_existentials(a); - this.relate(a, b) - })?; - - self.ambient_variance = variance; - } - - if self.ambient_contravariance() { - // Contravariance, so we want `for<..> A :> for<..> B` - // -- therefore we compare every instantiation of A (i.e., - // A instantiated with universals) against any - // instantiation of B (i.e., B instantiated with - // existentials). Opposite of above. - - // Reset ambient variance to contravariance. See the - // covariant case above for an explanation. - let variance = - std::mem::replace(&mut self.ambient_variance, ty::Variance::Contravariant); - - self.enter_forall(a, |this, a| { - let b = this.instantiate_binder_with_existentials(b); - this.relate(a, b) - })?; - - self.ambient_variance = variance; - } - - Ok(a) - } -} - -impl<'tcx, D> ObligationEmittingRelation<'tcx> for NllTypeRelating<'_, 'tcx, D> -where - D: NllTypeRelatingDelegate<'tcx>, -{ - fn span(&self) -> Span { - self.delegate.span() - } - - fn param_env(&self) -> ty::ParamEnv<'tcx> { - self.delegate.param_env() - } - - fn register_predicates(&mut self, obligations: impl IntoIterator>) { - self.delegate.register_obligations( - obligations - .into_iter() - .map(|to_pred| { - Obligation::new(self.tcx(), ObligationCause::dummy(), self.param_env(), to_pred) - }) - .collect(), - ); - } - - fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { - self.delegate.register_obligations(obligations); - } - - fn alias_relate_direction(&self) -> ty::AliasRelationDirection { - unreachable!("manually overridden to handle ty::Variance::Contravariant ambient variance") - } - - fn register_type_relate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { - self.register_predicates([ty::Binder::dummy(match self.ambient_variance { - ty::Variance::Covariant => ty::PredicateKind::AliasRelate( - a.into(), - b.into(), - ty::AliasRelationDirection::Subtype, - ), - // a :> b is b <: a - ty::Variance::Contravariant => ty::PredicateKind::AliasRelate( - b.into(), - a.into(), - ty::AliasRelationDirection::Subtype, - ), - ty::Variance::Invariant => ty::PredicateKind::AliasRelate( - a.into(), - b.into(), - ty::AliasRelationDirection::Equate, - ), - // FIXME(deferred_projection_equality): Implement this when we trigger it. - // Probably just need to do nothing here. - ty::Variance::Bivariant => { - unreachable!("cannot defer an alias-relate goal with Bivariant variance (yet?)") - } - })]); - } -}