Skip to content

Commit 1f65562

Browse files
Introduce helper that deals with moving async args into the coroutine
1 parent 67e7b84 commit 1f65562

File tree

1 file changed

+191
-170
lines changed
  • compiler/rustc_ast_lowering/src

1 file changed

+191
-170
lines changed

compiler/rustc_ast_lowering/src/item.rs

Lines changed: 191 additions & 170 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,194 +1082,215 @@ impl<'hir> LoweringContext<'_, 'hir> {
10821082
let (Some(coroutine_kind), Some(body)) = (coroutine_kind, body) else {
10831083
return self.lower_fn_body_block(span, decl, body);
10841084
};
1085-
let closure_id = coroutine_kind.closure_id();
1086-
10871085
self.lower_body(|this| {
1088-
let mut parameters: Vec<hir::Param<'_>> = Vec::new();
1089-
let mut statements: Vec<hir::Stmt<'_>> = Vec::new();
1090-
1091-
// Async function parameters are lowered into the closure body so that they are
1092-
// captured and so that the drop order matches the equivalent non-async functions.
1093-
//
1094-
// from:
1095-
//
1096-
// async fn foo(<pattern>: <ty>, <pattern>: <ty>, <pattern>: <ty>) {
1097-
// <body>
1098-
// }
1099-
//
1100-
// into:
1101-
//
1102-
// fn foo(__arg0: <ty>, __arg1: <ty>, __arg2: <ty>) {
1103-
// async move {
1104-
// let __arg2 = __arg2;
1105-
// let <pattern> = __arg2;
1106-
// let __arg1 = __arg1;
1107-
// let <pattern> = __arg1;
1108-
// let __arg0 = __arg0;
1109-
// let <pattern> = __arg0;
1110-
// drop-temps { <body> } // see comments later in fn for details
1111-
// }
1112-
// }
1113-
//
1114-
// If `<pattern>` is a simple ident, then it is lowered to a single
1115-
// `let <pattern> = <pattern>;` statement as an optimization.
1116-
//
1117-
// Note that the body is embedded in `drop-temps`; an
1118-
// equivalent desugaring would be `return { <body>
1119-
// };`. The key point is that we wish to drop all the
1120-
// let-bound variables and temporaries created in the body
1121-
// (and its tail expression!) before we drop the
1122-
// parameters (c.f. rust-lang/rust#64512).
1123-
for (index, parameter) in decl.inputs.iter().enumerate() {
1124-
let parameter = this.lower_param(parameter);
1125-
let span = parameter.pat.span;
1126-
1127-
// Check if this is a binding pattern, if so, we can optimize and avoid adding a
1128-
// `let <pat> = __argN;` statement. In this case, we do not rename the parameter.
1129-
let (ident, is_simple_parameter) = match parameter.pat.kind {
1130-
hir::PatKind::Binding(hir::BindingAnnotation(ByRef::No, _), _, ident, _) => {
1131-
(ident, true)
1132-
}
1133-
// For `ref mut` or wildcard arguments, we can't reuse the binding, but
1134-
// we can keep the same name for the parameter.
1135-
// This lets rustdoc render it correctly in documentation.
1136-
hir::PatKind::Binding(_, _, ident, _) => (ident, false),
1137-
hir::PatKind::Wild => {
1138-
(Ident::with_dummy_span(rustc_span::symbol::kw::Underscore), false)
1139-
}
1140-
_ => {
1141-
// Replace the ident for bindings that aren't simple.
1142-
let name = format!("__arg{index}");
1143-
let ident = Ident::from_str(&name);
1144-
1145-
(ident, false)
1146-
}
1147-
};
1148-
1149-
let desugared_span = this.mark_span_with_reason(DesugaringKind::Async, span, None);
1150-
1151-
// Construct a parameter representing `__argN: <ty>` to replace the parameter of the
1152-
// async function.
1153-
//
1154-
// If this is the simple case, this parameter will end up being the same as the
1155-
// original parameter, but with a different pattern id.
1156-
let stmt_attrs = this.attrs.get(&parameter.hir_id.local_id).copied();
1157-
let (new_parameter_pat, new_parameter_id) = this.pat_ident(desugared_span, ident);
1158-
let new_parameter = hir::Param {
1159-
hir_id: parameter.hir_id,
1160-
pat: new_parameter_pat,
1161-
ty_span: this.lower_span(parameter.ty_span),
1162-
span: this.lower_span(parameter.span),
1163-
};
1086+
let (parameters, expr) = this.lower_coroutine_body_with_moved_arguments(
1087+
decl,
1088+
body,
1089+
coroutine_kind,
1090+
CaptureBy::Value { move_kw: rustc_span::DUMMY_SP },
1091+
);
11641092

1165-
if is_simple_parameter {
1166-
// If this is the simple case, then we only insert one statement that is
1167-
// `let <pat> = <pat>;`. We re-use the original argument's pattern so that
1168-
// `HirId`s are densely assigned.
1169-
let expr = this.expr_ident(desugared_span, ident, new_parameter_id);
1170-
let stmt = this.stmt_let_pat(
1171-
stmt_attrs,
1172-
desugared_span,
1173-
Some(expr),
1174-
parameter.pat,
1175-
hir::LocalSource::AsyncFn,
1176-
);
1177-
statements.push(stmt);
1178-
} else {
1179-
// If this is not the simple case, then we construct two statements:
1180-
//
1181-
// ```
1182-
// let __argN = __argN;
1183-
// let <pat> = __argN;
1184-
// ```
1185-
//
1186-
// The first statement moves the parameter into the closure and thus ensures
1187-
// that the drop order is correct.
1188-
//
1189-
// The second statement creates the bindings that the user wrote.
1190-
1191-
// Construct the `let mut __argN = __argN;` statement. It must be a mut binding
1192-
// because the user may have specified a `ref mut` binding in the next
1193-
// statement.
1194-
let (move_pat, move_id) = this.pat_ident_binding_mode(
1195-
desugared_span,
1196-
ident,
1197-
hir::BindingAnnotation::MUT,
1198-
);
1199-
let move_expr = this.expr_ident(desugared_span, ident, new_parameter_id);
1200-
let move_stmt = this.stmt_let_pat(
1201-
None,
1202-
desugared_span,
1203-
Some(move_expr),
1204-
move_pat,
1205-
hir::LocalSource::AsyncFn,
1206-
);
1093+
// FIXME(async_fn_track_caller): Can this be moved above?
1094+
let hir_id = this.lower_node_id(coroutine_kind.closure_id());
1095+
this.maybe_forward_track_caller(body.span, fn_id, hir_id);
12071096

1208-
// Construct the `let <pat> = __argN;` statement. We re-use the original
1209-
// parameter's pattern so that `HirId`s are densely assigned.
1210-
let pattern_expr = this.expr_ident(desugared_span, ident, move_id);
1211-
let pattern_stmt = this.stmt_let_pat(
1212-
stmt_attrs,
1213-
desugared_span,
1214-
Some(pattern_expr),
1215-
parameter.pat,
1216-
hir::LocalSource::AsyncFn,
1217-
);
1097+
(parameters, expr)
1098+
})
1099+
}
12181100

1219-
statements.push(move_stmt);
1220-
statements.push(pattern_stmt);
1221-
};
1101+
/// Lowers a desugared coroutine body after moving all of the arguments
1102+
/// into the body. This makes sure that
1103+
fn lower_coroutine_body_with_moved_arguments(
1104+
&mut self,
1105+
decl: &FnDecl,
1106+
body: &Block,
1107+
coroutine_kind: CoroutineKind,
1108+
capture_clause: CaptureBy,
1109+
) -> (&'hir [hir::Param<'hir>], hir::Expr<'hir>) {
1110+
let mut parameters: Vec<hir::Param<'_>> = Vec::new();
1111+
let mut statements: Vec<hir::Stmt<'_>> = Vec::new();
1112+
1113+
// Async function parameters are lowered into the closure body so that they are
1114+
// captured and so that the drop order matches the equivalent non-async functions.
1115+
//
1116+
// from:
1117+
//
1118+
// async fn foo(<pattern>: <ty>, <pattern>: <ty>, <pattern>: <ty>) {
1119+
// <body>
1120+
// }
1121+
//
1122+
// into:
1123+
//
1124+
// fn foo(__arg0: <ty>, __arg1: <ty>, __arg2: <ty>) {
1125+
// async move {
1126+
// let __arg2 = __arg2;
1127+
// let <pattern> = __arg2;
1128+
// let __arg1 = __arg1;
1129+
// let <pattern> = __arg1;
1130+
// let __arg0 = __arg0;
1131+
// let <pattern> = __arg0;
1132+
// drop-temps { <body> } // see comments later in fn for details
1133+
// }
1134+
// }
1135+
//
1136+
// If `<pattern>` is a simple ident, then it is lowered to a single
1137+
// `let <pattern> = <pattern>;` statement as an optimization.
1138+
//
1139+
// Note that the body is embedded in `drop-temps`; an
1140+
// equivalent desugaring would be `return { <body>
1141+
// };`. The key point is that we wish to drop all the
1142+
// let-bound variables and temporaries created in the body
1143+
// (and its tail expression!) before we drop the
1144+
// parameters (c.f. rust-lang/rust#64512).
1145+
for (index, parameter) in decl.inputs.iter().enumerate() {
1146+
let parameter = self.lower_param(parameter);
1147+
let span = parameter.pat.span;
1148+
1149+
// Check if this is a binding pattern, if so, we can optimize and avoid adding a
1150+
// `let <pat> = __argN;` statement. In this case, we do not rename the parameter.
1151+
let (ident, is_simple_parameter) = match parameter.pat.kind {
1152+
hir::PatKind::Binding(hir::BindingAnnotation(ByRef::No, _), _, ident, _) => {
1153+
(ident, true)
1154+
}
1155+
// For `ref mut` or wildcard arguments, we can't reuse the binding, but
1156+
// we can keep the same name for the parameter.
1157+
// This lets rustdoc render it correctly in documentation.
1158+
hir::PatKind::Binding(_, _, ident, _) => (ident, false),
1159+
hir::PatKind::Wild => {
1160+
(Ident::with_dummy_span(rustc_span::symbol::kw::Underscore), false)
1161+
}
1162+
_ => {
1163+
// Replace the ident for bindings that aren't simple.
1164+
let name = format!("__arg{index}");
1165+
let ident = Ident::from_str(&name);
12221166

1223-
parameters.push(new_parameter);
1224-
}
1167+
(ident, false)
1168+
}
1169+
};
12251170

