Skip to content

Commit 8d6080a

Browse files
committed
Auto merge of rust-lang#120361 - compiler-errors:async-closures, r=oli-obk
Rework support for async closures; allow them to return futures that borrow from the closure's captures This PR implements a new lowering for async closures via `TyKind::CoroutineClosure` which handles the curious relationship between the closure and the coroutine that it returns. I wrote up a bunch in [this hackmd](https://hackmd.io/`@compiler-errors/S1HvqQxca)` which will be copied to the dev guide after this PR lands, and hopefully left sufficient comments in the source code explaining why this change is as large as it is. This also necessitates that they begin implementing the `AsyncFn`-family of traits, rather than the `Fn`-family of traits -- if you need `Fn` implementations, you should probably use the non-sugar `|| async {}` syntax instead. Notably this PR does not yet implement `async Fn()` syntax sugar for bounds, but I expect to add those soon (**edit:** rust-lang#120392). For now, users must use `AsyncFn()` traits directly, which necessitates adding the `async_fn_traits` feature gate as well. I will add this as a follow-up very soon. r? oli-obk This is based on top of rust-lang#120322, but that PR is minimal.
2 parents f067fd6 + b53905e commit 8d6080a

File tree

182 files changed

+3613
-366
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

182 files changed

+3613
-366
lines changed

compiler/rustc_ast_lowering/messages.ftl

-3
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,6 @@ ast_lowering_never_pattern_with_guard =
117117
a guard on a never pattern will never be run
118118
.suggestion = remove this guard
119119
120-
ast_lowering_not_supported_for_lifetime_binder_async_closure =
121-
`for<...>` binders on `async` closures are not currently supported
122-
123120
ast_lowering_previously_used_here = previously used here
124121
125122
ast_lowering_register1 = register `{$reg1_name}`

compiler/rustc_ast_lowering/src/errors.rs

-7
Original file line numberDiff line numberDiff line change
@@ -326,13 +326,6 @@ pub struct MisplacedRelaxTraitBound {
326326
pub span: Span,
327327
}
328328

329-
#[derive(Diagnostic, Clone, Copy)]
330-
#[diag(ast_lowering_not_supported_for_lifetime_binder_async_closure)]
331-
pub struct NotSupportedForLifetimeBinderAsyncClosure {
332-
#[primary_span]
333-
pub span: Span,
334-
}
335-
336329
#[derive(Diagnostic)]
337330
#[diag(ast_lowering_match_arm_with_no_body)]
338331
pub struct MatchArmWithNoBody {

compiler/rustc_ast_lowering/src/expr.rs

+18-20
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
use std::assert_matches::assert_matches;
2+
13
use super::errors::{
24
AsyncCoroutinesNotSupported, AwaitOnlyInAsyncFnAndBlocks, BaseExpressionDoubleDot,
35
ClosureCannotBeStatic, CoroutineTooManyParameters,
46
FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd, MatchArmWithNoBody,
5-
NeverPatternWithBody, NeverPatternWithGuard, NotSupportedForLifetimeBinderAsyncClosure,
6-
UnderscoreExprLhsAssign,
7+
NeverPatternWithBody, NeverPatternWithGuard, UnderscoreExprLhsAssign,
78
};
89
use super::ResolverAstLoweringExt;
910
use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs};
@@ -1026,30 +1027,27 @@ impl<'hir> LoweringContext<'_, 'hir> {
10261027
fn_decl_span: Span,
10271028
fn_arg_span: Span,
10281029
) -> hir::ExprKind<'hir> {
1029-
if let &ClosureBinder::For { span, .. } = binder {
1030-
self.dcx().emit_err(NotSupportedForLifetimeBinderAsyncClosure { span });
1031-
}
1032-
10331030
let (binder_clause, generic_params) = self.lower_closure_binder(binder);
10341031

