Skip to content
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

Require lifetime bounds for opaque types in order to allow hidden types to capture said lifetimes #102417

Merged
merged 1 commit into from
Oct 20, 2022
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
2 changes: 1 addition & 1 deletion compiler/rustc_borrowck/src/region_infer/opaque_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
}

let definition_ty = instantiated_ty
.remap_generic_params_to_declaration_params(opaque_type_key, self.tcx, false)
.remap_generic_params_to_declaration_params(opaque_type_key, self.tcx, false, origin)
.ty;

if !check_opaque_type_parameter_valid(
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir_analysis/src/check/writeback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
opaque_type_key,
self.fcx.infcx.tcx,
true,
decl.origin,
);

self.typeck_results.concrete_opaque_types.insert(opaque_type_key.def_id, hidden_type);
Expand Down
77 changes: 75 additions & 2 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use crate::ty::util::Discr;
pub use adt::*;
pub use assoc::*;
pub use generics::*;
use hir::OpaqueTyOrigin;
use rustc_ast as ast;
use rustc_ast::node_id::NodeMap;
use rustc_attr as attr;
Expand Down Expand Up @@ -1309,6 +1310,7 @@ impl<'tcx> OpaqueHiddenType<'tcx> {
tcx: TyCtxt<'tcx>,
// typeck errors have subpar spans for opaque types, so delay error reporting until borrowck.
ignore_errors: bool,
origin: OpaqueTyOrigin,
) -> Self {
let OpaqueTypeKey { def_id, substs } = opaque_type_key;

Expand All @@ -1320,8 +1322,79 @@ impl<'tcx> OpaqueHiddenType<'tcx> {
// shifting.
let id_substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id());
debug!(?id_substs);
let map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>> =
substs.iter().enumerate().map(|(index, subst)| (subst, id_substs[index])).collect();

let map = substs.iter().zip(id_substs);

let map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>> = match origin {
// HACK: The HIR lowering for async fn does not generate
// any `+ Captures<'x>` bounds for the `impl Future<...>`, so all async fns with lifetimes
// would now fail to compile. We should probably just make hir lowering fill this in properly.
OpaqueTyOrigin::AsyncFn(_) => map.collect(),
OpaqueTyOrigin::FnReturn(_) | OpaqueTyOrigin::TyAlias => {
// Opaque types may only use regions that are bound. So for
// ```rust
// type Foo<'a, 'b, 'c> = impl Trait<'a> + 'b;
// ```
// we may not use `'c` in the hidden type.
struct OpaqueTypeLifetimeCollector<'tcx> {
lifetimes: FxHashSet<ty::Region<'tcx>>,
}

impl<'tcx> ty::TypeVisitor<'tcx> for OpaqueTypeLifetimeCollector<'tcx> {
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
self.lifetimes.insert(r);
r.super_visit_with(self)
}
}

let mut collector = OpaqueTypeLifetimeCollector { lifetimes: Default::default() };

for pred in tcx.bound_explicit_item_bounds(def_id.to_def_id()).transpose_iter() {
let pred = pred.map_bound(|(pred, _)| *pred).subst(tcx, id_substs);

trace!(pred=?pred.kind());

// We only ignore opaque type substs if the opaque type is the outermost type.
// The opaque type may be nested within itself via recursion in e.g.
// type Foo<'a> = impl PartialEq<Foo<'a>>;
// which thus mentions `'a` and should thus accept hidden types that borrow 'a
// instead of requiring an additional `+ 'a`.
match pred.kind().skip_binder() {
ty::PredicateKind::Trait(TraitPredicate {
trait_ref: ty::TraitRef { def_id: _, substs },
constness: _,
polarity: _,
}) => {
trace!(?substs);
for subst in &substs[1..] {
subst.visit_with(&mut collector);
}
}
ty::PredicateKind::Projection(ty::ProjectionPredicate {
projection_ty: ty::ProjectionTy { substs, item_def_id: _ },
term,
}) => {
for subst in &substs[1..] {
subst.visit_with(&mut collector);
}
term.visit_with(&mut collector);
}
_ => {
pred.visit_with(&mut collector);
}
}
}
let lifetimes = collector.lifetimes;
trace!(?lifetimes);
map.filter(|(_, v)| {
let ty::GenericArgKind::Lifetime(lt) = v.unpack() else {
return true;
};
lifetimes.contains(&lt)
})
.collect()
}
};
debug!("map = {:#?}", map);

