]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_mir_build/src/build/block.rs
New upstream version 1.67.1+dfsg1
[rustc.git] / compiler / rustc_mir_build / src / build / block.rs
CommitLineData
dfeec247
XL
1use crate::build::ForGuard::OutsideGuard;
2use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
f2b60f7d 3use rustc_middle::middle::region::Scope;
17df50a5 4use rustc_middle::thir::*;
5e7ed085 5use rustc_middle::{mir::*, ty};
dfeec247 6use rustc_span::Span;
e9174d1e 7
dc9dc135 8impl<'a, 'tcx> Builder<'a, 'tcx> {
923072b8 9 pub(crate) fn ast_block(
dfeec247 10 &mut self,
ba9703b0 11 destination: Place<'tcx>,
dfeec247 12 block: BasicBlock,
f2b60f7d 13 ast_block: BlockId,
dfeec247
XL
14 source_info: SourceInfo,
15 ) -> BlockAnd<()> {
ea8adc8c
XL
16 let Block {
17 region_scope,
18 opt_destruction_scope,
19 span,
17df50a5 20 ref stmts,
ea8adc8c
XL
21 expr,
22 targeted_by_break,
dfeec247 23 safety_mode,
f2b60f7d 24 } = self.thir[ast_block];
17df50a5 25 let expr = expr.map(|expr| &self.thir[expr]);
dfeec247 26 self.in_opt_scope(opt_destruction_scope.map(|de| (de, source_info)), move |this| {
dc9dc135 27 this.in_scope((region_scope, source_info), LintLevel::Inherited, move |this| {
041b39d2 28 if targeted_by_break {
29967ef6
XL
29 this.in_breakable_scope(None, destination, span, |this| {
30 Some(this.ast_block_stmts(
31 destination,
32 block,
33 span,
17df50a5 34 &stmts,
29967ef6
XL
35 expr,
36 safety_mode,
f2b60f7d 37 region_scope,
29967ef6
XL
38 ))
39 })
041b39d2 40 } else {
f2b60f7d
FG
41 this.ast_block_stmts(
42 destination,
43 block,
44 span,
45 &stmts,
46 expr,
47 safety_mode,
48 region_scope,
49 )
041b39d2
XL
50 }
51 })
cc61c64b
XL
52 })
53 }
3157f602 54
dfeec247
XL
55 fn ast_block_stmts(
56 &mut self,
ba9703b0 57 destination: Place<'tcx>,
dfeec247
XL
58 mut block: BasicBlock,
59 span: Span,
17df50a5
XL
60 stmts: &[StmtId],
61 expr: Option<&Expr<'tcx>>,
dfeec247 62 safety_mode: BlockSafety,
f2b60f7d 63 region_scope: Scope,
dfeec247 64 ) -> BlockAnd<()> {
cc61c64b
XL
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.
ea8adc8c 83 let mut let_scope_stack = Vec::with_capacity(8);
94b46f34 84 let outer_source_scope = this.source_scope;
136023e0 85 let outer_in_scope_unsafe = this.in_scope_unsafe;
f2b60f7d
FG
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;
94b46f34 91 this.update_source_scope_for_safety_mode(span, safety_mode);
ea8adc8c 92
041b39d2 93 let source_info = this.source_info(span);
17df50a5
XL
94 for stmt in stmts {
95 let Stmt { ref kind, opt_destruction_scope } = this.thir[*stmt];
cc61c64b 96 match kind {
17df50a5 97 StmtKind::Expr { scope, expr } => {
0bf4aa26 98 this.block_context.push(BlockFrame::Statement { ignores_expr_result: true });
dfeec247
XL
99 unpack!(
100 block = this.in_opt_scope(
101 opt_destruction_scope.map(|de| (de, source_info)),
102 |this| {
17df50a5 103 let si = (*scope, source_info);
dfeec247 104 this.in_scope(si, LintLevel::Inherited, |this| {
17df50a5 105 this.stmt_expr(block, &this.thir[*expr], Some(*scope))
dfeec247
XL
106 })
107 }
108 )
109 );
cc61c64b 110 }
f2b60f7d
FG
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> };`,
487cf647 120 // the `<else>` block is nested in the parent scope enclosing this statement.
f2b60f7d
FG
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;
f2b60f7d
FG
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| {
2b03887a
FG
229 this.declare_bindings(
230 visibility_scope,
231 remainder_span,
232 pattern,
487cf647 233 None,
2b03887a
FG
234 Some((None, 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 );
f2b60f7d
FG
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 }
17df50a5
XL
274 StmtKind::Let {
275 remainder_scope,
276 init_scope,
277 ref pattern,
278 initializer,
279 lint_level,
f2b60f7d 280 else_block: None,
17df50a5 281 } => {
f2b60f7d 282 let ignores_expr_result = matches!(pattern.kind, PatKind::Wild);
0bf4aa26
XL
283 this.block_context.push(BlockFrame::Statement { ignores_expr_result });
284
0731742a 285 // Enter the remainder scope, i.e., the bindings' destruction scope.
6a06907d 286 this.push_scope((*remainder_scope, source_info));
ea8adc8c 287 let_scope_stack.push(remainder_scope);
3157f602 288
94b46f34 289 // Declare the bindings, which may create a source scope.
6a06907d 290 let remainder_span = remainder_scope.span(this.tcx, this.region_scope_tree);
8faf50e0 291
dc9dc135
XL
292 let visibility_scope =
293 Some(this.new_source_scope(remainder_span, LintLevel::Inherited, None));
3157f602 294
cc61c64b
XL
295 // Evaluate the initializer, if present.
296 if let Some(init) = initializer {
17df50a5 297 let init = &this.thir[*init];
6a06907d 298 let initializer_span = init.span;
8faf50e0 299
dfeec247
XL
300 unpack!(
301 block = this.in_opt_scope(
302 opt_destruction_scope.map(|de| (de, source_info)),
303 |this| {
6a06907d
XL
304 let scope = (*init_scope, source_info);
305 this.in_scope(scope, *lint_level, |this| {
f2b60f7d
FG
306 this.declare_bindings(
307 visibility_scope,
308 remainder_span,
309 pattern,
487cf647 310 None,
f2b60f7d
FG
311 Some((None, initializer_span)),
312 );
2b03887a
FG
313 this.expr_into_pattern(block, &pattern, init)
314 // irrefutable pattern
dfeec247 315 })
064997fb 316 },
dfeec247 317 )
064997fb 318 )
cc61c64b 319 } else {
6a06907d
XL
320 let scope = (*init_scope, source_info);
321 unpack!(this.in_scope(scope, *lint_level, |this| {
dc9dc135
XL
322 this.declare_bindings(
323 visibility_scope,
324 remainder_span,
6a06907d 325 pattern,
487cf647 326 None,
dc9dc135
XL
327 None,
328 );
329 block.unit()
330 }));
8faf50e0 331
0731742a 332 debug!("ast_block_stmts: pattern={:?}", pattern);
f9f354fc 333 this.visit_primary_bindings(
17df50a5 334 pattern,
0731742a 335 UserTypeProjections::none(),
0bf4aa26 336 &mut |this, _, _, _, node, span, _, _| {
74b04a01 337 this.storage_live_binding(block, node, span, OutsideGuard, true);
0bf4aa26 338 this.schedule_drop_for_binding(node, span, OutsideGuard);
dfeec247
XL
339 },
340 )
cc61c64b
XL
341 }
342
dc9dc135
XL
343 // Enter the visibility scope, after evaluating the initializer.
344 if let Some(source_scope) = visibility_scope {
94b46f34 345 this.source_scope = source_scope;
54a0048b 346 }
f2b60f7d 347 last_remainder_scope = *remainder_scope;
9cc50fc6
SL
348 }
349 }
0bf4aa26
XL
350
351 let popped = this.block_context.pop();
dfeec247 352 assert!(popped.map_or(false, |bf| bf.is_statement()));
cc61c64b 353 }
0bf4aa26 354
cc61c64b 355 // Then, the block may have an optional trailing expression which is a “return” value
0bf4aa26 356 // of the block, which is stored into `destination`.
6a06907d 357 let tcx = this.tcx;
532ac7d7 358 let destination_ty = destination.ty(&this.local_decls, tcx).ty;
cc61c64b 359 if let Some(expr) = expr {
dfeec247
XL
360 let tail_result_is_ignored =
361 destination_ty.is_unit() || this.block_context.currently_ignores_tail_results();
6a06907d
XL
362 this.block_context
363 .push(BlockFrame::TailExpr { tail_result_is_ignored, span: expr.span });
0bf4aa26 364
6a06907d 365 unpack!(block = this.expr_into_dest(destination, block, expr));
0bf4aa26
XL
366 let popped = this.block_context.pop();
367
dfeec247 368 assert!(popped.map_or(false, |bf| bf.is_tail_expr()));
cc61c64b 369 } else {
2c00a5a8
XL
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.
5e7ed085
FG
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() || matches!(destination_ty.kind(), ty::Opaque(..)) {
2c00a5a8
XL
377 // We only want to assign an implicit `()` as the return value of the block if the
378 // block does not diverge. (Otherwise, we may try to assign a unit to a `!`-type.)
6a06907d 379 this.cfg.push_assign_unit(block, source_info, destination, this.tcx);
2c00a5a8 380 }
cc61c64b
XL
381 }
382 // Finally, we pop all the let scopes before exiting out from the scope of block
383 // itself.
ea8adc8c 384 for scope in let_scope_stack.into_iter().rev() {
6a06907d 385 unpack!(block = this.pop_scope((*scope, source_info), block));
cc61c64b 386 }
94b46f34
XL
387 // Restore the original source scope.
388 this.source_scope = outer_source_scope;
136023e0 389 this.in_scope_unsafe = outer_in_scope_unsafe;
cc61c64b 390 block.unit()
e9174d1e 391 }
ea8adc8c 392
5e7ed085 393 /// If we are entering an unsafe block, create a new source scope
dfeec247 394 fn update_source_scope_for_safety_mode(&mut self, span: Span, safety_mode: BlockSafety) {
94b46f34 395 debug!("update_source_scope_for({:?}, {:?})", span, safety_mode);
ea8adc8c 396 let new_unsafety = match safety_mode {
5e7ed085
FG
397 BlockSafety::Safe => return,
398 BlockSafety::BuiltinUnsafe => Safety::BuiltinUnsafe,
532ac7d7 399 BlockSafety::ExplicitUnsafe(hir_id) => {
136023e0 400 self.in_scope_unsafe = Safety::ExplicitUnsafe(hir_id);
5e7ed085 401 Safety::ExplicitUnsafe(hir_id)
ea8adc8c 402 }
ea8adc8c
XL
403 };
404
5e7ed085 405 self.source_scope = self.new_source_scope(span, LintLevel::Inherited, Some(new_unsafety));
ea8adc8c 406 }
e9174d1e 407}