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