// Convert the type from the function into a type valid outside
Expand Down
6 changes: 5 additions & 1 deletion src/test/ui/impl-trait/issue-86465.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#![feature(type_alias_impl_trait)]

type X<'a, 'b> = impl std::fmt::Debug;
pub trait Captures<'a> {}

impl<'a, T: ?Sized> Captures<'a> for T {}

type X<'a, 'b> = impl std::fmt::Debug + Captures<'a> + Captures<'b>;

fn f<'t, 'u>(a: &'t u32, b: &'u u32) -> (X<'t, 'u>, X<'u, 't>) {
(a, a)
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/impl-trait/issue-86465.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: concrete type differs from previous defining opaque type use
--> $DIR/issue-86465.rs:6:5
--> $DIR/issue-86465.rs:10:5
|
LL | (a, a)
| ^^^^^^
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
#![feature(type_alias_impl_trait)]
#![allow(dead_code)]

type OneLifetime<'a, 'b> = impl std::fmt::Debug;
pub trait Captures<'a> {}

impl<'a, T: ?Sized> Captures<'a> for T {}

type OneLifetime<'a, 'b> = impl std::fmt::Debug + Captures<'a> + Captures<'b>;

fn foo<'a, 'b>(a: &'a u32, b: &'b u32) -> OneLifetime<'a, 'b> {
a
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
error: concrete type differs from previous defining opaque type use
--> $DIR/different_lifetimes_defining_uses.rs:11:5
--> $DIR/different_lifetimes_defining_uses.rs:15:5
|
LL | b
| ^ expected `&'a u32`, got `&'b u32`
|
note: previous use here
--> $DIR/different_lifetimes_defining_uses.rs:7:5
--> $DIR/different_lifetimes_defining_uses.rs:11:5
|
LL | a
| ^
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

fn main() {}

type Two<'a, 'b> = impl std::fmt::Debug;
pub trait Captures<'a> {}

impl<'a, T: ?Sized> Captures<'a> for T {}

type Two<'a, 'b> = impl std::fmt::Debug + Captures<'a> + Captures<'b>;

fn one<'a>(t: &'a ()) -> Two<'a, 'a> {
t
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
error: non-defining opaque type use in defining scope
--> $DIR/generic_duplicate_lifetime_param.rs:9:5
--> $DIR/generic_duplicate_lifetime_param.rs:12:5
|
LL | t
| ^
|
note: lifetime used multiple times
--> $DIR/generic_duplicate_lifetime_param.rs:5:10
--> $DIR/generic_duplicate_lifetime_param.rs:9:10
|
LL | type Two<'a, 'b> = impl std::fmt::Debug;
LL | type Two<'a, 'b> = impl std::fmt::Debug + Captures<'a> + Captures<'b>;
| ^^ ^^

error: aborting due to previous error
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ fn main() {}
// test that unused generic parameters are ok
type TwoTys<T, U> = impl Debug;

type TwoLifetimes<'a, 'b> = impl Debug;

pub trait Captures<'a> {}

impl<'a, T: ?Sized> Captures<'a> for T {}

type TwoLifetimes<'a, 'b> = impl Debug + Captures<'a> + Captures<'b>;

type TwoConsts<const X: usize, const Y: usize> = impl Debug;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: non-defining opaque type use in defining scope
--> $DIR/generic_duplicate_param_use.rs:16:5
--> $DIR/generic_duplicate_param_use.rs:21:5
|
LL | t
| ^
Expand All @@ -11,25 +11,25 @@ LL | type TwoTys<T, U> = impl Debug;
| ^ ^

error: non-defining opaque type use in defining scope
--> $DIR/generic_duplicate_param_use.rs:21:5
--> $DIR/generic_duplicate_param_use.rs:26:5
|
LL | t
| ^
|
note: lifetime used multiple times
--> $DIR/generic_duplicate_param_use.rs:10:19
--> $DIR/generic_duplicate_param_use.rs:15:19
|
LL | type TwoLifetimes<'a, 'b> = impl Debug;
LL | type TwoLifetimes<'a, 'b> = impl Debug + Captures<'a> + Captures<'b>;
| ^^ ^^

error: non-defining opaque type use in defining scope
--> $DIR/generic_duplicate_param_use.rs:26:5
--> $DIR/generic_duplicate_param_use.rs:31:5
|
LL | t
| ^
|
note: constant used multiple times
--> $DIR/generic_duplicate_param_use.rs:12:16
--> $DIR/generic_duplicate_param_use.rs:17:16
|
LL | type TwoConsts<const X: usize, const Y: usize> = impl Debug;
| ^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^
Expand Down
5 changes: 3 additions & 2 deletions src/test/ui/type-alias-impl-trait/generic_lifetime_param.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
// build-pass (FIXME(62277): could be check-pass?)
// check-pass

#![feature(type_alias_impl_trait)]

fn main() {}

type Region<'a> = impl std::fmt::Debug;
type Region<'a> = impl std::fmt::Debug + 'a;


fn region<'b>(a: &'b ()) -> Region<'b> {
a
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#![feature(type_alias_impl_trait)]

mod test_lifetime_param {
type Ty<'a> = impl Sized;
type Ty<'a> = impl Sized + 'a;
fn defining(a: &str) -> Ty<'_> { a }
fn assert_static<'a: 'static>() {}
//~^ WARN: unnecessary lifetime parameter `'a`
Expand All @@ -10,7 +10,7 @@ mod test_lifetime_param {
}

mod test_higher_kinded_lifetime_param {
type Ty<'a> = impl Sized;
type Ty<'a> = impl Sized + 'a;
fn defining(a: &str) -> Ty<'_> { a }
fn assert_static<'a: 'static>() {}
//~^ WARN: unnecessary lifetime parameter `'a`
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/type-alias-impl-trait/issue-89686.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use std::future::Future;

type G<'a, T> = impl Future<Output = ()>;
type G<'a, T> = impl Future<Output = ()> + 'a;

trait Trait {
type F: Future<Output = ()>;
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/type-alias-impl-trait/issue-89686.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ LL | async move { self.f().await }
|
help: consider restricting type parameter `T`
|
LL | type G<'a, T: Trait> = impl Future<Output = ()>;
LL | type G<'a, T: Trait> = impl Future<Output = ()> + 'a;
| +++++++

error: aborting due to previous error
Expand Down
7 changes: 7 additions & 0 deletions src/test/ui/type-alias-impl-trait/missing_lifetime_bound.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#![feature(type_alias_impl_trait)]

type Opaque<'a, T> = impl Sized;
fn defining<'a, T>(x: &'a i32) -> Opaque<T> { x }
//~^ ERROR: non-defining opaque type use in defining scope

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
error: non-defining opaque type use in defining scope
--> $DIR/missing_lifetime_bound.rs:4:47
|
LL | fn defining<'a, T>(x: &'a i32) -> Opaque<T> { x }
| ^ lifetime `'a` is part of concrete type but not used in parameter list of the `impl Trait` type alias

error: aborting due to previous error

Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#![feature(type_alias_impl_trait)]

type Foo<'a, 'b> = impl std::fmt::Debug;
pub trait Captures<'a> {}

impl<'a, T: ?Sized> Captures<'a> for T {}

type Foo<'a, 'b> = impl std::fmt::Debug + Captures<'a> + Captures<'b>;

fn foo<'x, 'y>(i: &'x i32, j: &'y i32) -> (Foo<'x, 'y>, Foo<'y, 'x>) {
(i, i) //~ ERROR concrete type differs from previous
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: concrete type differs from previous defining opaque type use
--> $DIR/multiple-def-uses-in-one-fn-lifetimes.rs:6:5
--> $DIR/multiple-def-uses-in-one-fn-lifetimes.rs:10:5
|
LL | (i, i)
| ^^^^^^
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ fn f<A: ToString + Clone, B: ToString + Clone>(a: A, b: B) -> (X<A, B>, X<A, B>)
(a.clone(), a)
}

type Foo<'a, 'b> = impl std::fmt::Debug;
pub trait Captures<'a> {}

impl<'a, T: ?Sized> Captures<'a> for T {}

type Foo<'a, 'b> = impl std::fmt::Debug + Captures<'a> + Captures<'b>;

fn foo<'x, 'y>(i: &'x i32, j: &'y i32) -> (Foo<'x, 'y>, Foo<'y, 'x>) {
(i, j)
Expand Down