Skip to content
Open
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
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/ty/context/impl_interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
}
}

fn expand_free_alias_tys<T: TypeFoldable<TyCtxt<'tcx>>>(self, t: T) -> T {
self.expand_free_alias_tys(t)
}

fn expand_abstract_consts<T: TypeFoldable<TyCtxt<'tcx>>>(self, t: T) -> T {
self.expand_abstract_consts(t)
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/ty/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_type_ir/src/interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ pub trait Interner:
/// (in theory) only happen when concurrent.
fn assert_evaluation_is_concurrent(&self);

fn expand_free_alias_tys<T: TypeFoldable<Self>>(self, t: T) -> T;
fn expand_abstract_consts<T: TypeFoldable<Self>>(self, t: T) -> T;

type GenericsOf: GenericsOf<Self>;
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_type_ir/src/outlives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ pub fn push_outlives_components<I: Interner>(
ty: I::Ty,
out: &mut SmallVec<[Component<I>; 4]>,
) {
let ty = cx.expand_free_alias_tys(ty);
ty.visit_with(&mut OutlivesCollector { cx, out, visited: Default::default() });
}

Expand Down Expand Up @@ -144,6 +145,11 @@ impl<I: Interner> TypeVisitor<I> 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
// `<P0 as Trait<P1...Pn>>::Foo: 'a`, because this gives the
// regionck more ways to prove that it holds. However,
Expand Down
Original file line number Diff line number Diff line change
@@ -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

Original file line number Diff line number Diff line change
@@ -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

45 changes: 45 additions & 0 deletions tests/ui/checked-type-aliases/implied-outlives-bounds-0.rs
Original file line number Diff line number Diff line change
@@ -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() {}
Original file line number Diff line number Diff line change
@@ -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<K, V>);
| ^^^^^^^^^^^^^^^^^^^^^^
|
= 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

35 changes: 35 additions & 0 deletions tests/ui/checked-type-aliases/implied-outlives-bounds-1.rs
Original file line number Diff line number Diff line change
@@ -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<K, V>` we want to infer outlives-bounds of the form `C: 'a`
// for all outlives components `C` of `Alias<K, V>`. 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<K, V>);
//[dump]~^ ERROR rustc_dump_inferred_outlives
//[dump]~| NOTE K: 'a
//[dump]~| NOTE V: 'a

type Alias0<K, V> = (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() {}
Loading