Rollup merge of #60437 - davidtwco:issue-60236, r=nikomatsakis
Ensure that drop order of `async fn` matches `fn` and that users cannot refer to generated arguments. Fixes #60236 and fixes #60438. This PR modifies the lowering of `async fn` arguments so that the drop order matches the equivalent `fn`. Previously, async function arguments were lowered as shown below: async fn foo(<pattern>: <ty>) { async move { } } // <-- dropped as you "exit" the fn // ...becomes... fn foo(__arg0: <ty>) { async move { let <pattern> = __arg0; } // <-- dropped as you "exit" the async block } After this PR, async function arguments will be lowered as: async fn foo(<pattern>: <ty>, <pattern>: <ty>, <pattern>: <ty>) { async move { } } // <-- dropped as you "exit" the fn // ...becomes... fn foo(__arg0: <ty>, __arg1: <ty>, __arg2: <ty>) { async move { let __arg2 = __arg2; let <pattern> = __arg2; let __arg1 = __arg1; let <pattern> = __arg1; let __arg0 = __arg0; let <pattern> = __arg0; } // <-- dropped as you "exit" the async block } If `<pattern>` is a simple ident, then it is lowered to a single `let <pattern> = <pattern>;` statement as an optimization. This PR also stops users from referring to the generated `__argN` identifiers. r? @nikomatsakis
This commit is contained in:
commit
16939a50ea
13 changed files with 505 additions and 232 deletions
|
@ -8720,13 +8720,39 @@ impl<'a> Parser<'a> {
|
|||
|
||||
// Construct a name for our temporary argument.
|
||||
let name = format!("__arg{}", index);
|
||||
let ident = Ident::from_str(&name);
|
||||
let ident = Ident::from_str(&name).gensym();
|
||||
|
||||
// Check if this is a ident pattern, if so, we can optimize and avoid adding a
|
||||
// `let <pat> = __argN;` statement, instead just adding a `let <pat> = <pat>;`
|
||||
// statement.
|
||||
let (ident, is_simple_pattern) = match input.pat.node {
|
||||
PatKind::Ident(_, ident, _) => (ident, true),
|
||||
_ => (ident, false),
|
||||
};
|
||||
|
||||
// Construct an argument representing `__argN: <ty>` to replace the argument of the
|
||||
// async function.
|
||||
let arg = Arg {
|
||||
ty: input.ty.clone(),
|
||||
id,
|
||||
// async function if it isn't a simple pattern.
|
||||
let arg = if is_simple_pattern {
|
||||
None
|
||||
} else {
|
||||
Some(Arg {
|
||||
ty: input.ty.clone(),
|
||||
id,
|
||||
pat: P(Pat {
|
||||
id,
|
||||
node: PatKind::Ident(
|
||||
BindingMode::ByValue(Mutability::Immutable), ident, None,
|
||||
),
|
||||
span,
|
||||
}),
|
||||
source: ArgSource::AsyncFn(input.pat.clone()),
|
||||
})
|
||||
};
|
||||
|
||||
// Construct a `let __argN = __argN;` statement to insert at the top of the
|
||||
// async closure. This makes sure that the argument is captured by the closure and
|
||||
// that the drop order is correct.
|
||||
let move_local = Local {
|
||||
pat: P(Pat {
|
||||
id,
|
||||
node: PatKind::Ident(
|
||||
|
@ -8734,13 +8760,6 @@ impl<'a> Parser<'a> {
|
|||
),
|
||||
span,
|
||||
}),
|
||||
source: ArgSource::AsyncFn(input.pat.clone()),
|
||||
};
|
||||
|
||||
// Construct a `let <pat> = __argN;` statement to insert at the top of the
|
||||
// async closure.
|
||||
let local = P(Local {
|
||||
pat: input.pat.clone(),
|
||||
// We explicitly do not specify the type for this statement. When the user's
|
||||
// argument type is `impl Trait` then this would require the
|
||||
// `impl_trait_in_bindings` feature to also be present for that same type to
|
||||
|
@ -8760,10 +8779,25 @@ impl<'a> Parser<'a> {
|
|||
span,
|
||||
attrs: ThinVec::new(),
|
||||
source: LocalSource::AsyncFn,
|
||||
});
|
||||
let stmt = Stmt { id, node: StmtKind::Local(local), span, };
|
||||
};
|
||||
|
||||
arguments.push(AsyncArgument { ident, arg, stmt });
|
||||
// Construct a `let <pat> = __argN;` statement to insert at the top of the
|
||||
// async closure if this isn't a simple pattern.
|
||||
let pat_stmt = if is_simple_pattern {
|
||||
None
|
||||
} else {
|
||||
Some(Stmt {
|
||||
id,
|
||||
node: StmtKind::Local(P(Local {
|
||||
pat: input.pat.clone(),
|
||||
..move_local.clone()
|
||||
})),
|
||||
span,
|
||||
})
|
||||
};
|
||||
|
||||
let move_stmt = Stmt { id, node: StmtKind::Local(P(move_local)), span };
|
||||
arguments.push(AsyncArgument { ident, arg, pat_stmt, move_stmt });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue