Skip to content

Commit 4a2fe44

Browse files
committed
Auto merge of #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:** #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 #120322, but that PR is minimal.
2 parents 037f515 + ed7fca1 commit 4a2fe44

File tree

180 files changed

+3643
-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.

180 files changed

+3643
-366
lines changed

compiler/rustc_ast_lowering/messages.ftl

-3
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,6 @@ ast_lowering_never_pattern_with_guard =
123123
a guard on a never pattern will never be run
124124
.suggestion = remove this guard
125125
126-
ast_lowering_not_supported_for_lifetime_binder_async_closure =
127-
`for<...>` binders on `async` closures are not currently supported
128-
129126
ast_lowering_previously_used_here = previously used here
130127
131128
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};
@@ -1028,30 +1029,27 @@ impl<'hir> LoweringContext<'_, 'hir> {
10281029
fn_decl_span: Span,
10291030
fn_arg_span: Span,
10301031
) -> hir::ExprKind<'hir> {
1031-
if let &ClosureBinder::For { span, .. } = binder {
1032-
self.dcx().emit_err(NotSupportedForLifetimeBinderAsyncClosure { span });
1033-
}
1034-
10351032
let (binder_clause, generic_params) = self.lower_closure_binder(binder);
10361033

1034+
assert_matches!(
1035+
coroutine_kind,
1036+
CoroutineKind::Async { .. },
1037+
"only async closures are supported currently"
1038+
);
1039+
10371040
let body = self.with_new_scopes(fn_decl_span, |this| {
1041+
let inner_decl =
1042+
FnDecl { inputs: decl.inputs.clone(), output: FnRetTy::Default(fn_decl_span) };
1043+
10381044
// Transform `async |x: u8| -> X { ... }` into
10391045
// `|x: u8| || -> X { ... }`.
10401046
let body_id = this.lower_body(|this| {
1041-
let async_ret_ty = if let FnRetTy::Ty(ty) = &decl.output {
1042-
let itctx = ImplTraitContext::Disallowed(ImplTraitPosition::AsyncBlock);
1043-
Some(hir::FnRetTy::Return(this.lower_ty(ty, &itctx)))
1044-
} else {
1045-
None
1046-
};
1047-
10481047
let (parameters, expr) = this.lower_coroutine_body_with_moved_arguments(
1049-
decl,
1048+
&inner_decl,
10501049
|this| this.with_new_scopes(fn_decl_span, |this| this.lower_expr_mut(body)),
10511050
body.span,
10521051
coroutine_kind,
10531052
hir::CoroutineSource::Closure,
1054-
async_ret_ty,
10551053
);
10561054

10571055
let hir_id = this.lower_node_id(coroutine_kind.closure_id());
@@ -1062,15 +1060,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
10621060
body_id
10631061
});
10641062

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

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

compiler/rustc_ast_lowering/src/item.rs

+6-7
Original file line numberDiff line numberDiff line change
@@ -1091,7 +1091,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
10911091
body.span,
10921092
coroutine_kind,
10931093
hir::CoroutineSource::Fn,
1094-
None,
10951094
);
10961095

10971096
// FIXME(async_fn_track_caller): Can this be moved above?
@@ -1113,7 +1112,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
11131112
body_span: Span,
11141113
coroutine_kind: CoroutineKind,
11151114
coroutine_source: hir::CoroutineSource,
1116-
return_type_hint: Option<hir::FnRetTy<'hir>>,
11171115
) -> (&'hir [hir::Param<'hir>], hir::Expr<'hir>) {
11181116
let mut parameters: Vec<hir::Param<'_>> = Vec::new();
11191117
let mut statements: Vec<hir::Stmt<'_>> = Vec::new();
@@ -1283,12 +1281,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
12831281
};
12841282
let closure_id = coroutine_kind.closure_id();
12851283
let coroutine_expr = self.make_desugared_coroutine_expr(
1286-
// FIXME(async_closures): This should only move locals,
1287-
// and not upvars. Capturing closure upvars by ref doesn't
1288-
// work right now anyways, so whatever.
1289-
CaptureBy::Value { move_kw: rustc_span::DUMMY_SP },
1284+
// The default capture mode here is by-ref. Later on during upvar analysis,
1285+
// we will force the captured arguments to by-move, but for async closures,
1286+
// we want to make sure that we avoid unnecessarily moving captures, or else
1287+
// all async closures would default to `FnOnce` as their calling mode.
1288+
CaptureBy::Ref,
12901289
closure_id,
1291-
return_type_hint,
1290+
None,
12921291
body_span,
12931292
desugaring_kind,
12941293
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)]
@@ -298,7 +299,6 @@ enum ImplTraitPosition {
298299
Path,
299300
Variable,
300301
Trait,
301-
AsyncBlock,
302302
Bound,
303303
Generic,
304304
ExternFnParam,
@@ -325,7 +325,6 @@ impl std::fmt::Display for ImplTraitPosition {
325325
ImplTraitPosition::Path => "paths",
326326
ImplTraitPosition::Variable => "the type of variable bindings",
327327
ImplTraitPosition::Trait => "traits",
328-
ImplTraitPosition::AsyncBlock => "async blocks",
329328
ImplTraitPosition::Bound => "bounds",
330329
ImplTraitPosition::Generic => "generics",
331330
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)