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:
commit
b4f1098e10
16 changed files with 343 additions and 166 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)) => {
|
||||
|
|
|
|||
|
|
@ -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)) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue