adjust desugaring for async fn to correct drop order
Old desugaring, given a user function body { $stmts; $expr }
```
{
let $param_pattern0 = $raw_param0;
...
let $param_patternN = $raw_paramN;
$stmts;
$expr
}
```
New desugaring:
```
{
let $param_pattern0 = $raw_param0;
...
let $param_patternN = $raw_paramN;
drop-temps {
$stmts;
$expr
}
}
```
The drop-temps is an internal bit of HIR that drops temporaries from
the resulting expression, but it should be equivalent to `return {
$stmts; $expr }`.
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index 58789a1..492029e 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -2682,12 +2682,8 @@
bounds.iter().map(|bound| self.lower_param_bound(bound, itctx.reborrow())).collect()
}
- fn lower_block_with_stmts(
- &mut self,
- b: &Block,
- targeted_by_break: bool,
- mut stmts: Vec<hir::Stmt>,
- ) -> P<hir::Block> {
+ fn lower_block(&mut self, b: &Block, targeted_by_break: bool) -> P<hir::Block> {
+ let mut stmts = vec![];
let mut expr = None;
for (index, stmt) in b.stmts.iter().enumerate() {
@@ -2712,10 +2708,6 @@
})
}
- fn lower_block(&mut self, b: &Block, targeted_by_break: bool) -> P<hir::Block> {
- self.lower_block_with_stmts(b, targeted_by_break, vec![])
- }
-
fn lower_pat(&mut self, p: &Pat) -> P<hir::Pat> {
let node = match p.node {
PatKind::Wild => hir::PatKind::Wild,
diff --git a/src/librustc/hir/lowering/expr.rs b/src/librustc/hir/lowering/expr.rs
index a46cdab..87462e9 100644
--- a/src/librustc/hir/lowering/expr.rs
+++ b/src/librustc/hir/lowering/expr.rs
@@ -1310,7 +1310,7 @@
/// `{ let _t = $expr; _t }` but should provide better compile-time performance.
///
/// The drop order can be important in e.g. `if expr { .. }`.
- fn expr_drop_temps(
+ pub(super) fn expr_drop_temps(
&mut self,
span: Span,
expr: P<hir::Expr>,
diff --git a/src/librustc/hir/lowering/item.rs b/src/librustc/hir/lowering/item.rs
index 5f82e42..1e621f7 100644
--- a/src/librustc/hir/lowering/item.rs
+++ b/src/librustc/hir/lowering/item.rs
@@ -1219,8 +1219,44 @@
let async_expr = this.make_async_expr(
CaptureBy::Value, closure_id, None, body.span,
|this| {
- let body = this.lower_block_with_stmts(body, false, statements);
- this.expr_block(body, ThinVec::new())
+ // Create a block from the user's function body:
+ let user_body = this.lower_block(body, false);
+ let user_body = this.expr_block(user_body, ThinVec::new());
+
+ // Transform into `drop-temps { <user-body> }`, an expression:
+ let desugared_span = this.mark_span_with_reason(
+ DesugaringKind::Async,
+ user_body.span,
+ None,
+ );
+ let user_body = this.expr_drop_temps(
+ desugared_span,
+ P(user_body),
+ ThinVec::new(),
+ );
+
+ // Create a block like
+ //
+ // ```
+ // {
+ // let $param_pattern = $raw_param;
+ // ...
+ // drop-temps { <user-body> }
+ // }
+ // ```
+ //
+ // This construction is carefully calibrated to
+ // get the drop-order correct. In particular, the
+ // drop-temps ensures that any temporaries in the
+ // tail expression of `<user-body>` are dropped
+ // *before* the parameters are dropped (see
+ // rust-lang/rust#64512).
+ let body = this.block_all(
+ desugared_span,
+ statements.into(),
+ Some(P(user_body)),
+ );
+ this.expr_block(P(body), ThinVec::new())
});
(HirVec::from(parameters), this.expr(body.span, async_expr, ThinVec::new()))
})