Auto merge of #148823 - lcnr:generalize-no-subtyping, r=BoxyUwU

TypeRelating emit WellFormed, not generalize

fixes https://github.com/rust-lang/trait-system-refactor-initiative/issues/250

r? BoxyUwU
This commit is contained in:
bors 2025-12-06 02:53:23 +00:00
commit b4f1098e10
16 changed files with 343 additions and 166 deletions

View file

@ -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<TyCtxt<'tcx>> 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<T: Relate<TyCtxt<'tcx>>>(
&mut self,
variance: ty::Variance,

View file

@ -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<TyCtxt<'tcx>> 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<T: Relate<TyCtxt<'tcx>>>(
&mut self,
@ -328,7 +359,7 @@ impl<'b, 'tcx> TypeRelation<TyCtxt<'tcx>> 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 {

View file

@ -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<TyCtxt<'tcx>> 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<T: Relate<TyCtxt<'tcx>>>(
&mut self,
@ -145,6 +161,10 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> 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) }

View file

@ -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<TyCtxt<'tcx>> 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<TyCtxt<'tcx>> 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<T> {
/// 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<A, B> where A: Iterator<Item = B> {
/// 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<?A, ?B>`, then after generalization we will wind
/// up with a type like `Foo<?C, ?D>`. When we enforce `Foo<?A, ?B> <: Foo<?C, ?D>`,
/// 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,
}

View file

@ -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<TyCtxt<'tcx>> 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<T: Relate<TyCtxt<'tcx>>>(
&mut self,
variance: ty::Variance,

View file

@ -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<TyCtxt<'tcx>> 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)
}
}

View file

@ -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<TyCtxt<'tcx>> 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<T: Relate<TyCtxt<'tcx>>>(
&mut self,
variance: ty::Variance,

View file

@ -2069,6 +2069,19 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> 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<T: relate::Relate<TyCtxt<'tcx>>>(
&mut self,
_variance: ty::Variance,

View file

@ -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<TyCtxt<'tcx>> 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<T: Relate<TyCtxt<'tcx>>>(
&mut self,
_: ty::Variance,

View file

@ -73,20 +73,15 @@ pub trait TypeRelation<I: Interner>: 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<I, I::GenericArgs> {
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<I, I::Ty>;
/// Switch variance for the purpose of relating `a` and `b`.
fn relate_with_variance<T: Relate<I>>(
@ -138,27 +133,17 @@ pub fn relate_args_invariantly<I: Interner, R: TypeRelation<I>>(
pub fn relate_args_with_variances<I: Interner, R: TypeRelation<I>>(
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<I, I::GenericArgs> {
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<I: Interner> Relate<I> for ty::FnSig<I> {
@ -240,10 +225,7 @@ impl<I: Interner> Relate<I> for ty::AliasTy<I> {
} 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<I: Interner> Relate<I> for ty::AliasTerm<I> {
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<I: Interner, R: TypeRelation<I>>(
(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<I: Interner, R: TypeRelation<I>>(
}
(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)) => {

View file

@ -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, I, R>(
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<I, I::Ty>
where
Infcx: InferCtxtLike<Interner = I>,
I: Interner,
R: PredicateEmittingRelation<Infcx>,
{
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<A, B> where A: Iterator<Item = B> {
// 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<?A, ?B>`, then after generalization we will wind
// up with a type like `Foo<?C, ?D>`. When we enforce `Foo<?A, ?B> <: Foo<?C, ?D>`,
// 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)) }
}

View file

@ -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<I, I::GenericArgs> {
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<I, I::Ty> {
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<T: Relate<I>>(
&mut self,
variance: ty::Variance,

View file

@ -6,10 +6,6 @@ LL | fn m<'a>() {
LL | let unsend: *const dyn Cat<'a> = &();
LL | let _send = unsend as *const S<dyn Cat<'static>>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static`
|
= note: requirement occurs because of the type `S<dyn Cat<'_>>`, which makes the generic argument `dyn Cat<'_>` invariant
= note: the struct `S<T>` is invariant over the parameter `T`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error: aborting due to 1 previous error

View file

@ -6,10 +6,6 @@ LL | fn m<'a>() {
LL | let unsend: *const dyn Cat<'a> = &();
LL | let _send = unsend as *const S<dyn Cat<'static>>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static`
|
= note: requirement occurs because of the type `S<dyn Cat<'_>>`, which makes the generic argument `dyn Cat<'_>` invariant
= note: the struct `S<T>` is invariant over the parameter `T`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error: aborting due to 1 previous error

View file

@ -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 <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error: aborting due to 1 previous error

View file

@ -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<S, T>
where
S: Iterator<Item = T>,
{
Active { upstream: S },
WindDown,
Complete,
}
impl<S, T> State<S, T>
where
S: Iterator<Item = T>,
{
fn foo(self) {
let x = match self {
State::Active { .. } => None,
State::WindDown => None,
State::Complete => Some(State::Complete),
};
let _: Option<State<S, T>> = x;
}
}
// Test case 2
trait Trait {
type Assoc;
}
impl<T> Trait for T {
type Assoc = T;
}
struct Foo<T: Trait<Assoc = U>, U>(T);
fn main() {
let x = None.unwrap();
let y = x;
let _: Foo<_, _> = x;
let _: Foo<u32, u32> = x;
}