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