Skip to content

Commit

Permalink
Rollup merge of rust-lang#119978 - compiler-errors:async-closure-capt…
Browse files Browse the repository at this point in the history
…ures, r=oli-obk

Move async closure parameters into the resultant closure's future eagerly

Move async closure parameters into the closure's resultant future eagerly.

Before, we used to desugar `async |p1, p2, ..| { body }` as `|p1, p2, ..| { || async { body } }`. Now, we desugar the above like `|p1, p2, ..| { async move { let p1 = p1; let p2 = p2; ... body } }`. This mirrors the same desugaring that `async fn` does with its parameter types, and the compiler literally uses the same code via a shared helper function.

This removes the necessity for E0708, since now expressions like `async |x: i32| { x }` will not give you confusing borrow errors.

This does *not* fix the case where async closures have self-borrows. This will come with a general implementation of async closures, which is still in the works.

r? oli-obk
  • Loading branch information
matthiaskrgr committed Jan 16, 2024
2 parents f990e22 + 04a5ee6 commit 3ef303b
Show file tree
Hide file tree
Showing 13 changed files with 350 additions and 320 deletions.
4 changes: 0 additions & 4 deletions compiler/rustc_ast_lowering/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@ ast_lowering_assoc_ty_parentheses =
ast_lowering_async_coroutines_not_supported =
`async` coroutines are not yet supported
ast_lowering_async_non_move_closure_not_supported =
`async` non-`move` closures with parameters are not currently supported
.help = consider using `let` statements to manually capture variables by reference before entering an `async move` closure
ast_lowering_att_syntax_only_x86 =
the `att_syntax` option is only supported on x86
Expand Down
8 changes: 0 additions & 8 deletions compiler/rustc_ast_lowering/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,14 +145,6 @@ pub struct ClosureCannotBeStatic {
pub fn_decl_span: Span,
}

#[derive(Diagnostic, Clone, Copy)]
#[help]
#[diag(ast_lowering_async_non_move_closure_not_supported, code = "E0708")]
pub struct AsyncNonMoveClosureNotSupported {
#[primary_span]
pub fn_decl_span: Span,
}

#[derive(Diagnostic, Clone, Copy)]
#[diag(ast_lowering_functional_record_update_destructuring_assignment)]
pub struct FunctionalRecordUpdateDestructuringAssignment {
Expand Down
39 changes: 15 additions & 24 deletions compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::errors::{
AsyncCoroutinesNotSupported, AsyncNonMoveClosureNotSupported, AwaitOnlyInAsyncFnAndBlocks,
BaseExpressionDoubleDot, ClosureCannotBeStatic, CoroutineTooManyParameters,
AsyncCoroutinesNotSupported, AwaitOnlyInAsyncFnAndBlocks, BaseExpressionDoubleDot,
ClosureCannotBeStatic, CoroutineTooManyParameters,
FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd, MatchArmWithNoBody,
NeverPatternWithBody, NeverPatternWithGuard, NotSupportedForLifetimeBinderAsyncClosure,
UnderscoreExprLhsAssign,
Expand All @@ -13,7 +13,6 @@ use rustc_ast::*;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_middle::span_bug;
use rustc_session::errors::report_lit_error;
use rustc_span::source_map::{respan, Spanned};
use rustc_span::symbol::{kw, sym, Ident, Symbol};
Expand Down Expand Up @@ -1028,51 +1027,43 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn_decl_span: Span,
fn_arg_span: Span,
) -> hir::ExprKind<'hir> {
let CoroutineKind::Async { closure_id: inner_closure_id, .. } = coroutine_kind else {
span_bug!(fn_decl_span, "`async gen` and `gen` closures are not supported, yet");
};

if let &ClosureBinder::For { span, .. } = binder {
self.dcx().emit_err(NotSupportedForLifetimeBinderAsyncClosure { span });
}

let (binder_clause, generic_params) = self.lower_closure_binder(binder);

let outer_decl =
FnDecl { inputs: decl.inputs.clone(), output: FnRetTy::Default(fn_decl_span) };

let body = self.with_new_scopes(fn_decl_span, |this| {
// FIXME(cramertj): allow `async` non-`move` closures with arguments.
if capture_clause == CaptureBy::Ref && !decl.inputs.is_empty() {
this.dcx().emit_err(AsyncNonMoveClosureNotSupported { fn_decl_span });
}

// Transform `async |x: u8| -> X { ... }` into
// `|x: u8| || -> X { ... }`.
let body_id = this.lower_fn_body(&outer_decl, |this| {
let body_id = this.lower_body(|this| {
let async_ret_ty = if let FnRetTy::Ty(ty) = &decl.output {
let itctx = ImplTraitContext::Disallowed(ImplTraitPosition::AsyncBlock);
Some(hir::FnRetTy::Return(this.lower_ty(ty, &itctx)))
} else {
None
};

let async_body = this.make_desugared_coroutine_expr(
capture_clause,
inner_closure_id,
async_ret_ty,
let (parameters, expr) = this.lower_coroutine_body_with_moved_arguments(
decl,
|this| this.with_new_scopes(fn_decl_span, |this| this.lower_expr_mut(body)),
body.span,
hir::CoroutineDesugaring::Async,
coroutine_kind,
hir::CoroutineSource::Closure,
|this| this.with_new_scopes(fn_decl_span, |this| this.lower_expr_mut(body)),
async_ret_ty,
);
let hir_id = this.lower_node_id(inner_closure_id);

let hir_id = this.lower_node_id(coroutine_kind.closure_id());
this.maybe_forward_track_caller(body.span, closure_hir_id, hir_id);
hir::Expr { hir_id, kind: async_body, span: this.lower_span(body.span) }

(parameters, expr)
});
body_id
});

let outer_decl =
FnDecl { inputs: decl.inputs.clone(), output: FnRetTy::Default(fn_decl_span) };

let bound_generic_params = self.lower_lifetime_binder(closure_id, generic_params);
// We need to lower the declaration outside the new scope, because we
// have to conserve the state of being inside a loop condition for the
Expand Down

0 comments on commit 3ef303b

Please sign in to comment.