Skip to main content

rustc_ast_lowering/expr/
closure.rs

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