1use rustc_ast::node_id::NodeMap;
2use rustc_ast::*;
3use rustc_hir as hir;
4use rustc_hir::{HirId, Target, find_attr};
5use rustc_middle::span_bug;
6use rustc_span::Span;
7
8use super::{LoweringContext, MoveExprInitializerFinder, MoveExprState};
9use crate::FnDeclKind;
10use crate::diagnostics::{ClosureCannotBeStatic, CoroutineTooManyParameters};
11
12impl<'hir> LoweringContext<'_, 'hir> {
13 pub(super) fn lower_expr_closure_expr(
18 &mut self,
19 e: &Expr,
20 closure: &Closure,
21 ) -> hir::Expr<'hir> {
22 let expr_hir_id = self.lower_node_id(e.id);
23 let attrs = self.lower_attrs(expr_hir_id, &e.attrs, e.span, Target::from_expr(e));
24
25 match closure.coroutine_kind {
26 Some(coroutine_kind) => hir::Expr {
29 hir_id: expr_hir_id,
30 kind: self.lower_expr_coroutine_closure(
31 &closure.binder,
32 closure.capture_clause,
33 e.id,
34 expr_hir_id,
35 coroutine_kind,
36 closure.constness,
37 &closure.fn_decl,
38 &closure.body,
39 closure.fn_decl_span,
40 closure.fn_arg_span,
41 attrs,
42 ),
43 span: self.lower_span(e.span),
44 },
45 None => self.lower_expr_plain_closure_with_move_exprs(
46 expr_hir_id,
47 attrs,
48 &closure.binder,
49 closure.capture_clause,
50 e.id,
51 closure.constness,
52 closure.movability,
53 &closure.fn_decl,
54 &closure.body,
55 closure.fn_decl_span,
56 closure.fn_arg_span,
57 e.span,
58 ),
59 }
60 }
61
62 fn lower_expr_plain_closure_with_move_exprs(
93 &mut self,
94 expr_hir_id: HirId,
95 attrs: &[hir::Attribute],
96 binder: &ClosureBinder,
97 capture_clause: CaptureBy,
98 closure_id: NodeId,
99 constness: Const,
100 movability: Movability,
101 decl: &FnDecl,
102 body: &Expr,
103 fn_decl_span: Span,
104 fn_arg_span: Span,
105 whole_span: Span,
106 ) -> hir::Expr<'hir> {
107 let (closure_kind, move_expr_state) = self.lower_expr_closure(
108 attrs,
109 binder,
110 capture_clause,
111 closure_id,
112 constness,
113 movability,
114 decl,
115 body,
116 fn_decl_span,
117 fn_arg_span,
118 );
119
120 if move_expr_state.occurrences.is_empty() {
121 return hir::Expr {
122 hir_id: expr_hir_id,
123 kind: closure_kind,
124 span: self.lower_span(whole_span),
125 };
126 }
127
128 let initializers = MoveExprInitializerFinder::collect(body)
129 .into_iter()
130 .map(|initializer| (initializer.id, initializer.expr))
131 .collect::<NodeMap<_>>();
132 let mut stmts = Vec::with_capacity(move_expr_state.occurrences.len());
133 let mut initializer_bindings = NodeMap::default();
134 for occurrence in &move_expr_state.occurrences {
135 let expr = initializers[&occurrence.id];
140 let init = if initializer_bindings.is_empty() {
141 self.lower_expr(expr)
142 } else {
143 let (init, _) = self.with_move_expr_bindings(
147 Some(MoveExprState {
148 bindings: initializer_bindings.clone(),
149 occurrences: Vec::new(),
150 }),
151 |this| this.lower_expr(expr),
152 );
153 init
154 };
155 stmts.push(self.stmt_let_pat(
156 None,
157 expr.span,
158 Some(init),
159 occurrence.pat,
160 hir::LocalSource::Normal,
161 ));
162 initializer_bindings.insert(occurrence.id, (occurrence.ident, occurrence.binding));
163 }
164
165 let closure_expr = self.arena.alloc(hir::Expr {
166 hir_id: expr_hir_id,
167 kind: closure_kind,
168 span: self.lower_span(whole_span),
169 });
170
171 let stmts = self.arena.alloc_from_iter(stmts);
172 let block = self.block_all(whole_span, stmts, Some(closure_expr));
173 self.expr(whole_span, hir::ExprKind::Block(block, None))
174 }
175
176 fn lower_expr_closure(
180 &mut self,
181 attrs: &[hir::Attribute],
182 binder: &ClosureBinder,
183 capture_clause: CaptureBy,
184 closure_id: NodeId,
185 constness: Const,
186 movability: Movability,
187 decl: &FnDecl,
188 body: &Expr,
189 fn_decl_span: Span,
190 fn_arg_span: Span,
191 ) -> (hir::ExprKind<'hir>, MoveExprState<'hir>) {
192 let closure_def_id = self.local_def_id(closure_id);
193 let (binder_clause, generic_params) = self.lower_closure_binder(binder);
194
195 let ((body_id, closure_kind), move_expr_state) =
196 self.with_new_scopes(fn_decl_span, move |this| {
197 let mut coroutine_kind = {
'done:
{
for i in attrs {
#[allow(unused_imports)]
use rustc_hir::attrs::AttributeKind::*;
let i: &rustc_hir::Attribute = i;
match i {
rustc_hir::Attribute::Parsed(Coroutine) => {
break 'done
Some(hir::CoroutineKind::Coroutine(Movability::Movable));
}
rustc_hir::Attribute::Unparsed(..) =>
{}
#[deny(unreachable_patterns)]
_ => {}
}
}
None
}
}find_attr!(
198 attrs,
199 Coroutine => hir::CoroutineKind::Coroutine(Movability::Movable)
200 );
201
202 this.with_move_expr_bindings(Some(MoveExprState::default()), |this| {
203 let body_id = this.lower_fn_body(decl, None, |this| {
205 this.coroutine_kind = coroutine_kind;
206 let e = this.lower_expr_mut(body);
207 coroutine_kind = this.coroutine_kind;
208 e
209 });
210 let coroutine_option = this.closure_movability_for_fn(
211 decl,
212 fn_decl_span,
213 coroutine_kind,
214 movability,
215 );
216 (body_id, coroutine_option)
217 })
218 });
219 let Some(move_expr_state) = move_expr_state else {
220 ::rustc_middle::util::bug::span_bug_fmt(fn_decl_span,
format_args!("plain closure lowering did not return `move(...)` state"));span_bug!(fn_decl_span, "plain closure lowering did not return `move(...)` state");
221 };
222 let explicit_captures: &'hir [hir::ExplicitCapture] = self.arena.alloc_from_iter(
223 move_expr_state.occurrences.iter().filter_map(|occurrence| {
224 occurrence
225 .explicit_capture
226 .then_some(hir::ExplicitCapture { var_hir_id: occurrence.binding })
227 }),
228 );
229
230 let bound_generic_params = self.lower_lifetime_binder(closure_id, generic_params);
231 let fn_decl = self.lower_fn_decl(decl, closure_id, fn_decl_span, FnDeclKind::Closure, None);
233
234 let c = self.arena.alloc(hir::Closure {
235 def_id: closure_def_id,
236 binder: binder_clause,
237 capture_clause: self.lower_capture_clause(capture_clause),
238 bound_generic_params,
239 fn_decl,
240 body: body_id,
241 fn_decl_span: self.lower_span(fn_decl_span),
242 fn_arg_span: Some(self.lower_span(fn_arg_span)),
243 kind: closure_kind,
244 constness: self.lower_constness(attrs, constness),
245 explicit_captures,
246 });
247
248 (hir::ExprKind::Closure(c), move_expr_state)
249 }
250
251 fn closure_movability_for_fn(
252 &mut self,
253 decl: &FnDecl,
254 fn_decl_span: Span,
255 coroutine_kind: Option<hir::CoroutineKind>,
256 movability: Movability,
257 ) -> hir::ClosureKind {
258 match coroutine_kind {
259 Some(hir::CoroutineKind::Coroutine(_)) => {
260 if decl.inputs.len() > 1 {
261 self.dcx().emit_err(CoroutineTooManyParameters { fn_decl_span });
262 }
263 hir::ClosureKind::Coroutine(hir::CoroutineKind::Coroutine(movability))
264 }
265 Some(
266 hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _)
267 | hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)
268 | hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _),
269 ) => {
270 {
::core::panicking::panic_fmt(format_args!("non-`async`/`gen` closure body turned `async`/`gen` during lowering"));
};panic!("non-`async`/`gen` closure body turned `async`/`gen` during lowering");
271 }
272 None => {
273 if movability == Movability::Static {
274 self.dcx().emit_err(ClosureCannotBeStatic { fn_decl_span });
275 }
276 hir::ClosureKind::Closure
277 }
278 }
279 }
280
281 fn lower_closure_binder<'c>(
282 &mut self,
283 binder: &'c ClosureBinder,
284 ) -> (hir::ClosureBinder, &'c [GenericParam]) {
285 let (binder, params) = match binder {
286 ClosureBinder::NotPresent => (hir::ClosureBinder::Default, &[][..]),
287 ClosureBinder::For { span, generic_params } => {
288 let span = self.lower_span(*span);
289 (hir::ClosureBinder::For { span }, &**generic_params)
290 }
291 };
292
293 (binder, params)
294 }
295
296 fn lower_expr_coroutine_closure(
301 &mut self,
302 binder: &ClosureBinder,
303 capture_clause: CaptureBy,
304 closure_id: NodeId,
305 closure_hir_id: HirId,
306 coroutine_kind: CoroutineKind,
307 constness: Const,
308 decl: &FnDecl,
309 body: &Expr,
310 fn_decl_span: Span,
311 fn_arg_span: Span,
312 attrs: &[hir::Attribute],
313 ) -> hir::ExprKind<'hir> {
314 let closure_def_id = self.local_def_id(closure_id);
315 let (binder_clause, generic_params) = self.lower_closure_binder(binder);
316
317 let coroutine_desugaring = match coroutine_kind {
318 CoroutineKind::Async { .. } => hir::CoroutineDesugaring::Async,
319 CoroutineKind::Gen { .. } => hir::CoroutineDesugaring::Gen,
320 CoroutineKind::AsyncGen { span, .. } => {
321 ::rustc_middle::util::bug::span_bug_fmt(span,
format_args!("only async closures and `iter!` closures are supported currently"))span_bug!(span, "only async closures and `iter!` closures are supported currently")
322 }
323 };
324
325 let body = self.with_new_scopes(fn_decl_span, |this| {
326 let inner_decl =
327 FnDecl { inputs: decl.inputs.clone(), output: FnRetTy::Default(fn_decl_span) };
328
329 let body_id = this.lower_body(|this| {
332 let ((parameters, expr), _) = this.with_move_expr_bindings(None, |this| {
333 this.lower_coroutine_body_with_moved_arguments(
334 &inner_decl,
335 |this| this.with_new_scopes(fn_decl_span, |this| this.lower_expr_mut(body)),
336 fn_decl_span,
337 body.span,
338 coroutine_kind,
339 hir::CoroutineSource::Closure,
340 )
341 });
342
343 this.maybe_forward_track_caller(body.span, closure_hir_id, expr.hir_id);
344
345 (parameters, expr)
346 });
347 body_id
348 });
349
350 let bound_generic_params = self.lower_lifetime_binder(closure_id, generic_params);
351 let fn_decl =
355 self.lower_fn_decl(&decl, closure_id, fn_decl_span, FnDeclKind::Closure, None);
356
357 if let Const::Yes(span) = constness {
358 self.dcx().span_err(span, "const coroutines are not supported");
359 }
360
361 let c = self.arena.alloc(hir::Closure {
362 def_id: closure_def_id,
363 binder: binder_clause,
364 capture_clause: self.lower_capture_clause(capture_clause),
365 bound_generic_params,
366 fn_decl,
367 body,
368 fn_decl_span: self.lower_span(fn_decl_span),
369 fn_arg_span: Some(self.lower_span(fn_arg_span)),
370 kind: hir::ClosureKind::CoroutineClosure(coroutine_desugaring),
374 constness: self.lower_constness(attrs, constness),
375 explicit_captures: &[],
376 });
377 hir::ExprKind::Closure(c)
378 }
379}