@@ -46,8 +46,8 @@ use crate::{
4646 } ,
4747 hir:: {
4848 Array , Binding , BindingAnnotation , BindingId , BindingProblems , CaptureBy , ClosureKind ,
49- CoroutineSource , Expr , ExprId , Item , Label , LabelId , Literal , MatchArm , Movability ,
50- OffsetOf , Pat , PatId , RecordFieldPat , RecordLitField , RecordSpread , Statement ,
49+ CoroutineKind , CoroutineSource , Expr , ExprId , Item , Label , LabelId , Literal , MatchArm ,
50+ Movability , OffsetOf , Pat , PatId , RecordFieldPat , RecordLitField , RecordSpread , Statement ,
5151 generics:: GenericParams ,
5252 } ,
5353 item_scope:: BuiltinShadowMode ,
@@ -71,6 +71,7 @@ pub(super) fn lower_body(
7171 parameters : Option < ast:: ParamList > ,
7272 body : Option < ast:: Expr > ,
7373 is_async_fn : bool ,
74+ is_gen_fn : bool ,
7475) -> ( Body , BodySourceMap ) {
7576 // We cannot leave the root span map empty and let any identifier from it be treated as root,
7677 // because when inside nested macros `SyntaxContextId`s from the outer macro will be interleaved
@@ -175,6 +176,8 @@ pub(super) fn lower_body(
175176 DefWithBodyId :: VariantId ( ..) => Awaitable :: No ( "enum variant" ) ,
176177 }
177178 } ,
179+ is_async_fn,
180+ is_gen_fn,
178181 ) ;
179182 collector. store . inference_roots = Some ( smallvec ! [ ( body_expr, RootExprOrigin :: BodyRoot ) ] ) ;
180183
@@ -375,12 +378,20 @@ pub(crate) fn lower_function(
375378 expr_collector. lower_type_ref_opt ( ret_type. ty ( ) , & mut ExprCollector :: impl_trait_allocator)
376379 } ) ;
377380
378- let return_type = if fn_. value . async_token ( ) . is_some ( ) {
379- let path = hir_expand:: mod_path:: path![ core:: future:: Future ] ;
381+ let return_type = if fn_. value . async_token ( ) . is_some ( ) || fn_. value . gen_token ( ) . is_some ( ) {
382+ let ( path, assoc_name) =
383+ match ( fn_. value . async_token ( ) . is_some ( ) , fn_. value . gen_token ( ) . is_some ( ) ) {
384+ ( true , true ) => {
385+ ( hir_expand:: mod_path:: path![ core:: async_iter:: AsyncIterator ] , sym:: Item )
386+ }
387+ ( true , false ) => ( hir_expand:: mod_path:: path![ core:: future:: Future ] , sym:: Output ) ,
388+ ( false , true ) => ( hir_expand:: mod_path:: path![ core:: iter:: Iterator ] , sym:: Item ) ,
389+ ( false , false ) => unreachable ! ( ) ,
390+ } ;
380391 let mut generic_args: Vec < _ > =
381392 std:: iter:: repeat_n ( None , path. segments ( ) . len ( ) - 1 ) . collect ( ) ;
382393 let binding = AssociatedTypeBinding {
383- name : Name :: new_symbol_root ( sym :: Output ) ,
394+ name : Name :: new_symbol_root ( assoc_name ) ,
384395 args : None ,
385396 type_ref : Some (
386397 return_type
@@ -949,10 +960,11 @@ impl<'db> ExprCollector<'db> {
949960 /// into the body. This is to make sure that the future actually owns the
950961 /// arguments that are passed to the function, and to ensure things like
951962 /// drop order are stable.
952- fn lower_async_block_with_moved_arguments (
963+ fn lower_coroutine_with_moved_arguments (
953964 & mut self ,
954965 params : & mut [ PatId ] ,
955966 body : ExprId ,
967+ kind : CoroutineKind ,
956968 coroutine_source : CoroutineSource ,
957969 ) -> ExprId {
958970 let mut statements = Vec :: new ( ) ;
@@ -988,24 +1000,81 @@ impl<'db> ExprCollector<'db> {
9881000 * param = pat_id;
9891001 }
9901002
991- let async_ = self . async_block (
992- coroutine_source,
993- // The default capture mode here is by-ref. Later on during upvar analysis,
994- // we will force the captured arguments to by-move, but for async closures,
995- // we want to make sure that we avoid unnecessarily moving captures, or else
996- // all async closures would default to `FnOnce` as their calling mode.
997- CaptureBy :: Ref ,
1003+ let capture_by = match kind {
1004+ // Async closures use by-ref by default so they don't all collapse to `FnOnce`.
1005+ CoroutineKind :: Async => CaptureBy :: Ref ,
1006+ CoroutineKind :: Gen | CoroutineKind :: AsyncGen => CaptureBy :: Value ,
1007+ } ;
1008+ let coroutine = self . desugared_coroutine_expr (
1009+ ClosureKind :: Coroutine { kind, source : coroutine_source } ,
1010+ capture_by,
9981011 None ,
9991012 statements. into_boxed_slice ( ) ,
10001013 Some ( body) ,
10011014 ) ;
10021015 // It's important that this comes last, see the lowering of async closures for why.
1003- self . alloc_expr_desugared ( async_)
1016+ self . alloc_expr_desugared ( coroutine)
1017+ }
1018+
1019+ /// Coroutine-like functions need to capture all parameters in the generated block, even if they have
1020+ /// non-captured patterns such as wildcards (to ensure consistent drop order).
1021+ fn lower_coroutine_fn (
1022+ & mut self ,
1023+ params : & mut [ PatId ] ,
1024+ body : ExprId ,
1025+ is_async_fn : bool ,
1026+ is_gen_fn : bool ,
1027+ ) -> ExprId {
1028+ let mut statements = Vec :: new ( ) ;
1029+ for param in params {
1030+ let ( name, hygiene) = match self . store . pats [ * param] {
1031+ Pat :: Bind { id, .. }
1032+ if matches ! (
1033+ self . store. bindings[ id] . mode,
1034+ BindingAnnotation :: Unannotated | BindingAnnotation :: Mutable
1035+ ) =>
1036+ {
1037+ continue ;
1038+ }
1039+ Pat :: Bind { id, .. } => {
1040+ ( self . store . bindings [ id] . name . clone ( ) , self . store . bindings [ id] . hygiene )
1041+ }
1042+ _ => ( self . generate_new_name ( ) , HygieneId :: ROOT ) ,
1043+ } ;
1044+ let binding_id = self . alloc_binding ( name. clone ( ) , BindingAnnotation :: Mutable , hygiene) ;
1045+ let pat_id = self . alloc_pat_desugared ( Pat :: Bind { id : binding_id, subpat : None } ) ;
1046+ let expr = self . alloc_expr_desugared ( Expr :: Path ( name. into ( ) ) ) ;
1047+ if !hygiene. is_root ( ) {
1048+ self . store . ident_hygiene . insert ( expr. into ( ) , hygiene) ;
1049+ }
1050+ statements. push ( Statement :: Let {
1051+ pat : * param,
1052+ type_ref : None ,
1053+ initializer : Some ( expr) ,
1054+ else_branch : None ,
1055+ } ) ;
1056+ * param = pat_id;
1057+ }
1058+
1059+ let kind = match ( is_async_fn, is_gen_fn) {
1060+ ( true , true ) => CoroutineKind :: AsyncGen ,
1061+ ( true , false ) => CoroutineKind :: Async ,
1062+ ( false , true ) => CoroutineKind :: Gen ,
1063+ ( false , false ) => unreachable ! ( ) ,
1064+ } ;
1065+ let coroutine = self . desugared_coroutine_expr (
1066+ ClosureKind :: Coroutine { kind, source : CoroutineSource :: Fn } ,
1067+ CaptureBy :: Value ,
1068+ None ,
1069+ statements. into_boxed_slice ( ) ,
1070+ Some ( body) ,
1071+ ) ;
1072+ self . alloc_expr_desugared ( coroutine)
10041073 }
10051074
1006- fn async_block (
1075+ fn desugared_coroutine_expr (
10071076 & mut self ,
1008- source : CoroutineSource ,
1077+ closure_kind : ClosureKind ,
10091078 capture_by : CaptureBy ,
10101079 id : Option < BlockId > ,
10111080 statements : Box < [ Statement ] > ,
@@ -1017,7 +1086,7 @@ impl<'db> ExprCollector<'db> {
10171086 arg_types : Box :: default ( ) ,
10181087 ret_type : None ,
10191088 body : block,
1020- closure_kind : ClosureKind :: AsyncBlock { source } ,
1089+ closure_kind,
10211090 capture_by,
10221091 }
10231092 }
@@ -1027,12 +1096,21 @@ impl<'db> ExprCollector<'db> {
10271096 params : & mut [ PatId ] ,
10281097 expr : Option < ast:: Expr > ,
10291098 awaitable : Awaitable ,
1099+ is_async_fn : bool ,
1100+ is_gen_fn : bool ,
10301101 ) -> ExprId {
10311102 self . awaitable_context . replace ( awaitable) ;
10321103 self . with_label_rib ( RibKind :: Closure , |this| {
10331104 let body = this. collect_expr_opt ( expr) ;
1034- if awaitable == Awaitable :: Yes {
1035- this. lower_async_block_with_moved_arguments ( params, body, CoroutineSource :: Fn )
1105+ if is_gen_fn {
1106+ this. lower_coroutine_fn ( params, body, is_async_fn, is_gen_fn)
1107+ } else if awaitable == Awaitable :: Yes {
1108+ this. lower_coroutine_with_moved_arguments (
1109+ params,
1110+ body,
1111+ CoroutineKind :: Async ,
1112+ CoroutineSource :: Fn ,
1113+ )
10361114 } else {
10371115 body
10381116 }
@@ -1191,8 +1269,51 @@ impl<'db> ExprCollector<'db> {
11911269 self . with_label_rib ( RibKind :: Closure , |this| {
11921270 this. with_awaitable_block ( Awaitable :: Yes , |this| {
11931271 this. collect_block_ ( e, |this, id, statements, tail| {
1194- this. async_block (
1195- CoroutineSource :: Block ,
1272+ this. desugared_coroutine_expr (
1273+ ClosureKind :: Coroutine {
1274+ kind : CoroutineKind :: Async ,
1275+ source : CoroutineSource :: Block ,
1276+ } ,
1277+ capture_by,
1278+ id,
1279+ statements,
1280+ tail,
1281+ )
1282+ } )
1283+ } )
1284+ } )
1285+ }
1286+ Some ( ast:: BlockModifier :: Gen ( _) ) => {
1287+ let capture_by =
1288+ if e. move_token ( ) . is_some ( ) { CaptureBy :: Value } else { CaptureBy :: Ref } ;
1289+ self . with_label_rib ( RibKind :: Closure , |this| {
1290+ this. with_awaitable_block ( Awaitable :: No ( "non-async gen block" ) , |this| {
1291+ this. collect_block_ ( e, |this, id, statements, tail| {
1292+ this. desugared_coroutine_expr (
1293+ ClosureKind :: Coroutine {
1294+ kind : CoroutineKind :: Gen ,
1295+ source : CoroutineSource :: Block ,
1296+ } ,
1297+ capture_by,
1298+ id,
1299+ statements,
1300+ tail,
1301+ )
1302+ } )
1303+ } )
1304+ } )
1305+ }
1306+ Some ( ast:: BlockModifier :: AsyncGen ( _) ) => {
1307+ let capture_by =
1308+ if e. move_token ( ) . is_some ( ) { CaptureBy :: Value } else { CaptureBy :: Ref } ;
1309+ self . with_label_rib ( RibKind :: Closure , |this| {
1310+ this. with_awaitable_block ( Awaitable :: Yes , |this| {
1311+ this. collect_block_ ( e, |this, id, statements, tail| {
1312+ this. desugared_coroutine_expr (
1313+ ClosureKind :: Coroutine {
1314+ kind : CoroutineKind :: AsyncGen ,
1315+ source : CoroutineSource :: Block ,
1316+ } ,
11961317 capture_by,
11971318 id,
11981319 statements,
@@ -1212,14 +1333,6 @@ impl<'db> ExprCollector<'db> {
12121333 } )
12131334 } )
12141335 }
1215- // FIXME
1216- Some ( ast:: BlockModifier :: AsyncGen ( _) ) => {
1217- self . with_awaitable_block ( Awaitable :: Yes , |this| this. collect_block ( e) )
1218- }
1219- Some ( ast:: BlockModifier :: Gen ( _) ) => self
1220- . with_awaitable_block ( Awaitable :: No ( "non-async gen block" ) , |this| {
1221- this. collect_block ( e)
1222- } ) ,
12231336 None => self . collect_block ( e) ,
12241337 } ,
12251338 ast:: Expr :: LoopExpr ( e) => {
@@ -1458,24 +1571,35 @@ impl<'db> ExprCollector<'db> {
14581571 let mut body = this
14591572 . with_awaitable_block ( awaitable, |this| this. collect_expr_opt ( e. body ( ) ) ) ;
14601573
1461- let closure_kind = if this. is_lowering_coroutine {
1462- let movability = if e. static_token ( ) . is_some ( ) {
1463- Movability :: Static
1464- } else {
1465- Movability :: Movable
1466- } ;
1467- ClosureKind :: Coroutine ( movability)
1574+ let closure_kind = if let Some ( kind) =
1575+ if e. async_token ( ) . is_some ( ) && e. gen_token ( ) . is_some ( )
1576+ {
1577+ Some ( CoroutineKind :: AsyncGen )
14681578 } else if e. async_token ( ) . is_some ( ) {
1579+ Some ( CoroutineKind :: Async )
1580+ } else if e. gen_token ( ) . is_some ( ) {
1581+ Some ( CoroutineKind :: Gen )
1582+ } else {
1583+ None
1584+ } {
14691585 // It's important that this expr is allocated immediately before the closure.
14701586 // We rely on it for `coroutine_for_closure()`.
1471- body = this. lower_async_block_with_moved_arguments (
1587+ body = this. lower_coroutine_with_moved_arguments (
14721588 & mut args,
14731589 body,
1590+ kind,
14741591 CoroutineSource :: Closure ,
14751592 ) ;
14761593 body_is_bindings_owner = true ;
14771594
1478- ClosureKind :: AsyncClosure
1595+ ClosureKind :: CoroutineClosure ( kind)
1596+ } else if this. is_lowering_coroutine {
1597+ let movability = if e. static_token ( ) . is_some ( ) {
1598+ Movability :: Static
1599+ } else {
1600+ Movability :: Movable
1601+ } ;
1602+ ClosureKind :: OldCoroutine ( movability)
14791603 } else {
14801604 ClosureKind :: Closure
14811605 } ;
0 commit comments