1226-
let mkbody = |this: &mut LoweringContext<'_, 'hir>| {
1227-
// Create a block from the user's function body:
1228-
let user_body = this.lower_block_expr(body);
1171+
let desugared_span = self.mark_span_with_reason(DesugaringKind::Async, span, None);
12291172

1230-
// Transform into `drop-temps { <user-body> }`, an expression:
1231-
let desugared_span =
1232-
this.mark_span_with_reason(DesugaringKind::Async, user_body.span, None);
1233-
let user_body = this.expr_drop_temps(desugared_span, this.arena.alloc(user_body));
1173+
// Construct a parameter representing `__argN: <ty>` to replace the parameter of the
1174+
// async function.
1175+
//
1176+
// If this is the simple case, this parameter will end up being the same as the
1177+
// original parameter, but with a different pattern id.
1178+
let stmt_attrs = self.attrs.get(&parameter.hir_id.local_id).copied();
1179+
let (new_parameter_pat, new_parameter_id) = self.pat_ident(desugared_span, ident);
1180+
let new_parameter = hir::Param {
1181+
hir_id: parameter.hir_id,
1182+
pat: new_parameter_pat,
1183+
ty_span: self.lower_span(parameter.ty_span),
1184+
span: self.lower_span(parameter.span),
1185+
};
12341186

1235-
// As noted above, create the final block like
1187+
if is_simple_parameter {
1188+
// If this is the simple case, then we only insert one statement that is
1189+
// `let <pat> = <pat>;`. We re-use the original argument's pattern so that
1190+
// `HirId`s are densely assigned.
1191+
let expr = self.expr_ident(desugared_span, ident, new_parameter_id);
1192+
let stmt = self.stmt_let_pat(
1193+
stmt_attrs,
1194+
desugared_span,
1195+
Some(expr),
1196+
parameter.pat,
1197+
hir::LocalSource::AsyncFn,
1198+
);
1199+
statements.push(stmt);
1200+
} else {
1201+
// If this is not the simple case, then we construct two statements:
12361202
//
12371203
// ```
1238-
// {
1239-
// let $param_pattern = $raw_param;
1240-
// ...
1241-
// drop-temps { <user-body> }
1242-
// }
1204+
// let __argN = __argN;
1205+
// let <pat> = __argN;
12431206
// ```
1244-
let body = this.block_all(
1207+
//
1208+
// The first statement moves the parameter into the closure and thus ensures
1209+
// that the drop order is correct.
1210+
//
1211+
// The second statement creates the bindings that the user wrote.
1212+
1213+
// Construct the `let mut __argN = __argN;` statement. It must be a mut binding
1214+
// because the user may have specified a `ref mut` binding in the next
1215+
// statement.
1216+
let (move_pat, move_id) =
1217+
self.pat_ident_binding_mode(desugared_span, ident, hir::BindingAnnotation::MUT);
1218+
let move_expr = self.expr_ident(desugared_span, ident, new_parameter_id);
1219+
let move_stmt = self.stmt_let_pat(
1220+
None,
12451221
desugared_span,
1246-
this.arena.alloc_from_iter(statements),
1247-
Some(user_body),
1222+
Some(move_expr),
1223+
move_pat,
1224+
hir::LocalSource::AsyncFn,
12481225
);
12491226

1250-
this.expr_block(body)
1251-
};
1252-
let desugaring_kind = match coroutine_kind {
1253-
CoroutineKind::Async { .. } => hir::CoroutineDesugaring::Async,
1254-
CoroutineKind::Gen { .. } => hir::CoroutineDesugaring::Gen,
1255-
CoroutineKind::AsyncGen { .. } => hir::CoroutineDesugaring::AsyncGen,
1227+
// Construct the `let <pat> = __argN;` statement. We re-use the original
1228+
// parameter's pattern so that `HirId`s are densely assigned.
1229+
let pattern_expr = self.expr_ident(desugared_span, ident, move_id);
1230+
let pattern_stmt = self.stmt_let_pat(
1231+
stmt_attrs,
1232+
desugared_span,
1233+
Some(pattern_expr),
1234+
parameter.pat,
1235+
hir::LocalSource::AsyncFn,
1236+
);
1237+
1238+
statements.push(move_stmt);
1239+
statements.push(pattern_stmt);
12561240
};
1257-
let coroutine_expr = this.make_desugared_coroutine_expr(
1258-
CaptureBy::Value { move_kw: rustc_span::DUMMY_SP },
1259-
closure_id,
1260-
None,
1261-
body.span,
1262-
desugaring_kind,
1263-
hir::CoroutineSource::Fn,
1264-
mkbody,
1241+
1242+
parameters.push(new_parameter);
1243+
}
1244+
1245+
let mkbody = |this: &mut LoweringContext<'_, 'hir>| {
1246+
// Create a block from the user's function body:
1247+
let user_body = this.lower_block_expr(body);
1248+
1249+
// Transform into `drop-temps { <user-body> }`, an expression:
1250+
let desugared_span =
1251+
this.mark_span_with_reason(DesugaringKind::Async, user_body.span, None);
1252+
let user_body = this.expr_drop_temps(desugared_span, this.arena.alloc(user_body));
1253+
1254+
// As noted above, create the final block like
1255+
//
1256+
// ```
1257+
// {
1258+
// let $param_pattern = $raw_param;
1259+
// ...
1260+
// drop-temps { <user-body> }
1261+
// }
1262+
// ```
1263+
let body = this.block_all(
1264+
desugared_span,
1265+
this.arena.alloc_from_iter(statements),
1266+
Some(user_body),
12651267
);
12661268

1267-
let hir_id = this.lower_node_id(closure_id);
1268-
this.maybe_forward_track_caller(body.span, fn_id, hir_id);
1269-
let expr = hir::Expr { hir_id, kind: coroutine_expr, span: this.lower_span(body.span) };
1269+
this.expr_block(body)
1270+
};
1271+
let desugaring_kind = match coroutine_kind {
1272+
CoroutineKind::Async { .. } => hir::CoroutineDesugaring::Async,
1273+
CoroutineKind::Gen { .. } => hir::CoroutineDesugaring::Gen,
1274+
CoroutineKind::AsyncGen { .. } => hir::CoroutineDesugaring::AsyncGen,
1275+
};
1276+
let closure_id = coroutine_kind.closure_id();
1277+
let coroutine_expr = self.make_desugared_coroutine_expr(
1278+
capture_clause,
1279+
closure_id,
1280+
None,
1281+
body.span,
1282+
desugaring_kind,
1283+
hir::CoroutineSource::Fn,
1284+
mkbody,
1285+
);
12701286

1271-
(this.arena.alloc_from_iter(parameters), expr)
1272-
})
1287+
let expr = hir::Expr {
1288+
hir_id: self.lower_node_id(closure_id),
1289+
kind: coroutine_expr,
1290+
span: self.lower_span(body.span),
1291+
};
1292+
1293+
(self.arena.alloc_from_iter(parameters), expr)
12731294
}
12741295

12751296
fn lower_method_sig(

0 commit comments

Comments
 (0)