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}
;
9 impl<'a
, 'tcx
> Builder
<'a
, 'tcx
> {
10 pub(crate) fn ast_block(
12 destination
: Place
<'tcx
>,
15 source_info
: SourceInfo
,
19 opt_destruction_scope
,
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(
58 destination
: Place
<'tcx
>,
59 mut block
: BasicBlock
,
62 expr
: Option
<&Expr
<'tcx
>>,
63 safety_mode
: BlockSafety
,
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:
71 // let x = <init> in {
73 // let y = <init> in {
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.
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
);
94 let source_info
= this
.source_info(span
);
96 let Stmt { ref kind, opt_destruction_scope }
= this
.thir
[*stmt
];
98 StmtKind
::Expr { scope, expr }
=> {
99 this
.block_context
.push(BlockFrame
::Statement { ignores_expr_result: true }
);
101 block
= this
.in_opt_scope(
102 opt_destruction_scope
.map(|de
| (de
, source_info
)),
104 let si
= (*scope
, source_info
);
105 this
.in_scope(si
, LintLevel
::Inherited
, |this
| {
106 this
.stmt_expr(block
, &this
.thir
[*expr
], Some(*scope
))
116 initializer
: Some(initializer
),
118 else_block
: Some(else_block
),
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
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 │ │
155 // │ │ ┌─────────────────────┐ │ │
156 // │ │ │ Scopes for the rest │ │ │
157 // │ │ └─────────────────────┘ │ │
158 // │ └───────────────────────────┘ │
159 // └─────────────────────────────────┘
160 // Generated control flow:
161 // │ let Some(x) = y() else { return; }
163 // ┌────────▼───────┐
165 // └────────┬───────┘
166 // │ ┌────────────────┐
167 // ┌────────▼───────┐ │Drop temporaries│
168 // │Test the pattern├──────►in y() │
169 // └────────┬───────┘ │because breaking│
170 // │ │out of <expr> │
171 // ┌────────▼───────┐ │scope │
172 // │Move value into │ └───────┬────────┘
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> │ └───────┬────────┘
182 // └────────┬───────┘ ┌───────▼────────┐
183 // │ │Enter <else> ├────────►
184 // ┌────────▼───────┐ │block │ return;
185 // │Continue... │ └────────────────┘
186 // └────────────────┘
188 let ignores_expr_result
= matches
!(pattern
.kind
, PatKind
::Wild
);
189 this
.block_context
.push(BlockFrame
::Statement { ignores_expr_result }
);
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
197 let dummy_place
= this
.temp(this
.tcx
.types
.never
, else_block_span
);
198 let failure_entry
= this
.cfg
.start_new_block();
201 failure_block
= this
.ast_block(
205 this
.source_info(else_block_span
),
210 this
.source_info(else_block_span
),
211 TerminatorKind
::Unreachable
,
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
);
219 let visibility_scope
=
220 Some(this
.new_source_scope(remainder_span
, LintLevel
::Inherited
, None
));
222 let init
= &this
.thir
[*initializer
];
223 let initializer_span
= init
.span
;
224 this
.declare_bindings(
229 Some((None
, initializer_span
)),
231 this
.visit_primary_bindings(
233 UserTypeProjections
::none(),
234 &mut |this
, _
, _
, _
, node
, span
, _
, _
| {
235 this
.storage_live_binding(block
, node
, span
, OutsideGuard
, true);
238 let failure
= unpack
!(
239 block
= this
.in_opt_scope(
240 opt_destruction_scope
.map(|de
| (de
, source_info
)),
242 let scope
= (*init_scope
, source_info
);
243 this
.in_scope(scope
, *lint_level
, |this
| {
249 &last_remainder_scope
,
256 this
.cfg
.goto(failure
, source_info
, failure_entry
);
258 if let Some(source_scope
) = visibility_scope
{
259 this
.source_scope
= source_scope
;
261 last_remainder_scope
= *remainder_scope
;
263 StmtKind
::Let { init_scope, initializer: None, else_block: Some(_), .. }
=> {
265 init_scope
.span(this
.tcx
, this
.region_scope_tree
),
266 "initializer is missing, but else block is present in this let binding",
277 let ignores_expr_result
= matches
!(pattern
.kind
, PatKind
::Wild
);
278 this
.block_context
.push(BlockFrame
::Statement { ignores_expr_result }
);
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
);
284 // Declare the bindings, which may create a source scope.
285 let remainder_span
= remainder_scope
.span(this
.tcx
, this
.region_scope_tree
);
287 let visibility_scope
=
288 Some(this
.new_source_scope(remainder_span
, LintLevel
::Inherited
, None
));
290 // Evaluate the initializer, if present.
291 if let Some(init
) = initializer
{
292 let init
= &this
.thir
[*init
];
293 let initializer_span
= init
.span
;
296 block
= this
.in_opt_scope(
297 opt_destruction_scope
.map(|de
| (de
, source_info
)),
299 let scope
= (*init_scope
, source_info
);
300 this
.in_scope(scope
, *lint_level
, |this
| {
301 this
.declare_bindings(
306 Some((None
, initializer_span
)),
308 this
.expr_into_pattern(block
, &pattern
, init
) // irrefutable pattern
314 let scope
= (*init_scope
, source_info
);
315 unpack
!(this
.in_scope(scope
, *lint_level
, |this
| {
316 this
.declare_bindings(
326 debug
!("ast_block_stmts: pattern={:?}", pattern
);
327 this
.visit_primary_bindings(
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
);
337 // Enter the visibility scope, after evaluating the initializer.
338 if let Some(source_scope
) = visibility_scope
{
339 this
.source_scope
= source_scope
;
341 last_remainder_scope
= *remainder_scope
;
345 let popped
= this
.block_context
.pop();
346 assert
!(popped
.map_or(false, |bf
| bf
.is_statement()));
349 // Then, the block may have an optional trailing expression which is a “return” value
350 // of the block, which is stored into `destination`.
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();
357 .push(BlockFrame
::TailExpr { tail_result_is_ignored, span: expr.span }
);
359 unpack
!(block
= this
.expr_into_dest(destination
, block
, expr
));
360 let popped
= this
.block_context
.pop();
362 assert
!(popped
.map_or(false, |bf
| bf
.is_tail_expr()));
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
);
376 // Finally, we pop all the let scopes before exiting out from the scope of block
378 for scope
in let_scope_stack
.into_iter().rev() {
379 unpack
!(block
= this
.pop_scope((*scope
, source_info
), block
));
381 // Restore the original source scope.
382 this
.source_scope
= outer_source_scope
;
383 this
.in_scope_unsafe
= outer_in_scope_unsafe
;
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
)
399 self.source_scope
= self.new_source_scope(span
, LintLevel
::Inherited
, Some(new_unsafety
));