Skip to main content

rustc_ast_lowering/expr/
closure.rs

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