Skip to content

Commit

Permalink
Auto merge of rust-lang#102417 - oli-obk:opaque_lifetimes2, r=jackh726
Browse files Browse the repository at this point in the history
Require lifetime bounds for opaque types in order to allow hidden types to capture said lifetimes

fixes rust-lang#96996

cc `@aliemjay`
  • Loading branch information
bors committed Oct 20, 2022
2 parents 57781b2 + 1dc2119 commit cb94675
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 cb94675

Please sign in to comment.