]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_mir_build/src/build/block.rs
New upstream version 1.65.0+dfsg1
[rustc.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 this.declare_bindings(
225 visibility_scope,
226 remainder_span,
227 pattern,
228 ArmHasGuard(false),
229 Some((None, initializer_span)),
230 );
231 this.visit_primary_bindings(
232 pattern,
233 UserTypeProjections::none(),
234 &mut |this, _, _, _, node, span, _, _| {
235 this.storage_live_binding(block, node, span, OutsideGuard, true);
236 },
237 );
238 let failure = unpack!(
239 block = this.in_opt_scope(
240 opt_destruction_scope.map(|de| (de, source_info)),
241 |this| {
242 let scope = (*init_scope, source_info);
243 this.in_scope(scope, *lint_level, |this| {
244 this.ast_let_else(
245 block,
246 init,
247 initializer_span,
248 *else_block,
249 &last_remainder_scope,
250 pattern,
251 )
252 })
253 }
254 )
255 );
256 this.cfg.goto(failure, source_info, failure_entry);
257
258 if let Some(source_scope) = visibility_scope {
259 this.source_scope = source_scope;
260 }
261 last_remainder_scope = *remainder_scope;
262 }
263 StmtKind::Let { init_scope, initializer: None, else_block: Some(_), .. } => {
264 span_bug!(
265 init_scope.span(this.tcx, this.region_scope_tree),
266 "initializer is missing, but else block is present in this let binding",
267 )
268 }
269 StmtKind::Let {
270 remainder_scope,
271 init_scope,
272 ref pattern,
273 initializer,
274 lint_level,
275 else_block: None,
276 } => {
277 let ignores_expr_result = matches!(pattern.kind, PatKind::Wild);
278 this.block_context.push(BlockFrame::Statement { ignores_expr_result });
279
280 // Enter the remainder scope, i.e., the bindings' destruction scope.
281 this.push_scope((*remainder_scope, source_info));
282 let_scope_stack.push(remainder_scope);
283
284 // Declare the bindings, which may create a source scope.
285 let remainder_span = remainder_scope.span(this.tcx, this.region_scope_tree);
286
287 let visibility_scope =
288 Some(this.new_source_scope(remainder_span, LintLevel::Inherited, None));
289
290 // Evaluate the initializer, if present.
291 if let Some(init) = initializer {
292 let init = &this.thir[*init];
293 let initializer_span = init.span;
294
295 unpack!(
296 block = this.in_opt_scope(
297 opt_destruction_scope.map(|de| (de, source_info)),
298 |this| {
299 let scope = (*init_scope, source_info);
300 this.in_scope(scope, *lint_level, |this| {
301 this.declare_bindings(
302 visibility_scope,
303 remainder_span,
304 pattern,
305 ArmHasGuard(false),
306 Some((None, initializer_span)),
307 );
308 this.expr_into_pattern(block, &pattern, init) // irrefutable pattern
309 })
310 },
311 )
312 )
313 } else {
314 let scope = (*init_scope, source_info);
315 unpack!(this.in_scope(scope, *lint_level, |this| {
316 this.declare_bindings(
317 visibility_scope,
318 remainder_span,
319 pattern,
320 ArmHasGuard(false),
321 None,
322 );
323 block.unit()
324 }));
325
326 debug!("ast_block_stmts: pattern={:?}", pattern);
327 this.visit_primary_bindings(
328 pattern,
329 UserTypeProjections::none(),
330 &mut |this, _, _, _, node, span, _, _| {
331 this.storage_live_binding(block, node, span, OutsideGuard, true);
332 this.schedule_drop_for_binding(node, span, OutsideGuard);
333 },
334 )
335 }
336
337 // Enter the visibility scope, after evaluating the initializer.
338 if let Some(source_scope) = visibility_scope {
339 this.source_scope = source_scope;
340 }
341 last_remainder_scope = *remainder_scope;
342 }
343 }
344
345 let popped = this.block_context.pop();
346 assert!(popped.map_or(false, |bf| bf.is_statement()));
347 }
348
349 // Then, the block may have an optional trailing expression which is a “return” value
350 // of the block, which is stored into `destination`.
351 let tcx = this.tcx;
352 let destination_ty = destination.ty(&this.local_decls, tcx).ty;
353 if let Some(expr) = expr {
354 let tail_result_is_ignored =
355 destination_ty.is_unit() || this.block_context.currently_ignores_tail_results();
356 this.block_context
357 .push(BlockFrame::TailExpr { tail_result_is_ignored, span: expr.span });
358
359 unpack!(block = this.expr_into_dest(destination, block, expr));
360 let popped = this.block_context.pop();
361
362 assert!(popped.map_or(false, |bf| bf.is_tail_expr()));
363 } else {
364 // If a block has no trailing expression, then it is given an implicit return type.
365 // This return type is usually `()`, unless the block is diverging, in which case the
366 // return type is `!`. For the unit type, we need to actually return the unit, but in
367 // the case of `!`, no return value is required, as the block will never return.
368 // Opaque types of empty bodies also need this unit assignment, in order to infer that their
369 // type is actually unit. Otherwise there will be no defining use found in the MIR.
370 if destination_ty.is_unit() || matches!(destination_ty.kind(), ty::Opaque(..)) {
371 // We only want to assign an implicit `()` as the return value of the block if the
372 // block does not diverge. (Otherwise, we may try to assign a unit to a `!`-type.)
373 this.cfg.push_assign_unit(block, source_info, destination, this.tcx);
374 }
375 }
376 // Finally, we pop all the let scopes before exiting out from the scope of block
377 // itself.
378 for scope in let_scope_stack.into_iter().rev() {
379 unpack!(block = this.pop_scope((*scope, source_info), block));
380 }
381 // Restore the original source scope.
382 this.source_scope = outer_source_scope;
383 this.in_scope_unsafe = outer_in_scope_unsafe;
384 block.unit()
385 }
386
387 /// If we are entering an unsafe block, create a new source scope
388 fn update_source_scope_for_safety_mode(&mut self, span: Span, safety_mode: BlockSafety) {
389 debug!("update_source_scope_for({:?}, {:?})", span, safety_mode);
390 let new_unsafety = match safety_mode {
391 BlockSafety::Safe => return,
392 BlockSafety::BuiltinUnsafe => Safety::BuiltinUnsafe,
393 BlockSafety::ExplicitUnsafe(hir_id) => {
394 self.in_scope_unsafe = Safety::ExplicitUnsafe(hir_id);
395 Safety::ExplicitUnsafe(hir_id)
396 }
397 };
398
399 self.source_scope = self.new_source_scope(span, LintLevel::Inherited, Some(new_unsafety));
400 }
401 }