@@ -945,12 +945,19 @@ impl<'db> ExprCollector<'db> {
945945 } )
946946 }
947947
948- /// An `async fn` needs to capture all parameters in the generated `async` block, even if they have
949- /// non-captured patterns such as wildcards (to ensure consistent drop order).
950- fn lower_async_fn ( & mut self , params : & mut Vec < PatId > , body : ExprId ) -> ExprId {
948+ /// Lowers a desugared coroutine body after moving all of the arguments
949+ /// into the body. This is to make sure that the future actually owns the
950+ /// arguments that are passed to the function, and to ensure things like
951+ /// drop order are stable.
952+ fn lower_async_block_with_moved_arguments (
953+ & mut self ,
954+ params : & mut [ PatId ] ,
955+ body : ExprId ,
956+ coroutine_source : CoroutineSource ,
957+ ) -> ExprId {
951958 let mut statements = Vec :: new ( ) ;
952959 for param in params {
953- let name = match self . store . pats [ * param] {
960+ let ( name, hygiene ) = match self . store . pats [ * param] {
954961 Pat :: Bind { id, .. }
955962 if matches ! (
956963 self . store. bindings[ id] . mode,
@@ -962,14 +969,16 @@ impl<'db> ExprCollector<'db> {
962969 }
963970 Pat :: Bind { id, .. } => {
964971 // If this is a `ref` binding, we can't leave it as is but we can at least reuse the name, for better display.
965- self . store . bindings [ id] . name . clone ( )
972+ ( self . store . bindings [ id] . name . clone ( ) , self . store . bindings [ id ] . hygiene )
966973 }
967- _ => self . generate_new_name ( ) ,
974+ _ => ( self . generate_new_name ( ) , HygieneId :: ROOT ) ,
968975 } ;
969- let binding_id =
970- self . alloc_binding ( name. clone ( ) , BindingAnnotation :: Mutable , HygieneId :: ROOT ) ;
976+ let binding_id = self . alloc_binding ( name. clone ( ) , BindingAnnotation :: Mutable , hygiene) ;
971977 let pat_id = self . alloc_pat_desugared ( Pat :: Bind { id : binding_id, subpat : None } ) ;
972978 let expr = self . alloc_expr_desugared ( Expr :: Path ( name. into ( ) ) ) ;
979+ if !hygiene. is_root ( ) {
980+ self . store . ident_hygiene . insert ( expr. into ( ) , hygiene) ;
981+ }
973982 statements. push ( Statement :: Let {
974983 pat : * param,
975984 type_ref : None ,
@@ -980,12 +989,17 @@ impl<'db> ExprCollector<'db> {
980989 }
981990
982991 let async_ = self . async_block (
983- CoroutineSource :: Fn ,
984- CaptureBy :: Value ,
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 ,
985998 None ,
986999 statements. into_boxed_slice ( ) ,
9871000 Some ( body) ,
9881001 ) ;
1002+ // It's important that this comes last, see the lowering of async closures for why.
9891003 self . alloc_expr_desugared ( async_)
9901004 }
9911005
@@ -1010,14 +1024,18 @@ impl<'db> ExprCollector<'db> {
10101024
10111025 fn collect (
10121026 & mut self ,
1013- params : & mut Vec < PatId > ,
1027+ params : & mut [ PatId ] ,
10141028 expr : Option < ast:: Expr > ,
10151029 awaitable : Awaitable ,
10161030 ) -> ExprId {
10171031 self . awaitable_context . replace ( awaitable) ;
10181032 self . with_label_rib ( RibKind :: Closure , |this| {
10191033 let body = this. collect_expr_opt ( expr) ;
1020- if awaitable == Awaitable :: Yes { this. lower_async_fn ( params, body) } else { body }
1034+ if awaitable == Awaitable :: Yes {
1035+ this. lower_async_block_with_moved_arguments ( params, body, CoroutineSource :: Fn )
1036+ } else {
1037+ body
1038+ }
10211039 } )
10221040 }
10231041
@@ -1407,9 +1425,11 @@ impl<'db> ExprCollector<'db> {
14071425 }
14081426 }
14091427 ast:: Expr :: ClosureExpr ( e) => self . with_label_rib ( RibKind :: Closure , |this| {
1410- this. with_binding_owner ( |this| {
1428+ this. with_binding_owner_and_return ( |this| {
14111429 let mut args = Vec :: new ( ) ;
14121430 let mut arg_types = Vec :: new ( ) ;
1431+ // For coroutine closures, the body, aka. the coroutine is the bindings owner, and not the closure.
1432+ let mut body_is_bindings_owner = false ;
14131433 if let Some ( pl) = e. param_list ( ) {
14141434 let num_params = pl. params ( ) . count ( ) ;
14151435 args. reserve_exact ( num_params) ;
@@ -1448,18 +1468,12 @@ impl<'db> ExprCollector<'db> {
14481468 } else if e. async_token ( ) . is_some ( ) {
14491469 // It's important that this expr is allocated immediately before the closure.
14501470 // We rely on it for `coroutine_for_closure()`.
1451- body = this. alloc_expr_desugared ( Expr :: Closure {
1452- args : Box :: default ( ) ,
1453- arg_types : Box :: default ( ) ,
1454- ret_type : None ,
1471+ body = this. lower_async_block_with_moved_arguments (
1472+ & mut args,
14551473 body,
1456- closure_kind : ClosureKind :: AsyncBlock {
1457- source : CoroutineSource :: Closure ,
1458- } ,
1459- // The block may need to capture by move, but we cannot know it now.
1460- // It will be fixed in capture analysis.
1461- capture_by : CaptureBy :: Ref ,
1462- } ) ;
1474+ CoroutineSource :: Closure ,
1475+ ) ;
1476+ body_is_bindings_owner = true ;
14631477
14641478 ClosureKind :: AsyncClosure
14651479 } else {
@@ -1469,7 +1483,7 @@ impl<'db> ExprCollector<'db> {
14691483 if e. move_token ( ) . is_some ( ) { CaptureBy :: Value } else { CaptureBy :: Ref } ;
14701484 this. is_lowering_coroutine = prev_is_lowering_coroutine;
14711485 this. current_try_block = prev_try_block;
1472- this. alloc_expr (
1486+ let closure = this. alloc_expr (
14731487 Expr :: Closure {
14741488 args : args. into ( ) ,
14751489 arg_types : arg_types. into ( ) ,
@@ -1479,7 +1493,9 @@ impl<'db> ExprCollector<'db> {
14791493 capture_by,
14801494 } ,
14811495 syntax_ptr,
1482- )
1496+ ) ;
1497+
1498+ ( if body_is_bindings_owner { body } else { closure } , closure)
14831499 } )
14841500 } ) ,
14851501 ast:: Expr :: BinExpr ( e) => {
@@ -1781,13 +1797,24 @@ impl<'db> ExprCollector<'db> {
17811797 }
17821798 }
17831799
1784- fn with_binding_owner ( & mut self , create_expr : impl FnOnce ( & mut Self ) -> ExprId ) -> ExprId {
1800+ /// The callback should return two exprs: the first is the bindings owner, the second is the expr to return.
1801+ fn with_binding_owner_and_return (
1802+ & mut self ,
1803+ create_expr : impl FnOnce ( & mut Self ) -> ( ExprId , ExprId ) ,
1804+ ) -> ExprId {
17851805 let prev_unowned_bindings_len = self . unowned_bindings . len ( ) ;
1786- let expr_id = create_expr ( self ) ;
1806+ let ( bindings_owner , expr_to_return ) = create_expr ( self ) ;
17871807 for binding in self . unowned_bindings . drain ( prev_unowned_bindings_len..) {
1788- self . store . binding_owners . insert ( binding, expr_id ) ;
1808+ self . store . binding_owners . insert ( binding, bindings_owner ) ;
17891809 }
1790- expr_id
1810+ expr_to_return
1811+ }
1812+
1813+ fn with_binding_owner ( & mut self , create_expr : impl FnOnce ( & mut Self ) -> ExprId ) -> ExprId {
1814+ self . with_binding_owner_and_return ( move |this| {
1815+ let expr = create_expr ( this) ;
1816+ ( expr, expr)
1817+ } )
17911818 }
17921819
17931820 /// Desugar `try { <stmts>; <expr> }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(<expr>) }`,
0 commit comments