]>
Commit | Line | Data |
---|---|---|
dfeec247 XL |
1 | use crate::build::ForGuard::OutsideGuard; |
2 | use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder}; | |
f2b60f7d | 3 | use rustc_middle::middle::region::Scope; |
17df50a5 | 4 | use rustc_middle::thir::*; |
5e7ed085 | 5 | use rustc_middle::{mir::*, ty}; |
dfeec247 | 6 | use rustc_span::Span; |
e9174d1e | 7 | |
dc9dc135 | 8 | impl<'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, |
9c376795 | 234 | Some((Some(&destination), initializer_span)), |
2b03887a FG |
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. | |
9c376795 FG |
376 | if destination_ty.is_unit() |
377 | || matches!(destination_ty.kind(), ty::Alias(ty::Opaque, ..)) | |
378 | { | |
2c00a5a8 XL |
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.) | |
6a06907d | 381 | this.cfg.push_assign_unit(block, source_info, destination, this.tcx); |
2c00a5a8 | 382 | } |
cc61c64b XL |
383 | } |
384 | // Finally, we pop all the let scopes before exiting out from the scope of block | |
385 | // itself. | |
ea8adc8c | 386 | for scope in let_scope_stack.into_iter().rev() { |
6a06907d | 387 | unpack!(block = this.pop_scope((*scope, source_info), block)); |
cc61c64b | 388 | } |
94b46f34 XL |
389 | // Restore the original source scope. |
390 | this.source_scope = outer_source_scope; | |
136023e0 | 391 | this.in_scope_unsafe = outer_in_scope_unsafe; |
cc61c64b | 392 | block.unit() |
e9174d1e | 393 | } |
ea8adc8c | 394 | |
5e7ed085 | 395 | /// If we are entering an unsafe block, create a new source scope |
dfeec247 | 396 | fn update_source_scope_for_safety_mode(&mut self, span: Span, safety_mode: BlockSafety) { |
94b46f34 | 397 | debug!("update_source_scope_for({:?}, {:?})", span, safety_mode); |
ea8adc8c | 398 | let new_unsafety = match safety_mode { |
5e7ed085 FG |
399 | BlockSafety::Safe => return, |
400 | BlockSafety::BuiltinUnsafe => Safety::BuiltinUnsafe, | |
532ac7d7 | 401 | BlockSafety::ExplicitUnsafe(hir_id) => { |
136023e0 | 402 | self.in_scope_unsafe = Safety::ExplicitUnsafe(hir_id); |
5e7ed085 | 403 | Safety::ExplicitUnsafe(hir_id) |
ea8adc8c | 404 | } |
ea8adc8c XL |
405 | }; |
406 | ||
5e7ed085 | 407 | self.source_scope = self.new_source_scope(span, LintLevel::Inherited, Some(new_unsafety)); |
ea8adc8c | 408 | } |
e9174d1e | 409 | } |