Skip to content

Commit

Permalink
Require lifetime bounds for opaque types in order to allow hidden typ…
Browse files Browse the repository at this point in the history
…es to capture said lifetimes
  • Loading branch information
oli-obk authored and Yiming Lei committed Oct 21, 2022
1 parent 3d7a9c8 commit cdf6163
Show file tree
Hide file tree
Showing 20 changed files with 142 additions and 28 deletions.
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

0 comments on commit cdf6163

Please sign in to comment.