Skip to content

Commit 44dcf11

Browse files
committed
Auto merge of rust-lang#120361 - compiler-errors:async-closures, r=<try>
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 fb4bca0 + e9c9d23 commit 44dcf11

File tree

182 files changed

+3640
-369
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

+3640
-369
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};
@@ -1027,30 +1028,27 @@ impl<'hir> LoweringContext<'_, 'hir> {
10271028
fn_decl_span: Span,
10281029
fn_arg_span: Span,
10291030
) -> hir::ExprKind<'hir> {
1030-
if let &ClosureBinder::For { span, .. } = binder {
1031-
self.dcx().emit_err(NotSupportedForLifetimeBinderAsyncClosure { span });
1032-
}
1033-
10341031
let (binder_clause, generic_params) = self.lower_closure_binder(binder);
10351032

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

10561054
let hir_id = this.lower_node_id(coroutine_kind.closure_id());
@@ -1061,15 +1059,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
10611059
body_id
10621060
});
10631061

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

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

compiler/rustc_ast_lowering/src/item.rs

+6-7
Original file line numberDiff line numberDiff line change
@@ -1089,7 +1089,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
10891089
body.span,
10901090
coroutine_kind,
10911091
hir::CoroutineSource::Fn,
1092-
None,
10931092
);
10941093

10951094
// FIXME(async_fn_track_caller): Can this be moved above?
@@ -1111,7 +1110,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
11111110
body_span: Span,
11121111
coroutine_kind: CoroutineKind,
11131112
coroutine_source: hir::CoroutineSource,
1114-
return_type_hint: Option<hir::FnRetTy<'hir>>,
11151113
) -> (&'hir [hir::Param<'hir>], hir::Expr<'hir>) {
11161114
let mut parameters: Vec<hir::Param<'_>> = Vec::new();
11171115
let mut statements: Vec<hir::Stmt<'_>> = Vec::new();
@@ -1281,12 +1279,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
12811279
};
12821280
let closure_id = coroutine_kind.closure_id();
12831281
let coroutine_expr = self.make_desugared_coroutine_expr(
1284-
// FIXME(async_closures): This should only move locals,
1285-
// and not upvars. Capturing closure upvars by ref doesn't
1286-
// work right now anyways, so whatever.
1287-
CaptureBy::Value { move_kw: rustc_span::DUMMY_SP },
1282+
// The default capture mode here is by-ref. Later on during upvar analysis,
1283+
// we will force the captured arguments to by-move, but for async closures,
1284+
// we want to make sure that we avoid unnecessarily moving captures, or else
1285+
// all async closures would default to `FnOnce` as their calling mode.
1286+
CaptureBy::Ref,
12881287
closure_id,
1289-
return_type_hint,
1288+
None,
12901289
body_span,
12911290
desugaring_kind,
12921291
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)]
@@ -1304,7 +1305,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
13041305
// moved into the closure and subsequently used by the closure,
13051306
// in order to populate our used_mut set.
13061307
match **aggregate_kind {
1307-
AggregateKind::Closure(def_id, _) | AggregateKind::Coroutine(def_id, _) => {
1308+
AggregateKind::Closure(def_id, _)
1309+
| AggregateKind::CoroutineClosure(def_id, _)
1310+
| AggregateKind::Coroutine(def_id, _) => {
13081311
let def_id = def_id.expect_local();
13091312
let BorrowCheckResult { used_mut_upvars, .. } =
13101313
self.infcx.tcx.mir_borrowck(def_id);
@@ -1610,6 +1613,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
16101613
| ty::FnPtr(_)
16111614
| ty::Dynamic(_, _, _)
16121615
| ty::Closure(_, _)
1616+
| ty::CoroutineClosure(_, _)
16131617
| ty::Coroutine(_, _)
16141618
| ty::CoroutineWitness(..)
16151619
| ty::Never
@@ -1634,7 +1638,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
16341638
return;
16351639
}
16361640
}
1637-
ty::Closure(_, _) | ty::Coroutine(_, _) | ty::Tuple(_) => (),
1641+
ty::Closure(..)
1642+
| ty::CoroutineClosure(..)
1643+
| ty::Coroutine(_, _)
1644+
| ty::Tuple(_) => (),
16381645
ty::Bool
16391646
| ty::Char
16401647
| 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)