Add opt_alias_variances and use it in outlives code
This commit is contained in:
parent
2f92f050e8
commit
d17a4a7f9a
12 changed files with 117 additions and 50 deletions
|
@ -284,7 +284,7 @@ where
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
match ty.kind() {
|
match *ty.kind() {
|
||||||
ty::Closure(_, args) => {
|
ty::Closure(_, args) => {
|
||||||
// Skip lifetime parameters of the enclosing item(s)
|
// Skip lifetime parameters of the enclosing item(s)
|
||||||
|
|
||||||
|
@ -316,10 +316,12 @@ where
|
||||||
args.as_coroutine().resume_ty().visit_with(self);
|
args.as_coroutine().resume_ty().visit_with(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
|
ty::Alias(kind, ty::AliasTy { def_id, args, .. })
|
||||||
// Skip lifetime parameters that are not captures.
|
if let Some(variances) = self.tcx.opt_alias_variances(kind, def_id) =>
|
||||||
let variances = self.tcx.variances_of(*def_id);
|
{
|
||||||
|
// Skip lifetime parameters that are not captured, since they do
|
||||||
|
// not need member constraints registered for them; we'll erase
|
||||||
|
// them (and hopefully in the future replace them with placeholders).
|
||||||
for (v, s) in std::iter::zip(variances, args.iter()) {
|
for (v, s) in std::iter::zip(variances, args.iter()) {
|
||||||
if *v != ty::Bivariant {
|
if *v != ty::Bivariant {
|
||||||
s.visit_with(self);
|
s.visit_with(self);
|
||||||
|
|
|
@ -48,7 +48,7 @@ where
|
||||||
return ty.super_visit_with(self);
|
return ty.super_visit_with(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
match ty.kind() {
|
match *ty.kind() {
|
||||||
// We can prove that an alias is live two ways:
|
// We can prove that an alias is live two ways:
|
||||||
// 1. All the components are live.
|
// 1. All the components are live.
|
||||||
//
|
//
|
||||||
|
@ -95,11 +95,9 @@ where
|
||||||
assert!(r.type_flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS));
|
assert!(r.type_flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS));
|
||||||
r.visit_with(self);
|
r.visit_with(self);
|
||||||
} else {
|
} else {
|
||||||
// Skip lifetime parameters that are not captures.
|
// Skip lifetime parameters that are not captured, since they do
|
||||||
let variances = match kind {
|
// not need to be live.
|
||||||
ty::Opaque => Some(self.tcx.variances_of(*def_id)),
|
let variances = tcx.opt_alias_variances(kind, def_id);
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
for (idx, s) in args.iter().enumerate() {
|
for (idx, s) in args.iter().enumerate() {
|
||||||
if variances.map(|variances| variances[idx]) != Some(ty::Bivariant) {
|
if variances.map(|variances| variances[idx]) != Some(ty::Bivariant) {
|
||||||
|
|
|
@ -392,13 +392,13 @@ where
|
||||||
// the problem is to add `T: 'r`, which isn't true. So, if there are no
|
// the problem is to add `T: 'r`, which isn't true. So, if there are no
|
||||||
// inference variables, we use a verify constraint instead of adding
|
// inference variables, we use a verify constraint instead of adding
|
||||||
// edges, which winds up enforcing the same condition.
|
// edges, which winds up enforcing the same condition.
|
||||||
let is_opaque = alias_ty.kind(self.tcx) == ty::Opaque;
|
let kind = alias_ty.kind(self.tcx);
|
||||||
if approx_env_bounds.is_empty()
|
if approx_env_bounds.is_empty()
|
||||||
&& trait_bounds.is_empty()
|
&& trait_bounds.is_empty()
|
||||||
&& (alias_ty.has_infer_regions() || is_opaque)
|
&& (alias_ty.has_infer_regions() || kind == ty::Opaque)
|
||||||
{
|
{
|
||||||
debug!("no declared bounds");
|
debug!("no declared bounds");
|
||||||
let opt_variances = is_opaque.then(|| self.tcx.variances_of(alias_ty.def_id));
|
let opt_variances = self.tcx.opt_alias_variances(kind, alias_ty.def_id);
|
||||||
self.args_must_outlive(alias_ty.args, origin, region, opt_variances);
|
self.args_must_outlive(alias_ty.args, origin, region, opt_variances);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,12 +102,11 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
|
||||||
|
|
||||||
#[instrument(level = "debug", skip(self))]
|
#[instrument(level = "debug", skip(self))]
|
||||||
pub(crate) fn alias_bound(&self, alias_ty: ty::AliasTy<'tcx>) -> VerifyBound<'tcx> {
|
pub(crate) fn alias_bound(&self, alias_ty: ty::AliasTy<'tcx>) -> VerifyBound<'tcx> {
|
||||||
let alias_ty_as_ty = alias_ty.to_ty(self.tcx);
|
|
||||||
|
|
||||||
// Search the env for where clauses like `P: 'a`.
|
// Search the env for where clauses like `P: 'a`.
|
||||||
let env_bounds = self.approx_declared_bounds_from_env(alias_ty).into_iter().map(|binder| {
|
let env_bounds = self.approx_declared_bounds_from_env(alias_ty).into_iter().map(|binder| {
|
||||||
if let Some(ty::OutlivesPredicate(ty, r)) = binder.no_bound_vars()
|
if let Some(ty::OutlivesPredicate(ty, r)) = binder.no_bound_vars()
|
||||||
&& ty == alias_ty_as_ty
|
&& let ty::Alias(_, alias_ty_from_bound) = *ty.kind()
|
||||||
|
&& alias_ty_from_bound == alias_ty
|
||||||
{
|
{
|
||||||
// Micro-optimize if this is an exact match (this
|
// Micro-optimize if this is an exact match (this
|
||||||
// occurs often when there are no region variables
|
// occurs often when there are no region variables
|
||||||
|
@ -127,7 +126,8 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
|
||||||
// see the extensive comment in projection_must_outlive
|
// see the extensive comment in projection_must_outlive
|
||||||
let recursive_bound = {
|
let recursive_bound = {
|
||||||
let mut components = smallvec![];
|
let mut components = smallvec![];
|
||||||
compute_alias_components_recursive(self.tcx, alias_ty_as_ty, &mut components);
|
let kind = alias_ty.kind(self.tcx);
|
||||||
|
compute_alias_components_recursive(self.tcx, kind, alias_ty, &mut components);
|
||||||
self.bound_from_components(&components)
|
self.bound_from_components(&components)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -194,6 +194,14 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
|
||||||
self.variances_of(def_id)
|
self.variances_of(def_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn opt_alias_variances(
|
||||||
|
self,
|
||||||
|
kind: impl Into<ty::AliasTermKind>,
|
||||||
|
def_id: DefId,
|
||||||
|
) -> Option<&'tcx [ty::Variance]> {
|
||||||
|
self.opt_alias_variances(kind, def_id)
|
||||||
|
}
|
||||||
|
|
||||||
fn type_of(self, def_id: DefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> {
|
fn type_of(self, def_id: DefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> {
|
||||||
self.type_of(def_id)
|
self.type_of(def_id)
|
||||||
}
|
}
|
||||||
|
|
|
@ -951,6 +951,29 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||||
|
|
||||||
ty
|
ty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Computes the variances for an alias (opaque or RPITIT) that represent
|
||||||
|
// its (un)captured regions.
|
||||||
|
pub fn opt_alias_variances(
|
||||||
|
self,
|
||||||
|
kind: impl Into<ty::AliasTermKind>,
|
||||||
|
def_id: DefId,
|
||||||
|
) -> Option<&'tcx [ty::Variance]> {
|
||||||
|
match kind.into() {
|
||||||
|
ty::AliasTermKind::ProjectionTy => {
|
||||||
|
if self.is_impl_trait_in_trait(def_id) {
|
||||||
|
Some(self.variances_of(def_id))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ty::AliasTermKind::OpaqueTy => Some(self.variances_of(def_id)),
|
||||||
|
ty::AliasTermKind::InherentTy
|
||||||
|
| ty::AliasTermKind::WeakTy
|
||||||
|
| ty::AliasTermKind::UnevaluatedConst
|
||||||
|
| ty::AliasTermKind::ProjectionConst => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct OpaqueTypeExpander<'tcx> {
|
struct OpaqueTypeExpander<'tcx> {
|
||||||
|
|
|
@ -141,6 +141,12 @@ pub trait Interner:
|
||||||
type VariancesOf: Copy + Debug + SliceLike<Item = ty::Variance>;
|
type VariancesOf: Copy + Debug + SliceLike<Item = ty::Variance>;
|
||||||
fn variances_of(self, def_id: Self::DefId) -> Self::VariancesOf;
|
fn variances_of(self, def_id: Self::DefId) -> Self::VariancesOf;
|
||||||
|
|
||||||
|
fn opt_alias_variances(
|
||||||
|
self,
|
||||||
|
kind: impl Into<ty::AliasTermKind>,
|
||||||
|
def_id: Self::DefId,
|
||||||
|
) -> Option<Self::VariancesOf>;
|
||||||
|
|
||||||
fn type_of(self, def_id: Self::DefId) -> ty::EarlyBinder<Self, Self::Ty>;
|
fn type_of(self, def_id: Self::DefId) -> ty::EarlyBinder<Self, Self::Ty>;
|
||||||
|
|
||||||
type AdtDef: AdtDef<Self>;
|
type AdtDef: AdtDef<Self>;
|
||||||
|
|
|
@ -148,7 +148,7 @@ impl<I: Interner> TypeVisitor<I> for OutlivesCollector<'_, I> {
|
||||||
// trait-ref. Therefore, if we see any higher-ranked regions,
|
// trait-ref. Therefore, if we see any higher-ranked regions,
|
||||||
// we simply fallback to the most restrictive rule, which
|
// we simply fallback to the most restrictive rule, which
|
||||||
// requires that `Pi: 'a` for all `i`.
|
// requires that `Pi: 'a` for all `i`.
|
||||||
ty::Alias(_, alias_ty) => {
|
ty::Alias(kind, alias_ty) => {
|
||||||
if !alias_ty.has_escaping_bound_vars() {
|
if !alias_ty.has_escaping_bound_vars() {
|
||||||
// best case: no escaping regions, so push the
|
// best case: no escaping regions, so push the
|
||||||
// projection and skip the subtree (thus generating no
|
// projection and skip the subtree (thus generating no
|
||||||
|
@ -162,7 +162,7 @@ impl<I: Interner> TypeVisitor<I> for OutlivesCollector<'_, I> {
|
||||||
// OutlivesProjectionComponents. Continue walking
|
// OutlivesProjectionComponents. Continue walking
|
||||||
// through and constrain Pi.
|
// through and constrain Pi.
|
||||||
let mut subcomponents = smallvec![];
|
let mut subcomponents = smallvec![];
|
||||||
compute_alias_components_recursive(self.cx, ty, &mut subcomponents);
|
compute_alias_components_recursive(self.cx, kind, alias_ty, &mut subcomponents);
|
||||||
self.out.push(Component::EscapingAlias(subcomponents.into_iter().collect()));
|
self.out.push(Component::EscapingAlias(subcomponents.into_iter().collect()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -217,21 +217,17 @@ impl<I: Interner> TypeVisitor<I> for OutlivesCollector<'_, I> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Collect [Component]s for *all* the args of `parent`.
|
/// Collect [Component]s for *all* the args of `alias_ty`.
|
||||||
///
|
///
|
||||||
/// This should not be used to get the components of `parent` itself.
|
/// This should not be used to get the components of `alias_ty` itself.
|
||||||
/// Use [push_outlives_components] instead.
|
/// Use [push_outlives_components] instead.
|
||||||
pub fn compute_alias_components_recursive<I: Interner>(
|
pub fn compute_alias_components_recursive<I: Interner>(
|
||||||
cx: I,
|
cx: I,
|
||||||
alias_ty: I::Ty,
|
kind: ty::AliasTyKind,
|
||||||
|
alias_ty: ty::AliasTy<I>,
|
||||||
out: &mut SmallVec<[Component<I>; 4]>,
|
out: &mut SmallVec<[Component<I>; 4]>,
|
||||||
) {
|
) {
|
||||||
let ty::Alias(kind, alias_ty) = alias_ty.kind() else {
|
let opt_variances = cx.opt_alias_variances(kind, alias_ty.def_id);
|
||||||
unreachable!("can only call `compute_alias_components_recursive` on an alias type")
|
|
||||||
};
|
|
||||||
|
|
||||||
let opt_variances =
|
|
||||||
if kind == ty::Opaque { Some(cx.variances_of(alias_ty.def_id)) } else { None };
|
|
||||||
|
|
||||||
let mut visitor = OutlivesCollector { cx, out, visited: Default::default() };
|
let mut visitor = OutlivesCollector { cx, out, visited: Default::default() };
|
||||||
|
|
||||||
|
|
|
@ -469,6 +469,17 @@ impl AliasTermKind {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<ty::AliasTyKind> for AliasTermKind {
|
||||||
|
fn from(value: ty::AliasTyKind) -> Self {
|
||||||
|
match value {
|
||||||
|
ty::Projection => AliasTermKind::ProjectionTy,
|
||||||
|
ty::Opaque => AliasTermKind::OpaqueTy,
|
||||||
|
ty::Weak => AliasTermKind::WeakTy,
|
||||||
|
ty::Inherent => AliasTermKind::InherentTy,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents the unprojected term of a projection goal.
|
/// Represents the unprojected term of a projection goal.
|
||||||
///
|
///
|
||||||
/// * For a projection, this would be `<Ty as Trait<...>>::N<...>`.
|
/// * For a projection, this would be `<Ty as Trait<...>>::N<...>`.
|
||||||
|
|
|
@ -236,28 +236,14 @@ impl<I: Interner> Relate<I> for ty::AliasTy<I> {
|
||||||
ExpectedFound::new(a, b)
|
ExpectedFound::new(a, b)
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
let args = match a.kind(relation.cx()) {
|
let cx = relation.cx();
|
||||||
ty::Opaque => relate_args_with_variances(
|
let args = if let Some(variances) = cx.opt_alias_variances(a.kind(cx), a.def_id) {
|
||||||
relation,
|
relate_args_with_variances(
|
||||||
a.def_id,
|
relation, a.def_id, variances, a.args, b.args,
|
||||||
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
|
false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle
|
||||||
)?,
|
)?
|
||||||
ty::Projection if relation.cx().is_impl_trait_in_trait(a.def_id) => {
|
} else {
|
||||||
relate_args_with_variances(
|
relate_args_invariantly(relation, a.args, b.args)?
|
||||||
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::Projection | ty::Weak | ty::Inherent => {
|
|
||||||
relate_args_invariantly(relation, a.args, b.args)?
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
Ok(ty::AliasTy::new_from_args(relation.cx(), a.def_id, args))
|
Ok(ty::AliasTy::new_from_args(relation.cx(), a.def_id, args))
|
||||||
}
|
}
|
||||||
|
|
19
tests/ui/impl-trait/precise-capturing/rpitit-outlives-2.rs
Normal file
19
tests/ui/impl-trait/precise-capturing/rpitit-outlives-2.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
//@ check-pass
|
||||||
|
|
||||||
|
// Ensure that we skip uncaptured args from RPITITs when comptuing outlives.
|
||||||
|
|
||||||
|
#![feature(precise_capturing_in_traits)]
|
||||||
|
|
||||||
|
struct Invariant<T>(*mut T);
|
||||||
|
|
||||||
|
trait Foo {
|
||||||
|
fn hello<'s: 's>(&'s self) -> Invariant<impl Sized + use<Self>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn outlives_static(_: impl Sized + 'static) {}
|
||||||
|
|
||||||
|
fn hello<'s, T: Foo + 'static>(x: &'s T) {
|
||||||
|
outlives_static(x.hello());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
18
tests/ui/impl-trait/precise-capturing/rpitit-outlives.rs
Normal file
18
tests/ui/impl-trait/precise-capturing/rpitit-outlives.rs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
//@ check-pass
|
||||||
|
|
||||||
|
// Ensure that we skip uncaptured args from RPITITs when collecting the regions
|
||||||
|
// to enforce member constraints in opaque type inference.
|
||||||
|
|
||||||
|
#![feature(precise_capturing_in_traits)]
|
||||||
|
|
||||||
|
struct Invariant<T>(*mut T);
|
||||||
|
|
||||||
|
trait Foo {
|
||||||
|
fn hello<'s: 's>(&'s self) -> Invariant<impl Sized + use<Self>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hello<'s, T: Foo>(x: &'s T) -> Invariant<impl Sized> {
|
||||||
|
x.hello()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
Loading…
Add table
Add a link
Reference in a new issue