Skip to content

Add opt_alias_variances and use it in outlives code #136554

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions compiler/rustc_borrowck/src/type_check/opaque_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ where
return;
}

match ty.kind() {
match *ty.kind() {
ty::Closure(_, args) => {
// Skip lifetime parameters of the enclosing item(s)

Expand Down Expand Up @@ -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);
Expand Down
10 changes: 4 additions & 6 deletions compiler/rustc_infer/src/infer/outlives/for_liveness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
//
Expand Down Expand Up @@ -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) {
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_infer/src/infer/outlives/obligations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_infer/src/infer/outlives/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
};

Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
23 changes: 23 additions & 0 deletions compiler/rustc_middle/src/ty/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -951,6 +951,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> {
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_type_ir/src/interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand Down
18 changes: 7 additions & 11 deletions compiler/rustc_type_ir/src/outlives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()));
}
}
Expand Down Expand Up @@ -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() };

Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_type_ir/src/predicate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<...>`.
Expand Down
28 changes: 7 additions & 21 deletions compiler/rustc_type_ir/src/relate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
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
)?,
ty::Projection if relation.cx().is_impl_trait_in_trait(a.def_id) => {
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 | ty::Weak | ty::Inherent => {
relate_args_invariantly(relation, a.args, b.args)?
}
)?
} else {
relate_args_invariantly(relation, a.args, b.args)?
};
Ok(ty::AliasTy::new_from_args(relation.cx(), a.def_id, args))
}
Expand Down
19 changes: 19 additions & 0 deletions tests/ui/impl-trait/precise-capturing/rpitit-outlives-2.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 18 additions & 0 deletions tests/ui/impl-trait/precise-capturing/rpitit-outlives.rs
Original file line number Diff line number Diff line change
@@ -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