1032+
assert_matches!(
1033+
coroutine_kind,
1034+
CoroutineKind::Async { .. },
1035+
"only async closures are supported currently"
1036+
);
1037+
10351038
let body = self.with_new_scopes(fn_decl_span, |this| {
1039+
let inner_decl =
1040+
FnDecl { inputs: decl.inputs.clone(), output: FnRetTy::Default(fn_decl_span) };
1041+
10361042
// Transform `async |x: u8| -> X { ... }` into
10371043
// `|x: u8| || -> X { ... }`.
10381044
let body_id = this.lower_body(|this| {
1039-
let async_ret_ty = if let FnRetTy::Ty(ty) = &decl.output {
1040-
let itctx = ImplTraitContext::Disallowed(ImplTraitPosition::AsyncBlock);
1041-
Some(hir::FnRetTy::Return(this.lower_ty(ty, &itctx)))
1042-
} else {
1043-
None
1044-
};
1045-
10461045
let (parameters, expr) = this.lower_coroutine_body_with_moved_arguments(
1047-
decl,
1046+
&inner_decl,
10481047
|this| this.with_new_scopes(fn_decl_span, |this| this.lower_expr_mut(body)),
10491048
body.span,
10501049
coroutine_kind,
10511050
hir::CoroutineSource::Closure,
1052-
async_ret_ty,
10531051
);
10541052

10551053
let hir_id = this.lower_node_id(coroutine_kind.closure_id());
@@ -1060,15 +1058,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
10601058
body_id
10611059
});
10621060

1063-
let outer_decl =
1064-
FnDecl { inputs: decl.inputs.clone(), output: FnRetTy::Default(fn_decl_span) };
1065-
10661061
let bound_generic_params = self.lower_lifetime_binder(closure_id, generic_params);
10671062
// We need to lower the declaration outside the new scope, because we
10681063
// have to conserve the state of being inside a loop condition for the
10691064
// closure argument types.
10701065
let fn_decl =
1071-
self.lower_fn_decl(&outer_decl, closure_id, fn_decl_span, FnDeclKind::Closure, None);
1066+
self.lower_fn_decl(&decl, closure_id, fn_decl_span, FnDeclKind::Closure, None);
10721067

10731068
let c = self.arena.alloc(hir::Closure {
10741069
def_id: self.local_def_id(closure_id),
@@ -1079,7 +1074,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
10791074
body,
10801075
fn_decl_span: self.lower_span(fn_decl_span),
10811076
fn_arg_span: Some(self.lower_span(fn_arg_span)),
1082-
kind: hir::ClosureKind::Closure,
1077+
// Lower this as a `CoroutineClosure`. That will ensure that HIR typeck
1078+
// knows that a `FnDecl` output type like `-> &str` actually means
1079+
// "coroutine that returns &str", rather than directly returning a `&str`.
1080+
kind: hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async),
10831081
constness: hir::Constness::NotConst,
10841082
});
10851083
hir::ExprKind::Closure(c)

compiler/rustc_ast_lowering/src/item.rs

+6-7
Original file line numberDiff line numberDiff line change
@@ -1086,7 +1086,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
10861086
body.span,
10871087
coroutine_kind,
10881088
hir::CoroutineSource::Fn,
1089-
None,
10901089
);
10911090

