From 7a2e8f0c9bfe56185a432a421ae611628ddcdbc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 14 May 2025 16:15:57 +0200 Subject: [PATCH] Expand free alias types when computing outlives-components --- .../src/ty/context/impl_interner.rs | 4 ++ compiler/rustc_middle/src/ty/util.rs | 2 +- compiler/rustc_type_ir/src/interner.rs | 1 + compiler/rustc_type_ir/src/outlives.rs | 6 +++ .../implied-outlives-bounds-0.dump.stderr | 34 ++++++++++++++ .../implied-outlives-bounds-0.fail.stderr | 34 ++++++++++++++ .../implied-outlives-bounds-0.rs | 45 +++++++++++++++++++ .../implied-outlives-bounds-1.dump.stderr | 21 +++++++++ .../implied-outlives-bounds-1.rs | 35 +++++++++++++++ 9 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 tests/ui/checked-type-aliases/implied-outlives-bounds-0.dump.stderr create mode 100644 tests/ui/checked-type-aliases/implied-outlives-bounds-0.fail.stderr create mode 100644 tests/ui/checked-type-aliases/implied-outlives-bounds-0.rs create mode 100644 tests/ui/checked-type-aliases/implied-outlives-bounds-1.dump.stderr create mode 100644 tests/ui/checked-type-aliases/implied-outlives-bounds-1.rs diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index d8fb3ad998cfd..6260ac68e8669 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -180,6 +180,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { } } + fn expand_free_alias_tys>>(self, t: T) -> T { + self.expand_free_alias_tys(t) + } + fn expand_abstract_consts>>(self, t: T) -> T { self.expand_abstract_consts(t) } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 83589adb1691b..2b3904ff65521 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -913,7 +913,7 @@ impl<'tcx> TyCtxt<'tcx> { value.fold_with(&mut FreeAliasTypeExpander { tcx: self, depth: 0 }) } - /// Peel off all [free alias types] in this type until there are none left. + /// Peel off all [free alias types][free] in this type until there are none left. /// /// This only expands free alias types in “head” / outermost positions. It can /// be used over [expand_free_alias_tys] as an optimization in situations where diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 6780c66183aa1..2a24f8ec726c5 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -270,6 +270,7 @@ pub trait Interner: /// (in theory) only happen when concurrent. fn assert_evaluation_is_concurrent(&self); + fn expand_free_alias_tys>(self, t: T) -> T; fn expand_abstract_consts>(self, t: T) -> T; type GenericsOf: GenericsOf; diff --git a/compiler/rustc_type_ir/src/outlives.rs b/compiler/rustc_type_ir/src/outlives.rs index 15741c926092e..f9bc55d5caf84 100644 --- a/compiler/rustc_type_ir/src/outlives.rs +++ b/compiler/rustc_type_ir/src/outlives.rs @@ -61,6 +61,7 @@ pub fn push_outlives_components( ty: I::Ty, out: &mut SmallVec<[Component; 4]>, ) { + let ty = cx.expand_free_alias_tys(ty); ty.visit_with(&mut OutlivesCollector { cx, out, visited: Default::default() }); } @@ -144,6 +145,11 @@ impl TypeVisitor for OutlivesCollector<'_, I> { self.out.push(Component::Placeholder(p)); } + // All free alias types should've been expanded beforehand. + ty::Alias(_, ty::AliasTy { kind: ty::Free { .. }, .. }) => { + panic!("unexpected free alias type") + } + // For projections, we prefer to generate an obligation like // `>::Foo: 'a`, because this gives the // regionck more ways to prove that it holds. However, diff --git a/tests/ui/checked-type-aliases/implied-outlives-bounds-0.dump.stderr b/tests/ui/checked-type-aliases/implied-outlives-bounds-0.dump.stderr new file mode 100644 index 0000000000000..0e155a906df88 --- /dev/null +++ b/tests/ui/checked-type-aliases/implied-outlives-bounds-0.dump.stderr @@ -0,0 +1,34 @@ +error: rustc_dump_inferred_outlives + --> $DIR/implied-outlives-bounds-0.rs:10:1 + | +LL | type TypeOutlives<'a, T> = &'a T; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: T: 'a + +error: rustc_dump_inferred_outlives + --> $DIR/implied-outlives-bounds-0.rs:15:1 + | +LL | type RegionOutlives<'a, 'b> = &'a &'b (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: 'b: 'a + +error: rustc_dump_inferred_outlives + --> $DIR/implied-outlives-bounds-0.rs:21:1 + | +LL | struct Outer0<'a, T>(ExplicitTypeOutlives<'a, T>); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: T: 'a + +error: rustc_dump_inferred_outlives + --> $DIR/implied-outlives-bounds-0.rs:29:1 + | +LL | type Outer1<'b, U> = TypeOutlives<'b, U>; + | ^^^^^^^^^^^^^^^^^^ + | + = note: U: 'b + +error: aborting due to 4 previous errors + diff --git a/tests/ui/checked-type-aliases/implied-outlives-bounds-0.fail.stderr b/tests/ui/checked-type-aliases/implied-outlives-bounds-0.fail.stderr new file mode 100644 index 0000000000000..12e35c516fdb4 --- /dev/null +++ b/tests/ui/checked-type-aliases/implied-outlives-bounds-0.fail.stderr @@ -0,0 +1,34 @@ +error: lifetime may not live long enough + --> $DIR/implied-outlives-bounds-0.rs:33:39 + | +LL | #[cfg(fail)] fn env0<'any>() { let _: TypeOutlives<'static, &'any ()>; } + | ---- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'any` must outlive `'static` + | | + | lifetime `'any` defined here + +error: lifetime may not live long enough + --> $DIR/implied-outlives-bounds-0.rs:36:39 + | +LL | #[cfg(fail)] fn env1<'any>() { let _: RegionOutlives<'static, 'any>; } + | ---- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'any` must outlive `'static` + | | + | lifetime `'any` defined here + +error: lifetime may not live long enough + --> $DIR/implied-outlives-bounds-0.rs:39:39 + | +LL | #[cfg(fail)] fn env2<'any>() { let _: Outer0<'static, &'any ()>; } + | ---- ^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'any` must outlive `'static` + | | + | lifetime `'any` defined here + +error: lifetime may not live long enough + --> $DIR/implied-outlives-bounds-0.rs:42:39 + | +LL | #[cfg(fail)] fn env3<'any>() { let _: Outer1<'static, &'any ()>; } + | ---- ^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'any` must outlive `'static` + | | + | lifetime `'any` defined here + +error: aborting due to 4 previous errors + diff --git a/tests/ui/checked-type-aliases/implied-outlives-bounds-0.rs b/tests/ui/checked-type-aliases/implied-outlives-bounds-0.rs new file mode 100644 index 0000000000000..8c8834f747cc0 --- /dev/null +++ b/tests/ui/checked-type-aliases/implied-outlives-bounds-0.rs @@ -0,0 +1,45 @@ +// Check that we imply outlives-bounds on checked type aliases. + +//@ revisions: pass fail dump +//@[pass] check-pass + +#![feature(checked_type_aliases)] +#![cfg_attr(dump, feature(rustc_attrs))] + +#[cfg_attr(dump, rustc_dump_inferred_outlives)] +type TypeOutlives<'a, T> = &'a T; +//[dump]~^ ERROR rustc_dump_inferred_outlives +//[dump]~| NOTE T: 'a + +#[cfg_attr(dump, rustc_dump_inferred_outlives)] +type RegionOutlives<'a, 'b> = &'a &'b (); +//[dump]~^ ERROR rustc_dump_inferred_outlives +//[dump]~| NOTE 'b: 'a + +// Ensure that we imply bounds from the explicit bounds of free alias types. +#[cfg_attr(dump, rustc_dump_inferred_outlives)] +struct Outer0<'a, T>(ExplicitTypeOutlives<'a, T>); +//[dump]~^ ERROR rustc_dump_inferred_outlives +//[dump]~| NOTE T: 'a + +type ExplicitTypeOutlives<'a, T: 'a> = (&'a (), T); + +// Ensure that we imply bounds from the implied bounds of free alias types. +#[cfg_attr(dump, rustc_dump_inferred_outlives)] +type Outer1<'b, U> = TypeOutlives<'b, U>; +//[dump]~^ ERROR rustc_dump_inferred_outlives +//[dump]~| NOTE U: 'b + +#[cfg(fail)] fn env0<'any>() { let _: TypeOutlives<'static, &'any ()>; } +//[fail]~^ ERROR lifetime may not live long enough + +#[cfg(fail)] fn env1<'any>() { let _: RegionOutlives<'static, 'any>; } +//[fail]~^ ERROR lifetime may not live long enough + +#[cfg(fail)] fn env2<'any>() { let _: Outer0<'static, &'any ()>; } +//[fail]~^ ERROR lifetime may not live long enough + +#[cfg(fail)] fn env3<'any>() { let _: Outer1<'static, &'any ()>; } +//[fail]~^ ERROR lifetime may not live long enough + +fn main() {} diff --git a/tests/ui/checked-type-aliases/implied-outlives-bounds-1.dump.stderr b/tests/ui/checked-type-aliases/implied-outlives-bounds-1.dump.stderr new file mode 100644 index 0000000000000..f19ded169aec7 --- /dev/null +++ b/tests/ui/checked-type-aliases/implied-outlives-bounds-1.dump.stderr @@ -0,0 +1,21 @@ +error: rustc_dump_inferred_outlives + --> $DIR/implied-outlives-bounds-1.rs:14:1 + | +LL | struct Type0<'a, K, V>(&'a mut Alias0); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: K: 'a + = note: V: 'a + +error: rustc_dump_inferred_outlives + --> $DIR/implied-outlives-bounds-1.rs:27:1 + | +LL | struct Type1<'a, 'b, T>(&'a Alias1<'b, T>); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: 'b: 'a + = note: T: 'a + = note: T: 'b + +error: aborting due to 2 previous errors + diff --git a/tests/ui/checked-type-aliases/implied-outlives-bounds-1.rs b/tests/ui/checked-type-aliases/implied-outlives-bounds-1.rs new file mode 100644 index 0000000000000..2f32a6fa2ad07 --- /dev/null +++ b/tests/ui/checked-type-aliases/implied-outlives-bounds-1.rs @@ -0,0 +1,35 @@ +// Check that we expand free alias types when computing outlives-components when +// inferring outlives-bounds. + +//@ revisions: pass dump +//@[pass] check-pass +#![feature(checked_type_aliases)] +#![cfg_attr(dump, feature(rustc_attrs))] + +// When encountering type `&'a mut Alias` we want to infer outlives-bounds of the form `C: 'a` +// for all outlives components `C` of `Alias`. Contrary to other alias types, we look behind +// the free alias type when computing the components which then turn out to be `K` & `V` leading us +// to infer `K: 'a` and `V: 'a`. +#[cfg_attr(dump, rustc_dump_inferred_outlives)] +struct Type0<'a, K, V>(&'a mut Alias0); +//[dump]~^ ERROR rustc_dump_inferred_outlives +//[dump]~| NOTE K: 'a +//[dump]~| NOTE V: 'a + +type Alias0 = (K, V); + +// Of course, that expansion doesn't mean that we don't consider explicit bounds on the corresp. +// type alias because we shallow walk the entire type. So below, (1) we infer `'b: 'a` & `T: 'a` +// from the `&'a ...` and the fact that the outlives-components in `Alias1<'b, T>` are `'b` & `T`. +// (2) Separately, we walk the `Alias1<'b, T>` and look at the explicit & inferred outlives-bounds +// implied by CTA `Alias1` which gives us `T: 'b`. +#[cfg_attr(dump, rustc_dump_inferred_outlives)] +struct Type1<'a, 'b, T>(&'a Alias1<'b, T>); +//[dump]~^ ERROR rustc_dump_inferred_outlives +//[dump]~| NOTE 'b: 'a +//[dump]~| NOTE T: 'a +//[dump]~| NOTE T: 'b + +type Alias1<'c, U: 'c> = (&'c (), U); + +fn main() {}