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

Inherit lifetimes for async fn instead of duplicating them. #91403

Merged
merged 5 commits into from
Feb 13, 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
69 changes: 19 additions & 50 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1659,11 +1659,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {

// When we create the opaque type for this async fn, it is going to have
// to capture all the lifetimes involved in the signature (including in the
// return type). This is done by introducing lifetime parameters for:
// return type). This is done by:
//
// - all the explicitly declared lifetimes from the impl and function itself;
// - all the elided lifetimes in the fn arguments;
// - all the elided lifetimes in the return type.
// - making the opaque type inherit all lifetime parameters from its parent;
// - make all the elided lifetimes in the fn arguments into parameters;
// - manually introducing parameters on the opaque type for elided
// lifetimes in the return type.
//
// So for example in this snippet:
//
Expand All @@ -1679,44 +1680,22 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// we would create an opaque type like:
//
// ```
// type Bar<'a, 'b, '0, '1, '2> = impl Future<Output = &'2 u32>;
// type Foo<'a>::bar<'b, '0, '1>::Bar<'2> = impl Future<Output = &'2 u32>;
// ```
//
// and we would then desugar `bar` to the equivalent of:
//
// ```rust
// impl<'a> Foo<'a> {
// fn bar<'b, '0, '1>(&'0 self, x: &'b Vec<f64>, y: &'1 str) -> Bar<'a, 'b, '0, '1, '_>
// fn bar<'b, '0, '1>(&'0 self, x: &'b Vec<f64>, y: &'1 str) -> Bar<'_>
// }
// ```
//
// Note that the final parameter to `Bar` is `'_`, not `'2` --
// this is because the elided lifetimes from the return type
// should be figured out using the ordinary elision rules, and
// this desugaring achieves that.

debug!("lower_async_fn_ret_ty: in_scope_lifetimes={:#?}", self.in_scope_lifetimes);
debug!("lower_async_fn_ret_ty: lifetimes_to_define={:#?}", self.lifetimes_to_define);

// Calculate all the lifetimes that should be captured
// by the opaque type. This should include all in-scope
// lifetime parameters, including those defined in-band.
//
// `lifetime_params` is a vector of tuple (span, parameter name, lifetime name).

// Input lifetime like `'a` or `'1`:
let mut lifetime_params: Vec<_> = self
.in_scope_lifetimes
.iter()
.cloned()
.map(|name| (name.ident().span, name, hir::LifetimeName::Param(name)))
.chain(
self.lifetimes_to_define
.iter()
.map(|&(span, name)| (span, name, hir::LifetimeName::Param(name))),
)
.collect();

let mut lifetime_params = Vec::new();
self.with_hir_id_owner(opaque_ty_node_id, |this| {
// We have to be careful to get elision right here. The
// idea is that we create a lifetime parameter for each
Expand All @@ -1735,16 +1714,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
debug!("lower_async_fn_ret_ty: future_bound={:#?}", future_bound);
debug!("lower_async_fn_ret_ty: lifetimes_to_define={:#?}", lifetimes_to_define);

lifetime_params.extend(
// Output lifetime like `'_`:
lifetimes_to_define
.into_iter()
.map(|(span, name)| (span, name, hir::LifetimeName::Implicit(false))),
);
// Output lifetime like `'_`:
lifetime_params = lifetimes_to_define;
debug!("lower_async_fn_ret_ty: lifetime_params={:#?}", lifetime_params);

let generic_params =
this.arena.alloc_from_iter(lifetime_params.iter().map(|&(span, hir_name, _)| {
this.arena.alloc_from_iter(lifetime_params.iter().map(|&(span, hir_name)| {
this.lifetime_to_generic_param(span, hir_name, opaque_ty_def_id)
}));

Expand All @@ -1762,28 +1737,22 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
this.generate_opaque_type(opaque_ty_def_id, opaque_ty_item, span, opaque_ty_span)
});

// As documented above on the variable
// `input_lifetimes_count`, we need to create the lifetime
// arguments to our opaque type. Continuing with our example,
// we're creating the type arguments for the return type:
// We need to create the lifetime arguments to our opaque type.
// Continuing with our example, we're creating the type arguments
// for the return type:
//
// ```
// Bar<'a, 'b, '0, '1, '_>
// For<'a>::bar<'b, '0, '1>::Bar<'_>
// ```
//
// For the "input" lifetime parameters, we wish to create
// references to the parameters themselves, including the
// "implicit" ones created from parameter types (`'a`, `'b`,
// '`0`, `'1`).
//
// For the "output" lifetime parameters, we just want to
// generate `'_`.
// For the "input" lifetime parameters are inherited automatically.
// For the "output" lifetime parameters, we just want to generate `'_`.
let generic_args =
self.arena.alloc_from_iter(lifetime_params.into_iter().map(|(span, _, name)| {
self.arena.alloc_from_iter(lifetime_params.into_iter().map(|(span, _)| {
GenericArg::Lifetime(hir::Lifetime {
hir_id: self.next_id(),
span: self.lower_span(span),
name,
name: hir::LifetimeName::Implicit(false),
})
}));

Expand Down
18 changes: 18 additions & 0 deletions compiler/rustc_borrowck/src/region_infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2156,6 +2156,24 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}
}

// When in async fn, prefer errors that come from inside the closure.
if !categorized_path[i].from_closure {
let span = categorized_path.iter().find_map(|p| {
if p.from_closure
&& p.category == categorized_path[i].category
&& categorized_path[i].cause.span.contains(p.cause.span)
{
Some(p.cause.span)
} else {
None
}
});

if let Some(span) = span {
categorized_path[i].cause.span = span;
}
}

return categorized_path[i].clone();
}

Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_error_codes/src/error_codes/E0760.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
`async fn`/`impl trait` return type cannot contain a projection
`impl trait` return type cannot contain a projection
or `Self` that references lifetimes from a parent scope.

Erroneous code example:
Expand All @@ -7,7 +7,7 @@ Erroneous code example:
struct S<'a>(&'a i32);

impl<'a> S<'a> {
async fn new(i: &'a i32) -> Self {
fn new(i: &'a i32) -> impl Into<Self> {
S(&22)
}
}
Expand All @@ -19,7 +19,7 @@ To fix this error we need to spell out `Self` to `S<'a>`:
struct S<'a>(&'a i32);

impl<'a> S<'a> {
async fn new(i: &'a i32) -> S<'a> {
fn new(i: &'a i32) -> impl Into<S<'a>> {
S(&22)
}
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_infer/src/infer/opaque_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
debug!(?concrete_ty);

let first_own_region = match opaque_defn.origin {
hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => {
hir::OpaqueTyOrigin::FnReturn(..) => {
// We lower
//
// fn foo<'l0..'ln>() -> impl Trait<'l0..'lm>
Expand All @@ -291,7 +291,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
}
// These opaque type inherit all lifetime parameters from their
// parent, so we have to check them all.
hir::OpaqueTyOrigin::TyAlias => 0,
hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::TyAlias => 0,
};

// For a case like `impl Foo<'a, 'b>`, we would generate a constraint
Expand Down
34 changes: 26 additions & 8 deletions compiler/rustc_resolve/src/late/lifetimes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -729,9 +729,16 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
match item.kind {
hir::ItemKind::Fn(ref sig, ref generics, _) => {
self.missing_named_lifetime_spots.push(generics.into());
self.visit_early_late(None, item.hir_id(), &sig.decl, generics, |this| {
intravisit::walk_item(this, item);
});
self.visit_early_late(
None,
item.hir_id(),
&sig.decl,
generics,
sig.header.asyncness,
|this| {
intravisit::walk_item(this, item);
},
);
self.missing_named_lifetime_spots.pop();
}

Expand Down Expand Up @@ -849,11 +856,16 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {

fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) {
match item.kind {
hir::ForeignItemKind::Fn(ref decl, _, ref generics) => {
self.visit_early_late(None, item.hir_id(), decl, generics, |this| {
hir::ForeignItemKind::Fn(ref decl, _, ref generics) => self.visit_early_late(
None,
item.hir_id(),
decl,
generics,
hir::IsAsync::NotAsync,
|this| {
intravisit::walk_foreign_item(this, item);
})
}
},
),
hir::ForeignItemKind::Static(..) => {
intravisit::walk_foreign_item(self, item);
}
Expand Down Expand Up @@ -1130,6 +1142,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
trait_item.hir_id(),
&sig.decl,
&trait_item.generics,
sig.header.asyncness,
|this| intravisit::walk_trait_item(this, trait_item),
);
self.missing_named_lifetime_spots.pop();
Expand Down Expand Up @@ -1199,6 +1212,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
impl_item.hir_id(),
&sig.decl,
&impl_item.generics,
sig.header.asyncness,
|this| intravisit::walk_impl_item(this, impl_item),
);
self.missing_named_lifetime_spots.pop();
Expand Down Expand Up @@ -2159,11 +2173,15 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
hir_id: hir::HirId,
decl: &'tcx hir::FnDecl<'tcx>,
generics: &'tcx hir::Generics<'tcx>,
asyncness: hir::IsAsync,
walk: F,
) where
F: for<'b, 'c> FnOnce(&'b mut LifetimeContext<'c, 'tcx>),
{
insert_late_bound_lifetimes(self.map, decl, generics);
// Async fns need all their lifetime parameters to be early bound.
if asyncness != hir::IsAsync::Async {
insert_late_bound_lifetimes(self.map, decl, generics);
}

// Find the start of nested early scopes, e.g., in methods.
let mut next_early_index = 0;
Expand Down
15 changes: 5 additions & 10 deletions compiler/rustc_typeck/src/astconv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2408,16 +2408,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let def_id = item_id.def_id.to_def_id();

match opaque_ty.kind {
hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => self
.impl_trait_ty_to_ty(
def_id,
lifetimes,
matches!(
origin,
hir::OpaqueTyOrigin::FnReturn(..)
| hir::OpaqueTyOrigin::AsyncFn(..)
),
),
hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => {
let replace_parent_lifetimes =
matches!(origin, hir::OpaqueTyOrigin::FnReturn(..));
self.impl_trait_ty_to_ty(def_id, lifetimes, replace_parent_lifetimes)
}
ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i),
}
}
Expand Down
15 changes: 3 additions & 12 deletions compiler/rustc_typeck/src/check/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -535,10 +535,8 @@ pub(super) fn check_opaque_for_inheriting_lifetimes<'tcx>(
}
}

if let ItemKind::OpaqueTy(hir::OpaqueTy {
origin: hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::FnReturn(..),
..
}) = item.kind
if let ItemKind::OpaqueTy(hir::OpaqueTy { origin: hir::OpaqueTyOrigin::FnReturn(..), .. }) =
item.kind
{
let mut visitor = ProhibitOpaqueVisitor {
opaque_identity_ty: tcx.mk_opaque(
Expand All @@ -560,20 +558,13 @@ pub(super) fn check_opaque_for_inheriting_lifetimes<'tcx>(

if let Some(ty) = prohibit_opaque.break_value() {
visitor.visit_item(&item);
let is_async = match item.kind {
ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => {
matches!(origin, hir::OpaqueTyOrigin::AsyncFn(..))
}
_ => unreachable!(),
};

let mut err = struct_span_err!(
tcx.sess,
span,
E0760,
"`{}` return type cannot contain a projection or `Self` that references lifetimes from \
"`impl Trait` return type cannot contain a projection or `Self` that references lifetimes from \
a parent scope",
if is_async { "async fn" } else { "impl Trait" },
);

for (span, name) in visitor.selftys {
Expand Down
5 changes: 2 additions & 3 deletions compiler/rustc_typeck/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2162,8 +2162,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP
generics
}
ItemKind::OpaqueTy(OpaqueTy {
origin: hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::FnReturn(..),
..
origin: hir::OpaqueTyOrigin::FnReturn(..), ..
}) => {
// return-position impl trait
//
Expand All @@ -2183,7 +2182,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP
}
ItemKind::OpaqueTy(OpaqueTy {
ref generics,
origin: hir::OpaqueTyOrigin::TyAlias,
origin: hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::TyAlias,
..
}) => {
// type-alias impl trait
Expand Down
7 changes: 6 additions & 1 deletion src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,12 @@ fn clean_ty_generics(
.params
.iter()
.filter_map(|param| match param.kind {
ty::GenericParamDefKind::Lifetime => Some(param.clean(cx)),
ty::GenericParamDefKind::Lifetime => {
if param.name == kw::UnderscoreLifetime {
return None;
}
Some(param.clean(cx))
}
ty::GenericParamDefKind::Type { synthetic, .. } => {
if param.name == kw::SelfUpper {
assert_eq!(param.index, 0);
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/async-await/issue-61949-self-return-type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ pub struct Foo<'a> {

impl<'a> Foo<'a> {
pub async fn new(_bar: &'a i32) -> Self {
//~^ ERROR `async fn` return type cannot contain a projection or `Self` that references lifetimes from a parent scope
Foo {
bar: &22
}
Expand All @@ -19,6 +18,7 @@ async fn foo() {
let x = {
let bar = 22;
Foo::new(&bar).await
//~^ ERROR `bar` does not live long enough [E0597]
};
drop(x);
}
Expand Down
16 changes: 11 additions & 5 deletions src/test/ui/async-await/issue-61949-self-return-type.stderr
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
error[E0760]: `async fn` return type cannot contain a projection or `Self` that references lifetimes from a parent scope
--> $DIR/issue-61949-self-return-type.rs:10:40
error[E0597]: `bar` does not live long enough
--> $DIR/issue-61949-self-return-type.rs:20:18
|
LL | pub async fn new(_bar: &'a i32) -> Self {
| ^^^^ help: consider spelling out the type instead: `Foo<'a>`
LL | let x = {
| - borrow later stored here
LL | let bar = 22;
LL | Foo::new(&bar).await
| ^^^^ borrowed value does not live long enough
LL |
LL | };
| - `bar` dropped here while still borrowed

error: aborting due to previous error

For more information about this error, try `rustc --explain E0760`.
For more information about this error, try `rustc --explain E0597`.
Loading