@@ -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
@@ -1450,18 +1468,11 @@ impl<'db> ExprCollector<'db> {
14501468 } else if e. async_token ( ) . is_some ( ) {
14511469 // It's important that this expr is allocated immediately before the closure.
14521470 // We rely on it for `coroutine_for_closure()`.
1453- body = this. alloc_expr_desugared ( Expr :: Closure {
1454- args : Box :: default ( ) ,
1455- arg_types : Box :: default ( ) ,
1456- ret_type : None ,
1471+ body = this. lower_async_block_with_moved_arguments (
1472+ & mut args,
14571473 body,
1458- closure_kind : ClosureKind :: AsyncBlock {
1459- source : CoroutineSource :: Closure ,
1460- } ,
1461- // The block may need to capture by move, but we cannot know it now.
1462- // It will be fixed in capture analysis.
1463- capture_by : CaptureBy :: Ref ,
1464- } ) ;
1474+ CoroutineSource :: Closure ,
1475+ ) ;
14651476 body_is_bindings_owner = true ;
14661477
14671478 ClosureKind :: AsyncClosure
0 commit comments