blob: 86a24eef7f5bcfd895e13694b4cf893790bf2774 [file] [log] [blame]
use super::combine::CombineFields;
use crate::infer::BoundRegionConversionTime::HigherRankedType;
use crate::infer::{
DefineOpaqueTypes, ObligationEmittingRelation, StructurallyRelateAliases, SubregionOrigin,
};
use crate::traits::{Obligation, PredicateObligations};
use rustc_middle::ty::relate::{
relate_args_invariantly, relate_args_with_variances, Relate, RelateResult, TypeRelation,
};
use rustc_middle::ty::TyVar;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::Span;
/// Enforce that `a` is equal to or a subtype of `b`.
pub struct TypeRelating<'combine, 'a, 'tcx> {
fields: &'combine mut CombineFields<'a, 'tcx>,
structurally_relate_aliases: StructurallyRelateAliases,
ambient_variance: ty::Variance,
}
impl<'combine, 'infcx, 'tcx> TypeRelating<'combine, 'infcx, 'tcx> {
pub fn new(
f: &'combine mut CombineFields<'infcx, 'tcx>,
structurally_relate_aliases: StructurallyRelateAliases,
ambient_variance: ty::Variance,
) -> TypeRelating<'combine, 'infcx, 'tcx> {
TypeRelating { fields: f, structurally_relate_aliases, ambient_variance }
}
}
impl<'tcx> TypeRelation<'tcx> for TypeRelating<'_, '_, 'tcx> {
fn tag(&self) -> &'static str {
"TypeRelating"
}
fn tcx(&self) -> TyCtxt<'tcx> {
self.fields.infcx.tcx
}
fn relate_item_args(
&mut self,
item_def_id: rustc_hir::def_id::DefId,
a_arg: ty::GenericArgsRef<'tcx>,
b_arg: ty::GenericArgsRef<'tcx>,
) -> RelateResult<'tcx, ty::GenericArgsRef<'tcx>> {
if self.ambient_variance == ty::Variance::Invariant {
// Avoid fetching the variance if we are in an invariant
// context; no need, and it can induce dependency cycles
// (e.g., #41849).
relate_args_invariantly(self, a_arg, b_arg)
} else {
let tcx = self.tcx();
let opt_variances = tcx.variances_of(item_def_id);
relate_args_with_variances(self, item_def_id, opt_variances, a_arg, b_arg, false)
}
}
fn relate_with_variance<T: Relate<'tcx>>(
&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);
debug!(?self.ambient_variance, "new ambient variance");
let r = if self.ambient_variance == ty::Bivariant { Ok(a) } else { self.relate(a, b) };
self.ambient_variance = old_ambient_variance;
r
}
#[instrument(skip(self), level = "debug")]
fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
if a == b {
return Ok(a);
}
let infcx = self.fields.infcx;
let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a);
let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b);
match (a.kind(), b.kind()) {
(&ty::Infer(TyVar(a_id)), &ty::Infer(TyVar(b_id))) => {
match self.ambient_variance {
ty::Covariant => {
// can't make progress on `A <: B` if both A and B are
// type variables, so record an obligation.
self.fields.obligations.push(Obligation::new(
self.tcx(),
self.fields.trace.cause.clone(),
self.fields.param_env,
ty::Binder::dummy(ty::PredicateKind::Subtype(ty::SubtypePredicate {
a_is_expected: true,
a,
b,
})),
));
}
ty::Contravariant => {
// can't make progress on `B <: A` if both A and B are
// type variables, so record an obligation.
self.fields.obligations.push(Obligation::new(
self.tcx(),
self.fields.trace.cause.clone(),
self.fields.param_env,
ty::Binder::dummy(ty::PredicateKind::Subtype(ty::SubtypePredicate {
a_is_expected: false,
a: b,
b: a,
})),
));
}
ty::Invariant => {
infcx.inner.borrow_mut().type_variables().equate(a_id, b_id);
}
ty::Bivariant => {
unreachable!("Expected bivariance to be handled in relate_with_variance")
}
}
}
(&ty::Infer(TyVar(a_vid)), _) => {
infcx.instantiate_ty_var(self, true, a_vid, self.ambient_variance, b)?;
}
(_, &ty::Infer(TyVar(b_vid))) => {
infcx.instantiate_ty_var(
self,
false,
b_vid,
self.ambient_variance.xform(ty::Contravariant),
a,
)?;
}
(&ty::Error(e), _) | (_, &ty::Error(e)) => {
infcx.set_tainted_by_errors(e);
return Ok(Ty::new_error(self.tcx(), e));
}
(
&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.super_combine_tys(self, a, b)?;
}
(&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _)
| (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
if self.fields.define_opaque_types == DefineOpaqueTypes::Yes
&& def_id.is_local()
&& !infcx.next_trait_solver() =>
{
self.fields.obligations.extend(
infcx
.handle_opaque_type(a, b, &self.fields.trace.cause, self.param_env())?
.obligations,
);
}
_ => {
infcx.super_combine_tys(self, a, b)?;
}
}
Ok(a)
}
fn regions(
&mut self,
a: ty::Region<'tcx>,
b: ty::Region<'tcx>,
) -> RelateResult<'tcx, ty::Region<'tcx>> {
debug!("{}.regions({:?}, {:?})", self.tag(), a, b);
let origin = SubregionOrigin::Subtype(Box::new(self.fields.trace.clone()));
match self.ambient_variance {
// Subtype(&'a u8, &'b u8) => Outlives('a: 'b) => SubRegion('b, 'a)
ty::Covariant => {
self.fields
.infcx
.inner
.borrow_mut()
.unwrap_region_constraints()
.make_subregion(origin, b, a);
}
// Suptype(&'a u8, &'b u8) => Outlives('b: 'a) => SubRegion('a, 'b)
ty::Contravariant => {
self.fields
.infcx
.inner
.borrow_mut()
.unwrap_region_constraints()
.make_subregion(origin, a, b);
}
ty::Invariant => {
self.fields
.infcx
.inner
.borrow_mut()
.unwrap_region_constraints()
.make_eqregion(origin, a, b);
}
ty::Bivariant => {
unreachable!("Expected bivariance to be handled in relate_with_variance")
}
}
Ok(a)
}
fn consts(
&mut self,
a: ty::Const<'tcx>,
b: ty::Const<'tcx>,
) -> RelateResult<'tcx, ty::Const<'tcx>> {
self.fields.infcx.super_combine_consts(self, a, b)
}
fn binders<T>(
&mut self,
a: ty::Binder<'tcx, T>,
b: ty::Binder<'tcx, T>,
) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
where
T: Relate<'tcx>,
{
if a == b {
// Do nothing
} else if let Some(a) = a.no_bound_vars()
&& let Some(b) = b.no_bound_vars()
{
self.relate(a, b)?;
} else {
let span = self.fields.trace.cause.span;
let infcx = self.fields.infcx;
match self.ambient_variance {
// Checks whether `for<..> sub <: for<..> sup` holds.
//
// For this to hold, **all** instantiations of the super type
// have to be a super type of **at least one** instantiation of
// the subtype.
//
// This is implemented by first entering a new universe.
// We then replace all bound variables in `sup` with placeholders,
// and all bound variables in `sub` with inference vars.
// We can then just relate the two resulting types as normal.
//
// Note: this is a subtle algorithm. For a full explanation, please see
// the [rustc dev guide][rd]
//
// [rd]: https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference/placeholders_and_universes.html
ty::Covariant => {
infcx.enter_forall(b, |b| {
let a = infcx.instantiate_binder_with_fresh_vars(span, HigherRankedType, a);
self.relate(a, b)
})?;
}
ty::Contravariant => {
infcx.enter_forall(a, |a| {
let b = infcx.instantiate_binder_with_fresh_vars(span, HigherRankedType, b);
self.relate(a, b)
})?;
}
// When **equating** binders, we check that there is a 1-to-1
// correspondence between the bound vars in both types.
//
// We do so by separately instantiating one of the binders with
// placeholders and the other with inference variables and then
// equating the instantiated types.
//
// We want `for<..> A == for<..> B` -- therefore we want
// `exists<..> A == for<..> B` and `exists<..> B == for<..> A`.
// Check if `exists<..> A == for<..> B`
ty::Invariant => {
infcx.enter_forall(b, |b| {
let a = infcx.instantiate_binder_with_fresh_vars(span, HigherRankedType, a);
self.relate(a, b)
})?;
// Check if `exists<..> B == for<..> A`.
infcx.enter_forall(a, |a| {
let b = infcx.instantiate_binder_with_fresh_vars(span, HigherRankedType, b);
self.relate(a, b)
})?;
}
ty::Bivariant => {
unreachable!("Expected bivariance to be handled in relate_with_variance")
}
}
}
Ok(a)
}
}
impl<'tcx> ObligationEmittingRelation<'tcx> for TypeRelating<'_, '_, 'tcx> {
fn span(&self) -> Span {
self.fields.trace.span()
}
fn param_env(&self) -> ty::ParamEnv<'tcx> {
self.fields.param_env
}
fn structurally_relate_aliases(&self) -> StructurallyRelateAliases {
self.structurally_relate_aliases
}
fn register_predicates(&mut self, obligations: impl IntoIterator<Item: ty::ToPredicate<'tcx>>) {
self.fields.register_predicates(obligations);
}
fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) {
self.fields.register_obligations(obligations);
}
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!("Expected bivariance to be handled in relate_with_variance")
}
})]);
}
}