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