10921091
// FIXME(async_fn_track_caller): Can this be moved above?
@@ -1108,7 +1107,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
11081107
body_span: Span,
11091108
coroutine_kind: CoroutineKind,
11101109
coroutine_source: hir::CoroutineSource,
1111-
return_type_hint: Option<hir::FnRetTy<'hir>>,
11121110
) -> (&'hir [hir::Param<'hir>], hir::Expr<'hir>) {
11131111
let mut parameters: Vec<hir::Param<'_>> = Vec::new();
11141112
let mut statements: Vec<hir::Stmt<'_>> = Vec::new();
@@ -1278,12 +1276,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
12781276
};
12791277
let closure_id = coroutine_kind.closure_id();
12801278
let coroutine_expr = self.make_desugared_coroutine_expr(
1281-
// FIXME(async_closures): This should only move locals,
1282-
// and not upvars. Capturing closure upvars by ref doesn't
1283-
// work right now anyways, so whatever.
1284-
CaptureBy::Value { move_kw: rustc_span::DUMMY_SP },
1279+
// The default capture mode here is by-ref. Later on during upvar analysis,
1280+
// we will force the captured arguments to by-move, but for async closures,
1281+
// we want to make sure that we avoid unnecessarily moving captures, or else
1282+
// all async closures would default to `FnOnce` as their calling mode.
1283+
CaptureBy::Ref,
12851284
closure_id,
1286-
return_type_hint,
1285+
None,
12871286
body_span,
12881287
desugaring_kind,
12891288
coroutine_source,

compiler/rustc_ast_lowering/src/lib.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#![allow(internal_features)]
3434
#![feature(rustdoc_internals)]
3535
#![doc(rust_logo)]
36+
#![feature(assert_matches)]
3637
#![feature(box_patterns)]
3738
#![feature(let_chains)]
3839
#![deny(rustc::untranslatable_diagnostic)]
@@ -296,7 +297,6 @@ enum ImplTraitPosition {
296297
Path,
297298
Variable,
298299
Trait,
299-
AsyncBlock,
300300
Bound,
301301
Generic,
302302
ExternFnParam,
@@ -323,7 +323,6 @@ impl std::fmt::Display for ImplTraitPosition {
323323
ImplTraitPosition::Path => "paths",
324324
ImplTraitPosition::Variable => "the type of variable bindings",
325325
ImplTraitPosition::Trait => "traits",
326-
ImplTraitPosition::AsyncBlock => "async blocks",
327326
ImplTraitPosition::Bound => "bounds",
328327
ImplTraitPosition::Generic => "generics",
329328
ImplTraitPosition::ExternFnParam => "`extern fn` parameters",

compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

+15-8
Original file line numberDiff line numberDiff line change
@@ -858,7 +858,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
858858
use crate::session_diagnostics::CaptureVarCause::*;
859859
match kind {
860860
hir::ClosureKind::Coroutine(_) => MoveUseInCoroutine { var_span },
861-
hir::ClosureKind::Closure => MoveUseInClosure { var_span },
861+
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
862+
MoveUseInClosure { var_span }
863+
}
862864
}
863865
});
864866

@@ -905,7 +907,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
905907
hir::ClosureKind::Coroutine(_) => {
906908
BorrowUsePlaceCoroutine { place: desc_place, var_span, is_single_var: true }
907909
}
908-
hir::ClosureKind::Closure => {
910+
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
909911
BorrowUsePlaceClosure { place: desc_place, var_span, is_single_var: true }
910912
}
911913
}
@@ -1056,7 +1058,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
10561058
var_span,
10571059
is_single_var: true,
10581060
},
1059-
hir::ClosureKind::Closure => BorrowUsePlaceClosure {
1061+
hir::ClosureKind::Closure
1062+
| hir::ClosureKind::CoroutineClosure(_) => BorrowUsePlaceClosure {
10601063
place: desc_place,
10611064
var_span,
10621065
is_single_var: true,
@@ -1140,7 +1143,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
11401143
var_span,
11411144
is_single_var: false,
11421145
},
1143-
hir::ClosureKind::Closure => {
1146+
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
11441147
BorrowUsePlaceClosure { place: desc_place, var_span, is_single_var: false }
11451148
}
11461149
}
@@ -1158,7 +1161,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
11581161
hir::ClosureKind::Coroutine(_) => {
11591162
FirstBorrowUsePlaceCoroutine { place: borrow_place_desc, var_span }
11601163
}
1161-
hir::ClosureKind::Closure => {
1164+
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
11621165
FirstBorrowUsePlaceClosure { place: borrow_place_desc, var_span }
11631166
}
11641167
}
@@ -1175,7 +1178,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
11751178
hir::ClosureKind::Coroutine(_) => {
11761179
SecondBorrowUsePlaceCoroutine { place: desc_place, var_span }
11771180
}
1178-
hir::ClosureKind::Closure => {
1181+
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
11791182
SecondBorrowUsePlaceClosure { place: desc_place, var_span }
11801183
}
11811184
}
@@ -2942,7 +2945,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
29422945
use crate::session_diagnostics::CaptureVarCause::*;
29432946
match kind {
29442947
hir::ClosureKind::Coroutine(_) => BorrowUseInCoroutine { var_span },
2945-
hir::ClosureKind::Closure => BorrowUseInClosure { var_span },
2948+
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
2949+
BorrowUseInClosure { var_span }
2950+
}
29462951
}
29472952
});
29482953

