]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir_build/src/build/block.rs
Auto merge of #102618 - aliemjay:simplify-closure-promote, r=compiler-errors
[rust.git] / compiler / rustc_mir_build / src / build / block.rs
1 use crate::build::matches::ArmHasGuard;
2 use crate::build::ForGuard::OutsideGuard;
3 use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
4 use rustc_middle::middle::region::Scope;
5 use rustc_middle::thir::*;
6 use rustc_middle::{mir::*, ty};
7 use rustc_span::Span;
8
9 impl<'a, 'tcx> Builder<'a, 'tcx> {
10     pub(crate) fn ast_block(
11         &mut self,
12         destination: Place<'tcx>,
13         block: BasicBlock,
14         ast_block: BlockId,
15         source_info: SourceInfo,
16     ) -> BlockAnd<()> {
17         let Block {
18             region_scope,
19             opt_destruction_scope,
20             span,
21             ref stmts,
22             expr,
23             targeted_by_break,
24             safety_mode,
25         } = self.thir[ast_block];
26         let expr = expr.map(|expr| &self.thir[expr]);
27         self.in_opt_scope(opt_destruction_scope.map(|de| (de, source_info)), move |this| {
28             this.in_scope((region_scope, source_info), LintLevel::Inherited, move |this| {
29                 if targeted_by_break {
30                     this.in_breakable_scope(None, destination, span, |this| {
31                         Some(this.ast_block_stmts(
32                             destination,
33                             block,
34                             span,
35                             &stmts,
36                             expr,
37                             safety_mode,
38                             region_scope,
39                         ))
40                     })
41                 } else {
42                     this.ast_block_stmts(
43                         destination,
44                         block,
45                         span,
46                         &stmts,
47                         expr,
48                         safety_mode,
49                         region_scope,
50                     )
51                 }
52             })
53         })
54     }
55
56     fn ast_block_stmts(
57         &mut self,
58         destination: Place<'tcx>,
59         mut block: BasicBlock,
60         span: Span,
61         stmts: &[StmtId],
62         expr: Option<&Expr<'tcx>>,
63         safety_mode: BlockSafety,
64         region_scope: Scope,
65     ) -> BlockAnd<()> {
66         let this = self;
67
68         // This convoluted structure is to avoid using recursion as we walk down a list
69         // of statements. Basically, the structure we get back is something like:
70         //
71         //    let x = <init> in {
72         //       expr1;
73         //       let y = <init> in {
74         //           expr2;
75         //           expr3;
76         //           ...
77         //       }
78         //    }
79         //
80         // The let bindings are valid till the end of block so all we have to do is to pop all
81         // the let-scopes at the end.
82         //
83         // First we build all the statements in the block.
84         let mut let_scope_stack = Vec::with_capacity(8);
85         let outer_source_scope = this.source_scope;
86         let outer_in_scope_unsafe = this.in_scope_unsafe;
87         // This scope information is kept for breaking out of the parent remainder scope in case
88         // one let-else pattern matching fails.
89         // By doing so, we can be sure that even temporaries that receive extended lifetime
90         // assignments are dropped, too.
91         let mut last_remainder_scope = region_scope;
92         this.update_source_scope_for_safety_mode(span, safety_mode);
93
94         let source_info = this.source_info(span);
95         for stmt in stmts {
96             let Stmt { ref kind, opt_destruction_scope } = this.thir[*stmt];
97             match kind {
98                 StmtKind::Expr { scope, expr } => {
99                     this.block_context.push(BlockFrame::Statement { ignores_expr_result: true });
100                     unpack!(
101                         block = this.in_opt_scope(
102                             opt_destruction_scope.map(|de| (de, source_info)),
103                             |this| {
104                                 let si = (*scope, source_info);
105                                 this.in_scope(si, LintLevel::Inherited, |this| {
106                                     this.stmt_expr(block, &this.thir[*expr], Some(*scope))
107                                 })
108                             }
109                         )
110                     );
111                 }
112                 StmtKind::Let {
113                     remainder_scope,
114                     init_scope,
115                     pattern,
116                     initializer: Some(initializer),
117                     lint_level,
118                     else_block: Some(else_block),
119                 } => {
120                     // When lowering the statement `let <pat> = <expr> else { <else> };`,
121                     // the `<else>` block is nested in the parent scope enclosing this statment.
122                     // That scope is usually either the enclosing block scope,
123                     // or the remainder scope of the last statement.
124                     // This is to make sure that temporaries instantiated in `<expr>` are dropped
125                     // as well.
126                     // In addition, even though bindings in `<pat>` only come into scope if
127                     // the pattern matching passes, in the MIR building the storages for them
128                     // are declared as live any way.
129                     // This is similar to `let x;` statements without an initializer expression,
130                     // where the value of `x` in this example may or may be assigned,
131                     // because the storage for their values may not be live after all due to
132                     // failure in pattern matching.
133                     // For this reason, we declare those storages as live but we do not schedule
134                     // any drop yet- they are scheduled later after the pattern matching.
135                     // The generated MIR will have `StorageDead` whenever the control flow breaks out
136                     // of the parent scope, regardless of the result of the pattern matching.
137                     // However, the drops are inserted in MIR only when the control flow breaks out of
138                     // the scope of the remainder scope associated with this `let .. else` statement.
139                     // Pictorial explanation of the scope structure:
140                     // ┌─────────────────────────────────┐
141                     // │  Scope of the enclosing block,  │
142                     // │  or the last remainder scope    │
143                     // │  ┌───────────────────────────┐  │
144                     // │  │  Scope for <else> block   │  │
145                     // │  └───────────────────────────┘  │
146                     // │  ┌───────────────────────────┐  │
147                     // │  │  Remainder scope of       │  │
148                     // │  │  this let-else statement  │  │
149                     // │  │  ┌─────────────────────┐  │  │
150                     // │  │  │ <expr> scope        │  │  │
151                     // │  │  └─────────────────────┘  │  │
152                     // │  │  extended temporaries in  │  │
153                     // │  │  <expr> lives in this     │  │
154                     // │  │  scope                    │  │
155                     // │  │  ┌─────────────────────┐  │  │
156                     // │  │  │ Scopes for the rest │  │  │
157                     // │  │  └─────────────────────┘  │  │
158                     // │  └───────────────────────────┘  │
159                     // └─────────────────────────────────┘
160                     // Generated control flow:
161                     //          │ let Some(x) = y() else { return; }
162                     //          │
163                     // ┌────────▼───────┐
164                     // │ evaluate y()   │
165                     // └────────┬───────┘
166                     //          │              ┌────────────────┐
167                     // ┌────────▼───────┐      │Drop temporaries│
168                     // │Test the pattern├──────►in y()          │
169                     // └────────┬───────┘      │because breaking│
170                     //          │              │out of <expr>   │
171                     // ┌────────▼───────┐      │scope           │
172                     // │Move value into │      └───────┬────────┘
173                     // │binding x       │              │
174                     // └────────┬───────┘      ┌───────▼────────┐
175                     //          │              │Drop extended   │
176                     // ┌────────▼───────┐      │temporaries in  │
177                     // │Drop temporaries│      │<expr> because  │
178                     // │in y()          │      │breaking out of │
179                     // │because breaking│      │remainder scope │
180                     // │out of <expr>   │      └───────┬────────┘
181                     // │scope           │              │
182                     // └────────┬───────┘      ┌───────▼────────┐
183                     //          │              │Enter <else>    ├────────►
184                     // ┌────────▼───────┐      │block           │ return;
185                     // │Continue...     │      └────────────────┘
186                     // └────────────────┘
187
188                     let ignores_expr_result = matches!(pattern.kind, PatKind::Wild);
189                     this.block_context.push(BlockFrame::Statement { ignores_expr_result });
190
191                     // Lower the `else` block first because its parent scope is actually
192                     // enclosing the rest of the `let .. else ..` parts.
193                     let else_block_span = this.thir[*else_block].span;
194                     // This place is not really used because this destination place
195                     // should never be used to take values at the end of the failure
196                     // block.
197                     let dummy_place = this.temp(this.tcx.types.never, else_block_span);
198                     let failure_entry = this.cfg.start_new_block();
199                     let failure_block;
200                     unpack!(
201                         failure_block = this.ast_block(
202                             dummy_place,
203                             failure_entry,
204                             *else_block,
205                             this.source_info(else_block_span),
206                         )
207                     );
208                     this.cfg.terminate(
209                         failure_block,
210                         this.source_info(else_block_span),
211                         TerminatorKind::Unreachable,
212                     );
213
214                     // Declare the bindings, which may create a source scope.
215                     let remainder_span = remainder_scope.span(this.tcx, this.region_scope_tree);
216                     this.push_scope((*remainder_scope, source_info));
217                     let_scope_stack.push(remainder_scope);
218
219                     let visibility_scope =
220                         Some(this.new_source_scope(remainder_span, LintLevel::Inherited, None));
221
222                     let init = &this.thir[*initializer];
223                     let initializer_span = init.span;
224                     let failure = unpack!(
225                         block = this.in_opt_scope(
226                             opt_destruction_scope.map(|de| (de, source_info)),
227                             |this| {
228                                 let scope = (*init_scope, source_info);
229                                 this.in_scope(scope, *lint_level, |this| {
230                                     this.declare_bindings(
231                                         visibility_scope,
232                                         remainder_span,
233                                         pattern,
234                                         ArmHasGuard(false),
235                                         Some((None, initializer_span)),
236                                     );
237                                     this.visit_primary_bindings(
238                                         pattern,
239                                         UserTypeProjections::none(),
240                                         &mut |this, _, _, _, node, span, _, _| {
241                                             this.storage_live_binding(
242                                                 block,
243                                                 node,
244                                                 span,
245                                                 OutsideGuard,
246                                                 true,
247                                             );
248                                         },
249                                     );
250                                     this.ast_let_else(
251                                         block,
252                                         init,
253                                         initializer_span,
254                                         *else_block,
255                                         &last_remainder_scope,
256                                         pattern,
257                                     )
258                                 })
259                             }
260                         )
261                     );
262                     this.cfg.goto(failure, source_info, failure_entry);
263
264                     if let Some(source_scope) = visibility_scope {
265                         this.source_scope = source_scope;
266                     }
267                     last_remainder_scope = *remainder_scope;
268                 }
269                 StmtKind::Let { init_scope, initializer: None, else_block: Some(_), .. } => {
270                     span_bug!(
271                         init_scope.span(this.tcx, this.region_scope_tree),
272                         "initializer is missing, but else block is present in this let binding",
273                     )
274                 }
275                 StmtKind::Let {
276                     remainder_scope,
277                     init_scope,
278                     ref pattern,
279                     initializer,
280                     lint_level,
281                     else_block: None,
282                 } => {
283                     let ignores_expr_result = matches!(pattern.kind, PatKind::Wild);
284                     this.block_context.push(BlockFrame::Statement { ignores_expr_result });
285
286                     // Enter the remainder scope, i.e., the bindings' destruction scope.
287                     this.push_scope((*remainder_scope, source_info));
288                     let_scope_stack.push(remainder_scope);
289
290                     // Declare the bindings, which may create a source scope.
291                     let remainder_span = remainder_scope.span(this.tcx, this.region_scope_tree);
292
293                     let visibility_scope =
294                         Some(this.new_source_scope(remainder_span, LintLevel::Inherited, None));
295
296                     // Evaluate the initializer, if present.
297                     if let Some(init) = initializer {
298                         let init = &this.thir[*init];
299                         let initializer_span = init.span;
300
301                         unpack!(
302                             block = this.in_opt_scope(
303                                 opt_destruction_scope.map(|de| (de, source_info)),
304                                 |this| {
305                                     let scope = (*init_scope, source_info);
306                                     this.in_scope(scope, *lint_level, |this| {
307                                         this.declare_bindings(
308                                             visibility_scope,
309                                             remainder_span,
310                                             pattern,
311                                             ArmHasGuard(false),
312                                             Some((None, initializer_span)),
313                                         );
314                                         this.expr_into_pattern(block, &pattern, init)
315                                         // irrefutable pattern
316                                     })
317                                 },
318                             )
319                         )
320                     } else {
321                         let scope = (*init_scope, source_info);
322                         unpack!(this.in_scope(scope, *lint_level, |this| {
323                             this.declare_bindings(
324                                 visibility_scope,
325                                 remainder_span,
326                                 pattern,
327                                 ArmHasGuard(false),
328                                 None,
329                             );
330                             block.unit()
331                         }));
332
333                         debug!("ast_block_stmts: pattern={:?}", pattern);
334                         this.visit_primary_bindings(
335                             pattern,
336                             UserTypeProjections::none(),
337                             &mut |this, _, _, _, node, span, _, _| {
338                                 this.storage_live_binding(block, node, span, OutsideGuard, true);
339                                 this.schedule_drop_for_binding(node, span, OutsideGuard);
340                             },
341                         )
342                     }
343
344                     // Enter the visibility scope, after evaluating the initializer.
345                     if let Some(source_scope) = visibility_scope {
346                         this.source_scope = source_scope;
347                     }
348                     last_remainder_scope = *remainder_scope;
349                 }
350             }
351
352             let popped = this.block_context.pop();
353             assert!(popped.map_or(false, |bf| bf.is_statement()));
354         }
355
356         // Then, the block may have an optional trailing expression which is a “return” value
357         // of the block, which is stored into `destination`.
358         let tcx = this.tcx;
359         let destination_ty = destination.ty(&this.local_decls, tcx).ty;
360         if let Some(expr) = expr {
361             let tail_result_is_ignored =
362                 destination_ty.is_unit() || this.block_context.currently_ignores_tail_results();
363             this.block_context
364                 .push(BlockFrame::TailExpr { tail_result_is_ignored, span: expr.span });
365
366             unpack!(block = this.expr_into_dest(destination, block, expr));
367             let popped = this.block_context.pop();
368
369             assert!(popped.map_or(false, |bf| bf.is_tail_expr()));
370         } else {
371             // If a block has no trailing expression, then it is given an implicit return type.
372             // This return type is usually `()`, unless the block is diverging, in which case the
373             // return type is `!`. For the unit type, we need to actually return the unit, but in
374             // the case of `!`, no return value is required, as the block will never return.
375             // Opaque types of empty bodies also need this unit assignment, in order to infer that their
376             // type is actually unit. Otherwise there will be no defining use found in the MIR.
377             if destination_ty.is_unit() || matches!(destination_ty.kind(), ty::Opaque(..)) {
378                 // We only want to assign an implicit `()` as the return value of the block if the
379                 // block does not diverge. (Otherwise, we may try to assign a unit to a `!`-type.)
380                 this.cfg.push_assign_unit(block, source_info, destination, this.tcx);
381             }
382         }
383         // Finally, we pop all the let scopes before exiting out from the scope of block
384         // itself.
385         for scope in let_scope_stack.into_iter().rev() {
386             unpack!(block = this.pop_scope((*scope, source_info), block));
387         }
388         // Restore the original source scope.
389         this.source_scope = outer_source_scope;
390         this.in_scope_unsafe = outer_in_scope_unsafe;
391         block.unit()
392     }
393
394     /// If we are entering an unsafe block, create a new source scope
395     fn update_source_scope_for_safety_mode(&mut self, span: Span, safety_mode: BlockSafety) {
396         debug!("update_source_scope_for({:?}, {:?})", span, safety_mode);
397         let new_unsafety = match safety_mode {
398             BlockSafety::Safe => return,
399             BlockSafety::BuiltinUnsafe => Safety::BuiltinUnsafe,
400             BlockSafety::ExplicitUnsafe(hir_id) => {
401                 self.in_scope_unsafe = Safety::ExplicitUnsafe(hir_id);
402                 Safety::ExplicitUnsafe(hir_id)
403             }
404         };
405
406         self.source_scope = self.new_source_scope(span, LintLevel::Inherited, Some(new_unsafety));
407     }
408 }