Rollup merge of #136554 - compiler-errors:opt-alias-variances, r=lcnr
Add `opt_alias_variances` and use it in outlives code ...so to fix some subtle outlives bugs with precise capturing in traits, and eventually make it easier to compute variances for "forced unconstrained" trait lifetimes. r? lcnr
This commit is contained in:
commit
7ca0fd18f6
12 changed files with 117 additions and 50 deletions
|
@ -284,7 +284,7 @@ where
|
|||
return;
|
||||
}
|
||||
|
||||
match ty.kind() {
|
||||
match *ty.kind() {
|
||||
ty::Closure(_, args) => {
|
||||
// Skip lifetime parameters of the enclosing item(s)
|
||||
|
||||
|
@ -316,10 +316,12 @@ where
|
|||
args.as_coroutine().resume_ty().visit_with(self);
|
||||
}
|
||||
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
|
||||
// Skip lifetime parameters that are not captures.
|
||||
let variances = self.tcx.variances_of(*def_id);
|
||||
|
||||
ty::Alias(kind, ty::AliasTy { def_id, args, .. })
|
||||
if let Some(variances) = self.tcx.opt_alias_variances(kind, 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()) {
|
||||
if *v != ty::Bivariant {
|
||||
s.visit_with(self);
|
||||
|
|
|
@ -48,7 +48,7 @@ where
|
|||
return ty.super_visit_with(self);
|
||||
}
|
||||
|
||||
match ty.kind() {
|
||||
match *ty.kind() {
|
||||
// We can prove that an alias is live two ways:
|
||||
// 1. All the components are live.
|
||||
//
|
||||
|
@ -95,11 +95,9 @@ where
|
|||
assert!(r.type_flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS));
|
||||
r.visit_with(self);
|
||||
} else {
|
||||
// Skip lifetime parameters that are not captures.
|
||||
let variances = match kind {
|
||||
ty::Opaque => Some(self.tcx.variances_of(*def_id)),
|
||||
_ => None,
|
||||
};
|
||||
// Skip lifetime parameters that are not captured, since they do
|
||||
// not need to be live.
|
||||
let variances = tcx.opt_alias_variances(kind, def_id);
|
||||
|
||||
for (idx, s) in args.iter().enumerate() {
|
||||
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
|
||||
// inference variables, we use a verify constraint instead of adding
|
||||
// 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()
|
||||
&& trait_bounds.is_empty()
|
||||
&& (alias_ty.has_infer_regions() || is_opaque)
|
||||
&& (alias_ty.has_infer_regions() || kind == ty::Opaque)
|
||||
{
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -102,12 +102,11 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
|
|||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
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`.
|
||||
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()
|
||||
&& 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
|
||||
// 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
|
||||
let recursive_bound = {
|
||||
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)
|
||||
};
|
||||
|
||||
|
|
|
@ -194,6 +194,14 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
|
|||
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>> {
|
||||
self.type_of(def_id)
|
||||
}
|
||||
|
|
|
@ -948,6 +948,29 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
|
||||
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> {
|
||||
|
|
|
@ -141,6 +141,12 @@ pub trait Interner:
|
|||
type VariancesOf: Copy + Debug + SliceLike<Item = ty::Variance>;
|
||||
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>;
|
||||
|
||||
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,
|
||||
// we simply fallback to the most restrictive rule, which
|
||||
// requires that `Pi: 'a` for all `i`.
|
||||
ty::Alias(_, alias_ty) => {
|
||||
ty::Alias(kind, alias_ty) => {
|
||||
if !alias_ty.has_escaping_bound_vars() {
|
||||
// best case: no escaping regions, so push the
|
||||
// projection and skip the subtree (thus generating no
|
||||
|
@ -162,7 +162,7 @@ impl<I: Interner> TypeVisitor<I> for OutlivesCollector<'_, I> {
|
|||
// OutlivesProjectionComponents. Continue walking
|
||||
// through and constrain Pi.
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
pub fn compute_alias_components_recursive<I: Interner>(
|
||||
cx: I,
|
||||
alias_ty: I::Ty,
|
||||
kind: ty::AliasTyKind,
|
||||
alias_ty: ty::AliasTy<I>,
|
||||
out: &mut SmallVec<[Component<I>; 4]>,
|
||||
) {
|
||||
let ty::Alias(kind, alias_ty) = alias_ty.kind() else {
|
||||
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 opt_variances = cx.opt_alias_variances(kind, alias_ty.def_id);
|
||||
|
||||
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.
|
||||
///
|
||||
/// * 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)
|
||||
}))
|
||||
} else {
|
||||
let args = match a.kind(relation.cx()) {
|
||||
ty::Opaque => 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::Projection if relation.cx().is_impl_trait_in_trait(a.def_id) => {
|
||||
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,
|
||||
relation.cx().variances_of(a.def_id),
|
||||
a.args,
|
||||
b.args,
|
||||
relation, a.def_id, variances, 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 => {
|
||||
} else {
|
||||
relate_args_invariantly(relation, a.args, b.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