@@ -2958,7 +2963,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
29582963
use crate::session_diagnostics::CaptureVarCause::*;
29592964
match kind {
29602965
hir::ClosureKind::Coroutine(_) => BorrowUseInCoroutine { var_span },
2961-
hir::ClosureKind::Closure => BorrowUseInClosure { var_span },
2966+
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
2967+
BorrowUseInClosure { var_span }
2968+
}
29622969
}
29632970
});
29642971

compiler/rustc_borrowck/src/diagnostics/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -614,7 +614,7 @@ impl UseSpans<'_> {
614614
PartialAssignment => AssignPartInCoroutine { path_span },
615615
});
616616
}
617-
hir::ClosureKind::Closure => {
617+
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
618618
err.subdiagnostic(match action {
619619
Borrow => BorrowInClosure { path_span },
620620
MatchOn | Use => UseInClosure { path_span },
@@ -1253,7 +1253,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
12531253
hir::ClosureKind::Coroutine(_) => {
12541254
CaptureVarCause::PartialMoveUseInCoroutine { var_span, is_partial }
12551255
}
1256-
hir::ClosureKind::Closure => {
1256+
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
12571257
CaptureVarCause::PartialMoveUseInClosure { var_span, is_partial }
12581258
}
12591259
})

compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1472,7 +1472,7 @@ fn suggest_ampmut<'tcx>(
14721472
}
14731473

14741474
fn is_closure_or_coroutine(ty: Ty<'_>) -> bool {
1475-
ty.is_closure() || ty.is_coroutine()
1475+
ty.is_closure() || ty.is_coroutine() || ty.is_coroutine_closure()
14761476
}
14771477

14781478
/// Given a field that needs to be mutable, returns a span where the " mut " could go.

compiler/rustc_borrowck/src/diagnostics/region_name.rs

+23-13
Original file line numberDiff line numberDiff line change
@@ -324,31 +324,32 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
324324
ty::BoundRegionKind::BrEnv => {
325325
let def_ty = self.regioncx.universal_regions().defining_ty;
326326

327-
let DefiningTy::Closure(_, args) = def_ty else {
328-
// Can't have BrEnv in functions, constants or coroutines.
329-
bug!("BrEnv outside of closure.");
327+
let closure_kind = match def_ty {
328+
DefiningTy::Closure(_, args) => args.as_closure().kind(),
329+
DefiningTy::CoroutineClosure(_, args) => args.as_coroutine_closure().kind(),
330+
_ => {
331+
// Can't have BrEnv in functions, constants or coroutines.
332+
bug!("BrEnv outside of closure.");
333+
}
330334
};
331335
let hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }) =
332336
tcx.hir().expect_expr(self.mir_hir_id()).kind
333337
else {
334338
bug!("Closure is not defined by a closure expr");
335339
};
336340
let region_name = self.synthesize_region_name();
337-
338-
let closure_kind_ty = args.as_closure().kind_ty();
339-
let note = match closure_kind_ty.to_opt_closure_kind() {
340-
Some(ty::ClosureKind::Fn) => {
341+
let note = match closure_kind {
342+
ty::ClosureKind::Fn => {
341343
"closure implements `Fn`, so references to captured variables \
342344
can't escape the closure"
343345
}
344-
Some(ty::ClosureKind::FnMut) => {
346+
ty::ClosureKind::FnMut => {
345347
"closure implements `FnMut`, so references to captured variables \
346348
can't escape the closure"
347349
}
348-
Some(ty::ClosureKind::FnOnce) => {
350+
ty::ClosureKind::FnOnce => {
349351
bug!("BrEnv in a `FnOnce` closure");
350352
}
351-
None => bug!("Closure kind not inferred in borrow check"),
352353
};
353354

354355
Some(RegionName {
@@ -692,7 +693,10 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
692693
hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
693694
hir::CoroutineDesugaring::Async,
694695
hir::CoroutineSource::Closure,
695-
)) => " of async closure",
696+
))
697+
| hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async) => {
698+
" of async closure"
699+
}
696700

697701
hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
698702
hir::CoroutineDesugaring::Async,
@@ -719,7 +723,10 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
719723
hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
720724
hir::CoroutineDesugaring::Gen,
721725
hir::CoroutineSource::Closure,
722-
)) => " of gen closure",
726+
))
727+
| hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Gen) => {
728+
" of gen closure"
729+
}
723730

