]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_mir_build/src/build/block.rs
New upstream version 1.79.0+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<()> {
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}