diff --git a/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs b/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs index 2ba72180d66a6..f1338b3bf1ee5 100644 --- a/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs +++ b/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs @@ -1,8 +1,11 @@ use std::collections::BTreeMap; +use rustc_hir::def_id::DefId; use rustc_index::bit_set::SparseBitMatrix; use rustc_middle::mir::{Body, Location}; -use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; +use rustc_middle::ty::relate::{ + self, Relate, RelateResult, TypeRelation, relate_args_with_variances, +}; use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeVisitable}; use rustc_mir_dataflow::points::PointIndex; @@ -256,6 +259,20 @@ impl<'tcx> TypeRelation> for VarianceExtractor<'_, 'tcx> { self.tcx } + fn relate_ty_args( + &mut self, + a_ty: Ty<'tcx>, + _: Ty<'tcx>, + def_id: DefId, + a_args: ty::GenericArgsRef<'tcx>, + b_args: ty::GenericArgsRef<'tcx>, + _: impl FnOnce(ty::GenericArgsRef<'tcx>) -> Ty<'tcx>, + ) -> RelateResult<'tcx, Ty<'tcx>> { + let variances = self.cx().variances_of(def_id); + relate_args_with_variances(self, variances, a_args, b_args)?; + Ok(a_ty) + } + fn relate_with_variance>>( &mut self, variance: ty::Variance, diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs index 7ac2dff12f756..539912609baae 100644 --- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs +++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs @@ -1,5 +1,6 @@ use rustc_data_structures::fx::FxHashMap; use rustc_errors::ErrorGuaranteed; +use rustc_hir::def_id::DefId; use rustc_infer::infer::relate::{ PredicateEmittingRelation, Relate, RelateResult, StructurallyRelateAliases, TypeRelation, }; @@ -9,7 +10,8 @@ use rustc_infer::traits::solve::Goal; use rustc_middle::mir::ConstraintCategory; use rustc_middle::traits::ObligationCause; use rustc_middle::traits::query::NoSolution; -use rustc_middle::ty::relate::combine::{super_combine_consts, super_combine_tys}; +use rustc_middle::ty::relate::combine::{combine_ty_args, super_combine_consts, super_combine_tys}; +use rustc_middle::ty::relate::relate_args_invariantly; use rustc_middle::ty::{self, FnMutDelegate, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::{bug, span_bug}; use rustc_span::{Span, Symbol, sym}; @@ -303,6 +305,35 @@ impl<'b, 'tcx> TypeRelation> for NllTypeRelating<'_, 'b, 'tcx> { self.type_checker.infcx.tcx } + fn relate_ty_args( + &mut self, + a_ty: Ty<'tcx>, + b_ty: Ty<'tcx>, + def_id: DefId, + a_args: ty::GenericArgsRef<'tcx>, + b_args: ty::GenericArgsRef<'tcx>, + _: impl FnOnce(ty::GenericArgsRef<'tcx>) -> Ty<'tcx>, + ) -> RelateResult<'tcx, Ty<'tcx>> { + if self.ambient_variance == ty::Invariant { + // Avoid fetching the variance if we are in an invariant context, + // slightly improves perf. + relate_args_invariantly(self, a_args, b_args)?; + Ok(a_ty) + } else { + let variances = self.cx().variances_of(def_id); + combine_ty_args( + &self.type_checker.infcx.infcx, + self, + a_ty, + b_ty, + variances, + a_args, + b_args, + |_| a_ty, + ) + } + } + #[instrument(skip(self, info), level = "trace", ret)] fn relate_with_variance>>( &mut self, @@ -328,7 +359,7 @@ impl<'b, 'tcx> TypeRelation> for NllTypeRelating<'_, 'b, 'tcx> { 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); + let a = infcx.shallow_resolve(a); assert!(!b.has_non_region_infer(), "unexpected inference var {:?}", b); if a == b { diff --git a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs index 6592360cf0a5c..7be5daf610565 100644 --- a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs +++ b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs @@ -1,8 +1,10 @@ use std::collections::hash_map::Entry; use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def_id::DefId; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; +use rustc_type_ir::relate::relate_args_with_variances; use tracing::instrument; use crate::infer::region_constraints::VerifyIfEq; @@ -137,6 +139,20 @@ impl<'tcx> TypeRelation> for MatchAgainstHigherRankedOutlives<'tcx> self.tcx } + fn relate_ty_args( + &mut self, + a_ty: Ty<'tcx>, + _: Ty<'tcx>, + def_id: DefId, + a_args: ty::GenericArgsRef<'tcx>, + b_args: ty::GenericArgsRef<'tcx>, + _: impl FnOnce(ty::GenericArgsRef<'tcx>) -> Ty<'tcx>, + ) -> RelateResult<'tcx, Ty<'tcx>> { + let variances = self.cx().variances_of(def_id); + relate_args_with_variances(self, variances, a_args, b_args)?; + Ok(a_ty) + } + #[instrument(level = "trace", skip(self))] fn relate_with_variance>>( &mut self, @@ -145,6 +161,10 @@ impl<'tcx> TypeRelation> for MatchAgainstHigherRankedOutlives<'tcx> a: T, b: T, ) -> RelateResult<'tcx, T> { + // FIXME(@lcnr): This is weird. We are ignoring the ambient variance + // here, effectively treating everything as being in either a covariant + // or contravariant context. + // // Opaque types args have lifetime parameters. // We must not check them to be equal, as we never insert anything to make them so. if variance != ty::Bivariant { self.relate(a, b) } else { Ok(a) } diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index cc41957c110f7..69c090b662e54 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -70,14 +70,13 @@ impl<'tcx> InferCtxt<'tcx> { // // We then relate `generalized_ty <: source_ty`, adding constraints like `'x: '?2` and // `?1 <: ?3`. - let Generalization { value_may_be_infer: generalized_ty, has_unconstrained_ty_var } = self - .generalize( - relation.span(), - relation.structurally_relate_aliases(), - target_vid, - instantiation_variance, - source_ty, - )?; + let Generalization { value_may_be_infer: generalized_ty } = self.generalize( + relation.span(), + relation.structurally_relate_aliases(), + target_vid, + instantiation_variance, + source_ty, + )?; // Constrain `b_vid` to the generalized type `generalized_ty`. if let &ty::Infer(ty::TyVar(generalized_vid)) = generalized_ty.kind() { @@ -86,11 +85,6 @@ impl<'tcx> InferCtxt<'tcx> { self.inner.borrow_mut().type_variables().instantiate(target_vid, generalized_ty); } - // See the comment on `Generalization::has_unconstrained_ty_var`. - if has_unconstrained_ty_var { - relation.register_predicates([ty::ClauseKind::WellFormed(generalized_ty.into())]); - } - // Finally, relate `generalized_ty` to `source_ty`, as described in previous comment. // // FIXME(#16847): This code is non-ideal because all these subtype @@ -210,19 +204,15 @@ impl<'tcx> InferCtxt<'tcx> { ) -> RelateResult<'tcx, ()> { // FIXME(generic_const_exprs): Occurs check failures for unevaluated // constants and generic expressions are not yet handled correctly. - let Generalization { value_may_be_infer: generalized_ct, has_unconstrained_ty_var } = self - .generalize( - relation.span(), - relation.structurally_relate_aliases(), - target_vid, - ty::Invariant, - source_ct, - )?; + let Generalization { value_may_be_infer: generalized_ct } = self.generalize( + relation.span(), + relation.structurally_relate_aliases(), + target_vid, + ty::Invariant, + source_ct, + )?; debug_assert!(!generalized_ct.is_ct_infer()); - if has_unconstrained_ty_var { - bug!("unconstrained ty var when generalizing `{source_ct:?}`"); - } self.inner .borrow_mut() @@ -281,12 +271,10 @@ impl<'tcx> InferCtxt<'tcx> { ambient_variance, in_alias: false, cache: Default::default(), - has_unconstrained_ty_var: false, }; let value_may_be_infer = generalizer.relate(source_term, source_term)?; - let has_unconstrained_ty_var = generalizer.has_unconstrained_ty_var; - Ok(Generalization { value_may_be_infer, has_unconstrained_ty_var }) + Ok(Generalization { value_may_be_infer }) } } @@ -376,9 +364,6 @@ struct Generalizer<'me, 'tcx> { in_alias: bool, cache: SsoHashMap<(Ty<'tcx>, ty::Variance, bool), Ty<'tcx>>, - - /// See the field `has_unconstrained_ty_var` in `Generalization`. - has_unconstrained_ty_var: bool, } impl<'tcx> Generalizer<'_, 'tcx> { @@ -391,10 +376,8 @@ impl<'tcx> Generalizer<'_, 'tcx> { } /// Create a new type variable in the universe of the target when - /// generalizing an alias. This has to set `has_unconstrained_ty_var` - /// if we're currently in a bivariant context. - fn next_ty_var_for_alias(&mut self) -> Ty<'tcx> { - self.has_unconstrained_ty_var |= self.ambient_variance == ty::Bivariant; + /// generalizing an alias. + fn next_ty_var_for_alias(&self) -> Ty<'tcx> { self.infcx.next_ty_var_in_universe(self.span, self.for_universe) } @@ -461,29 +444,26 @@ impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { self.infcx.tcx } - fn relate_item_args( + fn relate_ty_args( &mut self, - item_def_id: DefId, - a_arg: ty::GenericArgsRef<'tcx>, - b_arg: ty::GenericArgsRef<'tcx>, - ) -> RelateResult<'tcx, ty::GenericArgsRef<'tcx>> { - if self.ambient_variance == ty::Invariant { + a_ty: Ty<'tcx>, + _: Ty<'tcx>, + def_id: DefId, + a_args: ty::GenericArgsRef<'tcx>, + b_args: ty::GenericArgsRef<'tcx>, + mk: impl FnOnce(ty::GenericArgsRef<'tcx>) -> Ty<'tcx>, + ) -> RelateResult<'tcx, Ty<'tcx>> { + let args = if self.ambient_variance == ty::Invariant { // Avoid fetching the variance if we are in an invariant // context; no need, and it can induce dependency cycles // (e.g., #41849). - relate::relate_args_invariantly(self, a_arg, b_arg) + relate::relate_args_invariantly(self, a_args, b_args) } else { let tcx = self.cx(); - let opt_variances = tcx.variances_of(item_def_id); - relate::relate_args_with_variances( - self, - item_def_id, - opt_variances, - a_arg, - b_arg, - false, - ) - } + let variances = tcx.variances_of(def_id); + relate::relate_args_with_variances(self, variances, a_args, b_args) + }?; + if args == a_args { Ok(a_ty) } else { Ok(mk(args)) } } #[instrument(level = "debug", skip(self, variance, b), ret)] @@ -545,14 +525,8 @@ impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { } } - // Bivariant: make a fresh var, but remember that - // it is unconstrained. See the comment in - // `Generalization`. - ty::Bivariant => self.has_unconstrained_ty_var = true, - - // Co/contravariant: this will be - // sufficiently constrained later on. - ty::Covariant | ty::Contravariant => (), + // We do need a fresh type variable otherwise. + ty::Bivariant | ty::Covariant | ty::Contravariant => (), } let origin = inner.type_variables().var_origin(vid); @@ -771,32 +745,8 @@ struct Generalization { /// for `?0` generalization returns an inference /// variable. /// - /// This has to be handled wotj care as it can + /// This has to be handled with care as it can /// otherwise very easily result in infinite /// recursion. pub value_may_be_infer: T, - - /// In general, we do not check whether all types which occur during - /// type checking are well-formed. We only check wf of user-provided types - /// and when actually using a type, e.g. for method calls. - /// - /// This means that when subtyping, we may end up with unconstrained - /// inference variables if a generalized type has bivariant parameters. - /// A parameter may only be bivariant if it is constrained by a projection - /// bound in a where-clause. As an example, imagine a type: - /// - /// struct Foo where A: Iterator { - /// data: A - /// } - /// - /// here, `A` will be covariant, but `B` is unconstrained. - /// - /// However, whatever it is, for `Foo` to be WF, it must be equal to `A::Item`. - /// If we have an input `Foo`, then after generalization we will wind - /// up with a type like `Foo`. When we enforce `Foo <: Foo`, - /// we will wind up with the requirement that `?A <: ?C`, but no particular - /// relationship between `?B` and `?D` (after all, these types may be completely - /// different). If we do nothing else, this may mean that `?D` goes unconstrained - /// (as in #41677). To avoid this we emit a `WellFormed` obligation in these cases. - pub has_unconstrained_ty_var: bool, } diff --git a/compiler/rustc_infer/src/infer/relate/lattice.rs b/compiler/rustc_infer/src/infer/relate/lattice.rs index 4eb77a99be78e..a05e2d40e829f 100644 --- a/compiler/rustc_infer/src/infer/relate/lattice.rs +++ b/compiler/rustc_infer/src/infer/relate/lattice.rs @@ -17,8 +17,9 @@ //! //! [lattices]: https://en.wikipedia.org/wiki/Lattice_(order) +use rustc_hir::def_id::DefId; use rustc_middle::traits::solve::Goal; -use rustc_middle::ty::relate::combine::{super_combine_consts, super_combine_tys}; +use rustc_middle::ty::relate::combine::{combine_ty_args, 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; @@ -75,6 +76,19 @@ impl<'tcx> TypeRelation> for LatticeOp<'_, 'tcx> { self.infcx.tcx } + fn relate_ty_args( + &mut self, + a_ty: Ty<'tcx>, + b_ty: Ty<'tcx>, + def_id: DefId, + a_args: ty::GenericArgsRef<'tcx>, + b_args: ty::GenericArgsRef<'tcx>, + mk: impl FnOnce(ty::GenericArgsRef<'tcx>) -> Ty<'tcx>, + ) -> RelateResult<'tcx, Ty<'tcx>> { + let variances = self.cx().variances_of(def_id); + combine_ty_args(self.infcx, self, a_ty, b_ty, variances, a_args, b_args, |args| mk(args)) + } + fn relate_with_variance>>( &mut self, variance: ty::Variance, diff --git a/compiler/rustc_infer/src/infer/relate/type_relating.rs b/compiler/rustc_infer/src/infer/relate/type_relating.rs index 04ff776594e66..96a0375f5fba6 100644 --- a/compiler/rustc_infer/src/infer/relate/type_relating.rs +++ b/compiler/rustc_infer/src/infer/relate/type_relating.rs @@ -1,8 +1,7 @@ +use rustc_hir::def_id::DefId; 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, relate_args_invariantly, relate_args_with_variances, -}; +use rustc_middle::ty::relate::combine::{combine_ty_args, super_combine_consts, super_combine_tys}; +use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation, relate_args_invariantly}; use rustc_middle::ty::{self, DelayedSet, Ty, TyCtxt, TyVar}; use rustc_span::Span; use tracing::{debug, instrument}; @@ -79,21 +78,24 @@ impl<'tcx> TypeRelation> for TypeRelating<'_, 'tcx> { self.infcx.tcx } - fn relate_item_args( + fn relate_ty_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>> { + a_ty: Ty<'tcx>, + b_ty: Ty<'tcx>, + def_id: DefId, + a_args: ty::GenericArgsRef<'tcx>, + b_args: ty::GenericArgsRef<'tcx>, + _: impl FnOnce(ty::GenericArgsRef<'tcx>) -> Ty<'tcx>, + ) -> RelateResult<'tcx, Ty<'tcx>> { if self.ambient_variance == ty::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) + relate_args_invariantly(self, a_args, b_args)?; + Ok(a_ty) } else { - let tcx = self.cx(); - let opt_variances = tcx.variances_of(item_def_id); - relate_args_with_variances(self, item_def_id, opt_variances, a_arg, b_arg, false) + let variances = self.cx().variances_of(def_id); + combine_ty_args(self.infcx, self, a_ty, b_ty, variances, a_args, b_args, |_| a_ty) } } diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index b9afb62cf1c64..f54afce0615fe 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -12,7 +12,8 @@ use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_macros::LintDiagnostic; use rustc_middle::middle::resolve_bound_vars::ResolvedArg; use rustc_middle::ty::relate::{ - Relate, RelateResult, TypeRelation, structurally_relate_consts, structurally_relate_tys, + Relate, RelateResult, TypeRelation, relate_args_with_variances, structurally_relate_consts, + structurally_relate_tys, }; use rustc_middle::ty::{ self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, @@ -502,6 +503,20 @@ impl<'tcx> TypeRelation> for FunctionalVariances<'tcx> { self.tcx } + fn relate_ty_args( + &mut self, + a_ty: Ty<'tcx>, + _: Ty<'tcx>, + def_id: DefId, + a_args: ty::GenericArgsRef<'tcx>, + b_args: ty::GenericArgsRef<'tcx>, + _: impl FnOnce(ty::GenericArgsRef<'tcx>) -> Ty<'tcx>, + ) -> RelateResult<'tcx, Ty<'tcx>> { + let variances = self.cx().variances_of(def_id); + relate_args_with_variances(self, variances, a_args, b_args)?; + Ok(a_ty) + } + fn relate_with_variance>>( &mut self, variance: ty::Variance, diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index b280a6ec55af7..32be23eed7658 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -2069,6 +2069,19 @@ impl<'tcx> TypeRelation> for SameTypeModuloInfer<'_, 'tcx> { self.0.tcx } + fn relate_ty_args( + &mut self, + a_ty: Ty<'tcx>, + _: Ty<'tcx>, + _: DefId, + a_args: ty::GenericArgsRef<'tcx>, + b_args: ty::GenericArgsRef<'tcx>, + _: impl FnOnce(ty::GenericArgsRef<'tcx>) -> Ty<'tcx>, + ) -> RelateResult<'tcx, Ty<'tcx>> { + relate::relate_args_invariantly(self, a_args, b_args)?; + Ok(a_ty) + } + fn relate_with_variance>>( &mut self, _variance: ty::Variance, diff --git a/compiler/rustc_trait_selection/src/traits/select/_match.rs b/compiler/rustc_trait_selection/src/traits/select/_match.rs index 7c19c35a4f784..27de754aa7dc9 100644 --- a/compiler/rustc_trait_selection/src/traits/select/_match.rs +++ b/compiler/rustc_trait_selection/src/traits/select/_match.rs @@ -1,3 +1,4 @@ +use rustc_hir::def_id::DefId; use rustc_infer::infer::relate::{ self, Relate, RelateResult, TypeRelation, structurally_relate_tys, }; @@ -36,6 +37,19 @@ impl<'tcx> TypeRelation> for MatchAgainstFreshVars<'tcx> { self.tcx } + fn relate_ty_args( + &mut self, + a_ty: Ty<'tcx>, + _: Ty<'tcx>, + _: DefId, + a_args: ty::GenericArgsRef<'tcx>, + b_args: ty::GenericArgsRef<'tcx>, + _: impl FnOnce(ty::GenericArgsRef<'tcx>) -> Ty<'tcx>, + ) -> RelateResult<'tcx, Ty<'tcx>> { + relate::relate_args_invariantly(self, a_args, b_args)?; + Ok(a_ty) + } + fn relate_with_variance>>( &mut self, _: ty::Variance, diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index fc74cbf478236..4f843503d1afe 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -73,20 +73,15 @@ pub trait TypeRelation: Sized { Relate::relate(self, a, b) } - /// Relate the two args for the given item. The default - /// is to look up the variance for the item and proceed - /// accordingly. - #[instrument(skip(self), level = "trace")] - fn relate_item_args( + fn relate_ty_args( &mut self, - item_def_id: I::DefId, + a_ty: I::Ty, + b_ty: I::Ty, + ty_def_id: I::DefId, a_arg: I::GenericArgs, b_arg: I::GenericArgs, - ) -> RelateResult { - let cx = self.cx(); - let opt_variances = cx.variances_of(item_def_id); - relate_args_with_variances(self, item_def_id, opt_variances, a_arg, b_arg, true) - } + mk: impl FnOnce(I::GenericArgs) -> I::Ty, + ) -> RelateResult; /// Switch variance for the purpose of relating `a` and `b`. fn relate_with_variance>( @@ -138,27 +133,17 @@ pub fn relate_args_invariantly>( pub fn relate_args_with_variances>( relation: &mut R, - ty_def_id: I::DefId, variances: I::VariancesOf, - a_arg: I::GenericArgs, - b_arg: I::GenericArgs, - fetch_ty_for_diag: bool, + a_args: I::GenericArgs, + b_args: I::GenericArgs, ) -> RelateResult { let cx = relation.cx(); - - let mut cached_ty = None; - let params = iter::zip(a_arg.iter(), b_arg.iter()).enumerate().map(|(i, (a, b))| { + let args = iter::zip(a_args.iter(), b_args.iter()).enumerate().map(|(i, (a, b))| { let variance = variances.get(i).unwrap(); - let variance_info = if variance == ty::Invariant && fetch_ty_for_diag { - let ty = *cached_ty.get_or_insert_with(|| cx.type_of(ty_def_id).instantiate(cx, a_arg)); - VarianceDiagInfo::Invariant { ty, param_index: i.try_into().unwrap() } - } else { - VarianceDiagInfo::default() - }; - relation.relate_with_variance(variance, variance_info, a, b) + relation.relate_with_variance(variance, VarianceDiagInfo::None, a, b) }); - - cx.mk_args_from_iter(params) + // FIXME: We can probably try to reuse `a_args` here if it did not change. + cx.mk_args_from_iter(args) } impl Relate for ty::FnSig { @@ -240,10 +225,7 @@ impl Relate for ty::AliasTy { } else { let cx = relation.cx(); let args = if let Some(variances) = cx.opt_alias_variances(a.kind(cx), a.def_id) { - relate_args_with_variances( - relation, a.def_id, variances, a.args, b.args, - false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle - )? + relate_args_with_variances(relation, variances, a.args, b.args)? } else { relate_args_invariantly(relation, a.args, b.args)? }; @@ -268,11 +250,9 @@ impl Relate for ty::AliasTerm { let args = match a.kind(relation.cx()) { ty::AliasTermKind::OpaqueTy => relate_args_with_variances( relation, - a.def_id, relation.cx().variances_of(a.def_id), a.args, b.args, - false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle )?, ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::FreeConst @@ -402,12 +382,13 @@ pub fn structurally_relate_tys>( (ty::Placeholder(p1), ty::Placeholder(p2)) if p1 == p2 => Ok(a), (ty::Adt(a_def, a_args), ty::Adt(b_def, b_args)) if a_def == b_def => { - Ok(if a_args.is_empty() { - a + if a_args.is_empty() { + Ok(a) } else { - let args = relation.relate_item_args(a_def.def_id().into(), a_args, b_args)?; - if args == a_args { a } else { Ty::new_adt(cx, a_def, args) } - }) + relation.relate_ty_args(a, b, a_def.def_id().into(), a_args, b_args, |args| { + Ty::new_adt(cx, a_def, args) + }) + } } (ty::Foreign(a_id), ty::Foreign(b_id)) if a_id == b_id => Ok(Ty::new_foreign(cx, a_id)), @@ -516,12 +497,13 @@ pub fn structurally_relate_tys>( } (ty::FnDef(a_def_id, a_args), ty::FnDef(b_def_id, b_args)) if a_def_id == b_def_id => { - Ok(if a_args.is_empty() { - a + if a_args.is_empty() { + Ok(a) } else { - let args = relation.relate_item_args(a_def_id.into(), a_args, b_args)?; - if args == a_args { a } else { Ty::new_fn_def(cx, a_def_id, args) } - }) + relation.relate_ty_args(a, b, a_def_id.into(), a_args, b_args, |args| { + Ty::new_fn_def(cx, a_def_id, args) + }) + } } (ty::FnPtr(a_sig_tys, a_hdr), ty::FnPtr(b_sig_tys, b_hdr)) => { diff --git a/compiler/rustc_type_ir/src/relate/combine.rs b/compiler/rustc_type_ir/src/relate/combine.rs index 8dd7c4df24421..64b87fac77f94 100644 --- a/compiler/rustc_type_ir/src/relate/combine.rs +++ b/compiler/rustc_type_ir/src/relate/combine.rs @@ -1,3 +1,5 @@ +use std::iter; + use tracing::debug; use super::{ @@ -6,6 +8,7 @@ use super::{ }; use crate::error::TypeError; use crate::inherent::*; +use crate::relate::VarianceDiagInfo; use crate::solve::Goal; use crate::visit::TypeVisitableExt as _; use crate::{self as ty, InferCtxtLike, Interner, TypingMode, Upcast}; @@ -219,3 +222,75 @@ where _ => structurally_relate_consts(relation, a, b), } } + +pub fn combine_ty_args( + infcx: &Infcx, + relation: &mut R, + a_ty: I::Ty, + b_ty: I::Ty, + variances: I::VariancesOf, + a_args: I::GenericArgs, + b_args: I::GenericArgs, + mk: impl FnOnce(I::GenericArgs) -> I::Ty, +) -> RelateResult +where + Infcx: InferCtxtLike, + I: Interner, + R: PredicateEmittingRelation, +{ + let cx = infcx.cx(); + let mut has_unconstrained_bivariant_arg = false; + let args = iter::zip(a_args.iter(), b_args.iter()).enumerate().map(|(i, (a, b))| { + let variance = variances.get(i).unwrap(); + let variance_info = match variance { + ty::Invariant => { + VarianceDiagInfo::Invariant { ty: a_ty, param_index: i.try_into().unwrap() } + } + ty::Covariant | ty::Contravariant => VarianceDiagInfo::default(), + ty::Bivariant => { + let has_non_region_infer = |arg: I::GenericArg| { + arg.has_non_region_infer() + && infcx.resolve_vars_if_possible(arg).has_non_region_infer() + }; + if has_non_region_infer(a) || has_non_region_infer(b) { + has_unconstrained_bivariant_arg = true; + } + VarianceDiagInfo::default() + } + }; + relation.relate_with_variance(variance, variance_info, a, b) + }); + let args = cx.mk_args_from_iter(args)?; + + // In general, we do not check whether all types which occur during + // type checking are well-formed. We only check wf of user-provided types + // and when actually using a type, e.g. for method calls. + // + // This means that when subtyping, we may end up with unconstrained + // inference variables if a generalized type has bivariant parameters. + // A parameter may only be bivariant if it is constrained by a projection + // bound in a where-clause. As an example, imagine a type: + // + // struct Foo where A: Iterator { + // data: A + // } + // + // here, `A` will be covariant, but `B` is unconstrained. However, whatever it is, + // for `Foo` to be WF, it must be equal to `A::Item`. + // + // If we have an input `Foo`, then after generalization we will wind + // up with a type like `Foo`. When we enforce `Foo <: Foo`, + // we will wind up with the requirement that `?A <: ?C`, but no particular + // relationship between `?B` and `?D` (after all, these types may be completely + // different). If we do nothing else, this may mean that `?D` goes unconstrained + // (as in #41677). To avoid this we emit a `WellFormed` when relating types with + // bivariant arguments. + if has_unconstrained_bivariant_arg { + relation.register_predicates([ + ty::ClauseKind::WellFormed(a_ty.into()), + ty::ClauseKind::WellFormed(b_ty.into()), + ]); + } + + if a_args == args { Ok(a_ty) } else { Ok(mk(args)) } +} diff --git a/compiler/rustc_type_ir/src/relate/solver_relating.rs b/compiler/rustc_type_ir/src/relate/solver_relating.rs index 79f6bc5dabb40..82ee4f75fcb0a 100644 --- a/compiler/rustc_type_ir/src/relate/solver_relating.rs +++ b/compiler/rustc_type_ir/src/relate/solver_relating.rs @@ -2,6 +2,7 @@ use tracing::{debug, instrument}; use self::combine::{PredicateEmittingRelation, super_combine_consts, super_combine_tys}; use crate::data_structures::DelayedSet; +use crate::relate::combine::combine_ty_args; pub use crate::relate::*; use crate::solve::Goal; use crate::{self as ty, InferCtxtLike, Interner}; @@ -139,24 +140,26 @@ where self.infcx.cx() } - fn relate_item_args( + fn relate_ty_args( &mut self, - item_def_id: I::DefId, - a_arg: I::GenericArgs, - b_arg: I::GenericArgs, - ) -> RelateResult { + a_ty: I::Ty, + b_ty: I::Ty, + def_id: I::DefId, + a_args: I::GenericArgs, + b_args: I::GenericArgs, + _: impl FnOnce(I::GenericArgs) -> I::Ty, + ) -> RelateResult { if self.ambient_variance == ty::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) + relate_args_invariantly(self, a_args, b_args)?; + Ok(a_ty) } else { - let tcx = self.cx(); - let opt_variances = tcx.variances_of(item_def_id); - relate_args_with_variances(self, item_def_id, opt_variances, a_arg, b_arg, false) + let variances = self.cx().variances_of(def_id); + combine_ty_args(self.infcx, self, a_ty, b_ty, variances, a_args, b_args, |_| a_ty) } } - fn relate_with_variance>( &mut self, variance: ty::Variance, diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions-id-trait.current.stderr b/tests/ui/cast/ptr-to-trait-obj-different-regions-id-trait.current.stderr index 4e43d3b93fa35..62f17a6568550 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-regions-id-trait.current.stderr +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions-id-trait.current.stderr @@ -6,10 +6,6 @@ LL | fn m<'a>() { LL | let unsend: *const dyn Cat<'a> = &(); LL | let _send = unsend as *const S>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` - | - = note: requirement occurs because of the type `S>`, which makes the generic argument `dyn Cat<'_>` invariant - = note: the struct `S` is invariant over the parameter `T` - = help: see for more information about variance error: aborting due to 1 previous error diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions-id-trait.next.stderr b/tests/ui/cast/ptr-to-trait-obj-different-regions-id-trait.next.stderr index 4e43d3b93fa35..62f17a6568550 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-regions-id-trait.next.stderr +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions-id-trait.next.stderr @@ -6,10 +6,6 @@ LL | fn m<'a>() { LL | let unsend: *const dyn Cat<'a> = &(); LL | let _send = unsend as *const S>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` - | - = note: requirement occurs because of the type `S>`, which makes the generic argument `dyn Cat<'_>` invariant - = note: the struct `S` is invariant over the parameter `T` - = help: see for more information about variance error: aborting due to 1 previous error diff --git a/tests/ui/fn/implied-bounds-unnorm-associated-type-2.stderr b/tests/ui/fn/implied-bounds-unnorm-associated-type-2.stderr index 85e3452fbf2ca..95fac3ce51d2a 100644 --- a/tests/ui/fn/implied-bounds-unnorm-associated-type-2.stderr +++ b/tests/ui/fn/implied-bounds-unnorm-associated-type-2.stderr @@ -9,9 +9,6 @@ LL | f::<'a, 'b>(()); | ^^^^^^^^^^^^^^^ generic argument requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` - = note: requirement occurs because of a function pointer to `f` - = note: the function `f` is invariant over the parameter `'a` - = help: see for more information about variance error: aborting due to 1 previous error diff --git a/tests/ui/traits/next-solver/generalize/subtype-obligations-bivariant-args.rs b/tests/ui/traits/next-solver/generalize/subtype-obligations-bivariant-args.rs new file mode 100644 index 0000000000000..9e25401f0f9ab --- /dev/null +++ b/tests/ui/traits/next-solver/generalize/subtype-obligations-bivariant-args.rs @@ -0,0 +1,52 @@ +//@ revisions: old next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass + +// Regression test for trait-system-refactor-initiative#250. +// Subtyping previously handled bivariant arguments by emitting +// a `WellFormed` obligation when generalizing them. +// +// This obligation then got dropped inside of an ambiguous `Subtype` +// obligation so we never constrained the bivariant arg. + +// Test case 1 +enum State +where + S: Iterator, +{ + Active { upstream: S }, + WindDown, + Complete, +} + +impl State +where + S: Iterator, +{ + fn foo(self) { + let x = match self { + State::Active { .. } => None, + State::WindDown => None, + State::Complete => Some(State::Complete), + }; + let _: Option> = x; + } +} + +// Test case 2 +trait Trait { + type Assoc; +} +impl Trait for T { + type Assoc = T; +} + +struct Foo, U>(T); + +fn main() { + let x = None.unwrap(); + let y = x; + let _: Foo<_, _> = x; + let _: Foo = x; +}