724731
hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
725732
hir::CoroutineDesugaring::Gen,
@@ -743,7 +750,10 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
743750
hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
744751
hir::CoroutineDesugaring::AsyncGen,
745752
hir::CoroutineSource::Closure,
746-
)) => " of async gen closure",
753+
))
754+
| hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::AsyncGen) => {
755+
" of async gen closure"
756+
}
747757

748758
hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
749759
hir::CoroutineDesugaring::AsyncGen,

compiler/rustc_borrowck/src/lib.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#![allow(internal_features)]
44
#![feature(rustdoc_internals)]
55
#![doc(rust_logo)]
6+
#![feature(assert_matches)]
67
#![feature(associated_type_bounds)]
78
#![feature(box_patterns)]
89
#![feature(let_chains)]
@@ -1303,7 +1304,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
13031304
// moved into the closure and subsequently used by the closure,
13041305
// in order to populate our used_mut set.
13051306
match **aggregate_kind {
1306-
AggregateKind::Closure(def_id, _) | AggregateKind::Coroutine(def_id, _) => {
1307+
AggregateKind::Closure(def_id, _)
1308+
| AggregateKind::CoroutineClosure(def_id, _)
1309+
| AggregateKind::Coroutine(def_id, _) => {
13071310
let def_id = def_id.expect_local();
13081311
let BorrowCheckResult { used_mut_upvars, .. } =
13091312
self.infcx.tcx.mir_borrowck(def_id);
@@ -1609,6 +1612,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
16091612
| ty::FnPtr(_)
16101613
| ty::Dynamic(_, _, _)
16111614
| ty::Closure(_, _)
1615+
| ty::CoroutineClosure(_, _)
16121616
| ty::Coroutine(_, _)
16131617
| ty::CoroutineWitness(..)
16141618
| ty::Never
@@ -1633,7 +1637,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
16331637
return;
16341638
}
16351639
}
1636-
ty::Closure(_, _) | ty::Coroutine(_, _) | ty::Tuple(_) => (),
1640+
ty::Closure(..)
1641+
| ty::CoroutineClosure(..)
1642+
| ty::Coroutine(_, _)
1643+
| ty::Tuple(_) => (),
16371644
ty::Bool
16381645
| ty::Char
16391646
| ty::Int(_)

compiler/rustc_borrowck/src/path_utils.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ pub(crate) fn is_upvar_field_projection<'tcx>(
164164
match place_ref.last_projection() {
165165
Some((place_base, ProjectionElem::Field(field, _ty))) => {
166166
let base_ty = place_base.ty(body, tcx).ty;
167-
if (base_ty.is_closure() || base_ty.is_coroutine())
167+
if (base_ty.is_closure() || base_ty.is_coroutine() || base_ty.is_coroutine_closure())
168168
&& (!by_ref || upvars[field.index()].is_by_ref())
169169
{
170170
Some(field)

0 commit comments

Comments
 (0)