Skip to main content

rustc_ast_lowering/expr/
closure.rs

1use rustc_ast::*;
2use rustc_hir as hir;
3use rustc_hir::{HirId, Target, find_attr};
4use rustc_span::{Span, span_bug};
5
6use super::{LoweringContext, MoveExprState};
7use crate::FnDeclKind;
8use crate::diagnostics::{ClosureCannotBeStatic, CoroutineTooManyParameters};
9
10impl<'hir> LoweringContext<'_, 'hir> {
11    // Entry point for `ExprKind::Closure`. Plain closures go through
12    // `lower_expr_plain_closure_with_move_exprs`, which can wrap the lowered
13    // closure in `let` initializers for `move(...)`. Coroutine closures use the
14    // same wrapper after building their coroutine-specific body shape.
15    pub(super) fn lower_expr_closure_expr(
16        &mut self,
17        e: &Expr,
18        closure: &Closure,
19    ) -> hir::Expr<'hir> {
20        let expr_hir_id = self.lower_node_id(e.id);
21        let attrs = self.lower_attrs(expr_hir_id, &e.attrs, e.span, Target::from_expr(e));
22
23        match closure.coroutine_marker {
24            Some(coroutine_marker) => self.lower_expr_coroutine_closure_with_move_exprs(
25                expr_hir_id,
26                attrs,
27                &closure.binder,
28                closure.capture_clause,
29                e.id,
30                coroutine_marker,
31                closure.constness,
32                &closure.fn_decl,
33                &closure.body,
34                closure.fn_decl_span,
35                closure.fn_arg_span,
36                e.span,
37            ),
38            None => self.lower_expr_plain_closure_with_move_exprs(
39                expr_hir_id,
40                attrs,
41                &closure.binder,
42                closure.capture_clause,
43                e.id,
44                closure.constness,
45                closure.movability,
46                &closure.fn_decl,
47                &closure.body,
48                closure.fn_decl_span,
49                closure.fn_arg_span,
50                e.span,
51            ),
52        }
53    }
54
55    fn lower_expr_coroutine_closure_with_move_exprs(
56        &mut self,
57        expr_hir_id: HirId,
58        attrs: &[hir::Attribute],
59        binder: &ClosureBinder,
60        capture_clause: CaptureBy,
61        closure_id: NodeId,
62        coroutine_marker: CoroutineMarker,
63        constness: Const,
64        decl: &FnDecl,
65        body: &Expr,
66        fn_decl_span: Span,
67        fn_arg_span: Span,
68        whole_span: Span,
69    ) -> hir::Expr<'hir> {
70        let (kind, move_expr_state) =
71            self.with_move_expr_bindings(Some(MoveExprState::default()), |this| {
72                this.lower_expr_coroutine_closure(
73                    binder,
74                    capture_clause,
75                    closure_id,
76                    expr_hir_id,
77                    coroutine_marker,
78                    constness,
79                    decl,
80                    body,
81                    fn_decl_span,
82                    fn_arg_span,
83                    attrs,
84                )
85            });
86        let Some(move_expr_state) = move_expr_state else {
87            bug_impl(Some(fn_decl_span),
    format_args!("coroutine closure lowering did not return `move(...)` state"),
    Location::caller());span_bug!(fn_decl_span, "coroutine closure lowering did not return `move(...)` state");
88        };
89        let closure_expr =
90            hir::Expr { hir_id: expr_hir_id, kind, span: self.lower_span(whole_span) };
91
92        self.lower_expr_with_move_exprs(closure_expr, move_expr_state, body, whole_span)
93    }
94
95    /// Lowers a plain closure expression and wraps it in an outer block if the
96    /// closure body used `move(...)`.
97    ///
98    /// The lowering is split this way because `move(...)` initializers must be
99    /// evaluated before the closure is created, but the closure body must still
100    /// lower each `move(...)` occurrence as a use of the synthetic local that
101    /// will be introduced by that outer block. For example,
102    /// `|| move(foo.clone()).len()` becomes roughly:
103    ///
104    /// ```ignore (illustrative)
105    /// {
106    ///     let __move_expr_0 = foo.clone();
107    ///     || __move_expr_0.len()
108    /// }
109    /// ```
110    ///
111    /// If the initializer contains another `move(...)`, it is lowered after
112    /// this closure's state is popped and therefore belongs to the immediately
113    /// enclosing closure-like body.
114    fn lower_expr_plain_closure_with_move_exprs(
115        &mut self,
116        expr_hir_id: HirId,
117        attrs: &[hir::Attribute],
118        binder: &ClosureBinder,
119        capture_clause: CaptureBy,
120        closure_id: NodeId,
121        constness: Const,
122        movability: Movability,
123        decl: &FnDecl,
124        body: &Expr,
125        fn_decl_span: Span,
126        fn_arg_span: Span,
127        whole_span: Span,
128    ) -> hir::Expr<'hir> {
129        let (closure_kind, move_expr_state) = self.lower_expr_closure(
130            attrs,
131            binder,
132            capture_clause,
133            closure_id,
134            constness,
135            movability,
136            decl,
137            body,
138            fn_decl_span,
139            fn_arg_span,
140        );
141
142        let closure_expr = hir::Expr {
143            hir_id: expr_hir_id,
144            kind: closure_kind,
145            span: self.lower_span(whole_span),
146        };
147
148        self.lower_expr_with_move_exprs(closure_expr, move_expr_state, body, whole_span)
149    }
150
151    // Lowers the actual plain closure node and body. The body is lowered while a
152    // `MoveExprState` is active, so `move(...)` occurrences become synthetic
153    // local uses and the caller can later add the matching initializers.
154    fn lower_expr_closure(
155        &mut self,
156        attrs: &[hir::Attribute],
157        binder: &ClosureBinder,
158        capture_clause: CaptureBy,
159        closure_id: NodeId,
160        constness: Const,
161        movability: Movability,
162        decl: &FnDecl,
163        body: &Expr,
164        fn_decl_span: Span,
165        fn_arg_span: Span,
166    ) -> (hir::ExprKind<'hir>, MoveExprState<'hir>) {
167        let closure_def_id = self.local_def_id(closure_id);
168        let (binder_clause, generic_params) = self.lower_closure_binder(binder);
169
170        let ((body_id, closure_kind), move_expr_state) =
171            self.with_new_scopes(fn_decl_span, move |this| {
172                let mut coroutine_kind = {
    'done:
        {
        for i in attrs {
            #[allow(unused_imports)]
            use ::rustc_attr_ir::AttributeKind::*;
            let i: &::rustc_attr_ir::Attribute = i;
            match i {
                ::rustc_attr_ir::Attribute::Parsed(Coroutine) => {
                    break 'done
                        Some(hir::CoroutineKind::Coroutine(Movability::Movable));
                }
                ::rustc_attr_ir::Attribute::Unparsed(..) =>
                    {}
                    #[deny(unreachable_patterns)]
                    _ => {}
            }
        }
        None
    }
}find_attr!(
173                    attrs,
174                    Coroutine => hir::CoroutineKind::Coroutine(Movability::Movable)
175                );
176
177                this.with_move_expr_bindings(Some(MoveExprState::default()), |this| {
178                    // FIXME(contracts): Support contracts on closures?
179                    let body_id = this.lower_fn_body(decl, None, |this| {
180                        this.coroutine_kind = coroutine_kind;
181                        let e = this.lower_expr_mut(body);
182                        coroutine_kind = this.coroutine_kind;
183                        e
184                    });
185                    let coroutine_option = this.closure_movability_for_fn(
186                        decl,
187                        fn_decl_span,
188                        coroutine_kind,
189                        movability,
190                    );
191                    (body_id, coroutine_option)
192                })
193            });
194        let Some(move_expr_state) = move_expr_state else {
195            bug_impl(Some(fn_decl_span),
    format_args!("plain closure lowering did not return `move(...)` state"),
    Location::caller());span_bug!(fn_decl_span, "plain closure lowering did not return `move(...)` state");
196        };
197        let explicit_captures: &'hir [hir::ExplicitCapture] = self.arena.alloc_from_iter(
198            move_expr_state
199                .occurrences
200                .iter()
201                .map(|occurrence| hir::ExplicitCapture { var_hir_id: occurrence.binding }),
202        );
203
204        let bound_generic_params = self.lower_lifetime_binder(closure_id, generic_params);
205        // Lower outside new scope to preserve `is_in_loop_condition`.
206        let fn_decl = self.lower_fn_decl(decl, closure_id, FnDeclKind::Closure, None);
207
208        let c = self.arena.alloc(hir::Closure {
209            def_id: closure_def_id,
210            binder: binder_clause,
211            capture_clause: self.lower_capture_clause(capture_clause),
212            bound_generic_params,
213            fn_decl,
214            body: body_id,
215            fn_decl_span: self.lower_span(fn_decl_span),
216            fn_arg_span: Some(self.lower_span(fn_arg_span)),
217            kind: closure_kind,
218            constness: self.lower_constness(attrs, constness),
219            explicit_captures,
220        });
221
222        (hir::ExprKind::Closure(c), move_expr_state)
223    }
224
225    fn closure_movability_for_fn(
226        &mut self,
227        decl: &FnDecl,
228        fn_decl_span: Span,
229        coroutine_kind: Option<hir::CoroutineKind>,
230        movability: Movability,
231    ) -> hir::ClosureKind {
232        match coroutine_kind {
233            Some(hir::CoroutineKind::Coroutine(_)) => {
234                if decl.inputs.len() > 1 {
235                    self.dcx().emit_err(CoroutineTooManyParameters { fn_decl_span });
236                }
237                hir::ClosureKind::Coroutine(hir::CoroutineKind::Coroutine(movability))
238            }
239            Some(
240                hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _)
241                | hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)
242                | hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _),
243            ) => {
244                {
    ::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");
245            }
246            None => {
247                if movability == Movability::Static {
248                    self.dcx().emit_err(ClosureCannotBeStatic { fn_decl_span });
249                }
250                hir::ClosureKind::Closure
251            }
252        }
253    }
254
255    fn lower_closure_binder<'c>(
256        &mut self,
257        binder: &'c ClosureBinder,
258    ) -> (hir::ClosureBinder, &'c [GenericParam]) {
259        let (binder, params) = match binder {
260            ClosureBinder::NotPresent => (hir::ClosureBinder::Default, &[][..]),
261            ClosureBinder::For { span, generic_params } => {
262                let span = self.lower_span(*span);
263                (hir::ClosureBinder::For { span }, &**generic_params)
264            }
265        };
266
267        (binder, params)
268    }
269
270    // Coroutine closures are lowered separately because they build a different
271    // body shape. The source body is lowered with the caller's `MoveExprState`
272    // active, so `move(...)` occurrences are collected and hoisted into a block
273    // around the outer closure expression.
274    fn lower_expr_coroutine_closure(
275        &mut self,
276        binder: &ClosureBinder,
277        capture_clause: CaptureBy,
278        closure_id: NodeId,
279        closure_hir_id: HirId,
280        coroutine_marker: CoroutineMarker,
281        constness: Const,
282        decl: &FnDecl,
283        body: &Expr,
284        fn_decl_span: Span,
285        fn_arg_span: Span,
286        attrs: &[hir::Attribute],
287    ) -> hir::ExprKind<'hir> {
288        let closure_def_id = self.local_def_id(closure_id);
289        let (binder_clause, generic_params) = self.lower_closure_binder(binder);
290
291        let coroutine_desugaring = match coroutine_marker.kind {
292            CoroutineKind::Async => hir::CoroutineDesugaring::Async,
293            CoroutineKind::Gen => hir::CoroutineDesugaring::Gen,
294            CoroutineKind::AsyncGen => {
295                bug_impl(Some(coroutine_marker.span),
    format_args!("only async closures and `iter!` closures are supported currently"),
    Location::caller())span_bug!(
296                    coroutine_marker.span,
297                    "only async closures and `iter!` closures are supported currently"
298                )
299            }
300        };
301
302        let body = self.with_new_scopes(fn_decl_span, |this| {
303            let inner_decl =
304                FnDecl { inputs: decl.inputs.clone(), output: FnRetTy::Default(fn_decl_span) };
305
306            // Transform `async |x: u8| -> X { ... }` into
307            // `|x: u8| || -> X { ... }`.
308            let body_id = this.lower_body(|this| {
309                let (parameters, expr) = this.lower_coroutine_body_with_moved_arguments(
310                    &inner_decl,
311                    |this| this.with_new_scopes(fn_decl_span, |this| this.lower_expr_mut(body)),
312                    fn_decl_span,
313                    body.span,
314                    coroutine_marker,
315                    hir::CoroutineSource::Closure,
316                );
317
318                this.maybe_forward_track_caller(closure_hir_id, expr.hir_id);
319
320                (parameters, expr)
321            });
322            body_id
323        });
324
325        let bound_generic_params = self.lower_lifetime_binder(closure_id, generic_params);
326        // We need to lower the declaration outside the new scope, because we
327        // have to conserve the state of being inside a loop condition for the
328        // closure argument types.
329        let fn_decl = self.lower_fn_decl(&decl, closure_id, FnDeclKind::Closure, None);
330
331        if let Const::Yes(span) = constness {
332            self.dcx().span_err(span, "const coroutines are not supported");
333        }
334
335        let explicit_captures: &'hir [hir::ExplicitCapture] = self.arena.alloc_from_iter(
336            self.move_expr_bindings
337                .last()
338                .and_then(Option::as_ref)
339                .into_iter()
340                .flat_map(|state| &state.occurrences)
341                .map(|occurrence| hir::ExplicitCapture { var_hir_id: occurrence.binding }),
342        );
343
344        let c = self.arena.alloc(hir::Closure {
345            def_id: closure_def_id,
346            binder: binder_clause,
347            capture_clause: self.lower_capture_clause(capture_clause),
348            bound_generic_params,
349            fn_decl,
350            body,
351            fn_decl_span: self.lower_span(fn_decl_span),
352            fn_arg_span: Some(self.lower_span(fn_arg_span)),
353            // Lower this as a `CoroutineClosure`. That will ensure that HIR typeck
354            // knows that a `FnDecl` output type like `-> &str` actually means
355            // "coroutine that returns &str", rather than directly returning a `&str`.
356            kind: hir::ClosureKind::CoroutineClosure(coroutine_desugaring),
357            constness: self.lower_constness(attrs, constness),
358            explicit_captures,
359        });
360        hir::ExprKind::Closure(c)
361    }
362}