Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
let expr = initializers[&occurrence.id];
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;78use super::{LoweringContext, MoveExprInitializerFinder, MoveExprState};9use crate::FnDeclKind;10use crate::diagnostics::{ClosureCannotBeStatic, CoroutineTooManyParameters};1112impl<'hir> LoweringContext<'_, 'hir> {13 // Entry point for `ExprKind::Closure`. Plain closures go through14 // `lower_expr_plain_closure_with_move_exprs`, which can wrap the lowered15 // closure in `let` initializers for `move(...)`. Coroutine closures keep the16 // existing coroutine-specific path and reject `move(...)` for now.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));2425 match closure.coroutine_kind {26 // FIXME(TaKO8Ki): Support `move(expr)` in coroutine closures too.27 // For the first step, we only support plain closures.28 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 ),42 span: self.lower_span(e.span),43 },44 None => self.lower_expr_plain_closure_with_move_exprs(45 expr_hir_id,46 attrs,47 &closure.binder,48 closure.capture_clause,49 e.id,50 closure.constness,51 closure.movability,52 &closure.fn_decl,53 &closure.body,54 closure.fn_decl_span,55 closure.fn_arg_span,56 e.span,57 ),58 }59 }6061 /// Lowers a plain closure expression and wraps it in an outer block if the62 /// closure body used `move(...)`.63 ///64 /// The lowering is split this way because `move(...)` initializers must be65 /// evaluated before the closure is created, but the closure body must still66 /// lower each `move(...)` occurrence as a use of the synthetic local that67 /// will be introduced by that outer block. For example:68 ///69 /// ```ignore (illustrative)70 /// || (move(move(foo.clone()))).len()71 /// ```72 ///73 /// first lowers the closure body roughly as `|| __move_expr_1.len()` while74 /// recording two occurrences:75 ///76 /// ```ignore (illustrative)77 /// move(foo.clone()) -> __move_expr_078 /// move(move(foo.clone())) -> __move_expr_179 /// ```80 ///81 /// This method then lowers the recorded initializers in order and builds the82 /// surrounding block:83 ///84 /// ```ignore (illustrative)85 /// {86 /// let __move_expr_0 = foo.clone();87 /// let __move_expr_1 = __move_expr_0;88 /// || __move_expr_1.len()89 /// }90 /// ```91 fn lower_expr_plain_closure_with_move_exprs(92 &mut self,93 expr_hir_id: HirId,94 attrs: &[hir::Attribute],95 binder: &ClosureBinder,96 capture_clause: CaptureBy,97 closure_id: NodeId,98 constness: Const,99 movability: Movability,100 decl: &FnDecl,101 body: &Expr,102 fn_decl_span: Span,103 fn_arg_span: Span,104 whole_span: Span,105 ) -> hir::Expr<'hir> {106 let (closure_kind, move_expr_state) = self.lower_expr_closure(107 attrs,108 binder,109 capture_clause,110 closure_id,111 constness,112 movability,113 decl,114 body,115 fn_decl_span,116 fn_arg_span,117 );118119 if move_expr_state.occurrences.is_empty() {120 return hir::Expr {121 hir_id: expr_hir_id,122 kind: closure_kind,123 span: self.lower_span(whole_span),124 };125 }126127 let initializers = MoveExprInitializerFinder::collect(body)128 .into_iter()129 .map(|initializer| (initializer.id, initializer.expr))130 .collect::<NodeMap<_>>();131 let mut stmts = Vec::with_capacity(move_expr_state.occurrences.len());132 let mut initializer_bindings = NodeMap::default();133 for occurrence in &move_expr_state.occurrences {134 // Evaluate the expression inside `move(...)` before creating the135 // closure and store it in a synthetic local:136 // `|| move(foo).bar` becomes roughly137 // `let __move_expr_0 = foo; || __move_expr_0.bar`.138 let expr = initializers[&occurrence.id];139 let init = if initializer_bindings.is_empty() {140 self.lower_expr(expr)141 } else {142 // Earlier entries cover nested `move(...)` expressions that143 // appear inside this initializer, as in144 // `move(move(foo.clone()))`.145 let (init, _) = self.with_move_expr_bindings(146 Some(MoveExprState {147 bindings: initializer_bindings.clone(),148 occurrences: Vec::new(),149 }),150 |this| this.lower_expr(expr),151 );152 init153 };154 stmts.push(self.stmt_let_pat(155 None,156 expr.span,157 Some(init),158 occurrence.pat,159 hir::LocalSource::Normal,160 ));161 initializer_bindings.insert(occurrence.id, (occurrence.ident, occurrence.binding));162 }163164 let closure_expr = self.arena.alloc(hir::Expr {165 hir_id: expr_hir_id,166 kind: closure_kind,167 span: self.lower_span(whole_span),168 });169170 let stmts = self.arena.alloc_from_iter(stmts);171 let block = self.block_all(whole_span, stmts, Some(closure_expr));172 self.expr(whole_span, hir::ExprKind::Block(block, None))173 }174175 // Lowers the actual plain closure node and body. The body is lowered while a176 // `MoveExprState` is active, so `move(...)` occurrences become synthetic177 // local uses and the caller can later add the matching initializers.178 fn lower_expr_closure(179 &mut self,180 attrs: &[hir::Attribute],181 binder: &ClosureBinder,182 capture_clause: CaptureBy,183 closure_id: NodeId,184 constness: Const,185 movability: Movability,186 decl: &FnDecl,187 body: &Expr,188 fn_decl_span: Span,189 fn_arg_span: Span,190 ) -> (hir::ExprKind<'hir>, MoveExprState<'hir>) {191 let closure_def_id = self.local_def_id(closure_id);192 let (binder_clause, generic_params) = self.lower_closure_binder(binder);193194 let ((body_id, closure_kind), move_expr_state) =195 self.with_new_scopes(fn_decl_span, move |this| {196 let mut coroutine_kind = find_attr!(197 attrs,198 Coroutine => hir::CoroutineKind::Coroutine(Movability::Movable)199 );200201 this.with_move_expr_bindings(Some(MoveExprState::default()), |this| {202 // FIXME(contracts): Support contracts on closures?203 let body_id = this.lower_fn_body(decl, None, |this| {204 this.coroutine_kind = coroutine_kind;205 let e = this.lower_expr_mut(body);206 coroutine_kind = this.coroutine_kind;207 e208 });209 let coroutine_option = this.closure_movability_for_fn(210 decl,211 fn_decl_span,212 coroutine_kind,213 movability,214 );215 (body_id, coroutine_option)216 })217 });218 let Some(move_expr_state) = move_expr_state else {219 span_bug!(fn_decl_span, "plain closure lowering did not return `move(...)` state");220 };221 let explicit_captures: &'hir [hir::ExplicitCapture] = self.arena.alloc_from_iter(222 move_expr_state.occurrences.iter().filter_map(|occurrence| {223 occurrence224 .explicit_capture225 .then_some(hir::ExplicitCapture { var_hir_id: occurrence.binding })226 }),227 );228229 let bound_generic_params = self.lower_lifetime_binder(closure_id, generic_params);230 // Lower outside new scope to preserve `is_in_loop_condition`.231 let fn_decl = self.lower_fn_decl(decl, closure_id, fn_decl_span, FnDeclKind::Closure, None);232233 let c = self.arena.alloc(hir::Closure {234 def_id: closure_def_id,235 binder: binder_clause,236 capture_clause: self.lower_capture_clause(capture_clause),237 bound_generic_params,238 fn_decl,239 body: body_id,240 fn_decl_span: self.lower_span(fn_decl_span),241 fn_arg_span: Some(self.lower_span(fn_arg_span)),242 kind: closure_kind,243 constness: self.lower_constness(constness),244 explicit_captures,245 });246247 (hir::ExprKind::Closure(c), move_expr_state)248 }249250 fn closure_movability_for_fn(251 &mut self,252 decl: &FnDecl,253 fn_decl_span: Span,254 coroutine_kind: Option<hir::CoroutineKind>,255 movability: Movability,256 ) -> hir::ClosureKind {257 match coroutine_kind {258 Some(hir::CoroutineKind::Coroutine(_)) => {259 if decl.inputs.len() > 1 {260 self.dcx().emit_err(CoroutineTooManyParameters { fn_decl_span });261 }262 hir::ClosureKind::Coroutine(hir::CoroutineKind::Coroutine(movability))263 }264 Some(265 hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _)266 | hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)267 | hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _),268 ) => {269 panic!("non-`async`/`gen` closure body turned `async`/`gen` during lowering");270 }271 None => {272 if movability == Movability::Static {273 self.dcx().emit_err(ClosureCannotBeStatic { fn_decl_span });274 }275 hir::ClosureKind::Closure276 }277 }278 }279280 fn lower_closure_binder<'c>(281 &mut self,282 binder: &'c ClosureBinder,283 ) -> (hir::ClosureBinder, &'c [GenericParam]) {284 let (binder, params) = match binder {285 ClosureBinder::NotPresent => (hir::ClosureBinder::Default, &[][..]),286 ClosureBinder::For { span, generic_params } => {287 let span = self.lower_span(*span);288 (hir::ClosureBinder::For { span }, &**generic_params)289 }290 };291292 (binder, params)293 }294295 // Coroutine closures are lowered separately because they build a different296 // body shape. This path pushes `None` for `move_expr_bindings`, so any297 // `move(...)` in the coroutine body gets a targeted unsupported-position298 // error instead of being collected like a plain closure occurrence.299 fn lower_expr_coroutine_closure(300 &mut self,301 binder: &ClosureBinder,302 capture_clause: CaptureBy,303 closure_id: NodeId,304 closure_hir_id: HirId,305 coroutine_kind: CoroutineKind,306 constness: Const,307 decl: &FnDecl,308 body: &Expr,309 fn_decl_span: Span,310 fn_arg_span: Span,311 ) -> hir::ExprKind<'hir> {312 let closure_def_id = self.local_def_id(closure_id);313 let (binder_clause, generic_params) = self.lower_closure_binder(binder);314315 let coroutine_desugaring = match coroutine_kind {316 CoroutineKind::Async { .. } => hir::CoroutineDesugaring::Async,317 CoroutineKind::Gen { .. } => hir::CoroutineDesugaring::Gen,318 CoroutineKind::AsyncGen { span, .. } => {319 span_bug!(span, "only async closures and `iter!` closures are supported currently")320 }321 };322323 let body = self.with_new_scopes(fn_decl_span, |this| {324 let inner_decl =325 FnDecl { inputs: decl.inputs.clone(), output: FnRetTy::Default(fn_decl_span) };326327 // Transform `async |x: u8| -> X { ... }` into328 // `|x: u8| || -> X { ... }`.329 let body_id = this.lower_body(|this| {330 let ((parameters, expr), _) = this.with_move_expr_bindings(None, |this| {331 this.lower_coroutine_body_with_moved_arguments(332 &inner_decl,333 |this| this.with_new_scopes(fn_decl_span, |this| this.lower_expr_mut(body)),334 fn_decl_span,335 body.span,336 coroutine_kind,337 hir::CoroutineSource::Closure,338 )339 });340341 this.maybe_forward_track_caller(body.span, closure_hir_id, expr.hir_id);342343 (parameters, expr)344 });345 body_id346 });347348 let bound_generic_params = self.lower_lifetime_binder(closure_id, generic_params);349 // We need to lower the declaration outside the new scope, because we350 // have to conserve the state of being inside a loop condition for the351 // closure argument types.352 let fn_decl =353 self.lower_fn_decl(&decl, closure_id, fn_decl_span, FnDeclKind::Closure, None);354355 if let Const::Yes(span) = constness {356 self.dcx().span_err(span, "const coroutines are not supported");357 }358359 let c = self.arena.alloc(hir::Closure {360 def_id: closure_def_id,361 binder: binder_clause,362 capture_clause: self.lower_capture_clause(capture_clause),363 bound_generic_params,364 fn_decl,365 body,366 fn_decl_span: self.lower_span(fn_decl_span),367 fn_arg_span: Some(self.lower_span(fn_arg_span)),368 // Lower this as a `CoroutineClosure`. That will ensure that HIR typeck369 // knows that a `FnDecl` output type like `-> &str` actually means370 // "coroutine that returns &str", rather than directly returning a `&str`.371 kind: hir::ClosureKind::CoroutineClosure(coroutine_desugaring),372 constness: self.lower_constness(constness),373 explicit_captures: &[],374 });375 hir::ExprKind::Closure(c)376 }377}
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.