1use rustc_middle::middle::region::Scope;
2use rustc_middle::mir::*;
3use rustc_middle::thir::*;
4use rustc_middle::{span_bug, ty};
5use rustc_span::Span;
6use tracing::debug;
78use crate::builder::ForGuard::OutsideGuard;
9use crate::builder::matches::{DeclareLetBindings, ScheduleDrops};
10use crate::builder::scope::LintLevel;
11use crate::builder::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
1213impl<'a, 'tcx> Builder<'a, 'tcx> {
14pub(crate) fn ast_block(
15&mut self,
16 destination: Place<'tcx>,
17 block: BasicBlock,
18 ast_block: BlockId,
19 source_info: SourceInfo,
20 ) -> BlockAnd<()> {
21let Block { region_scope, span, ref stmts, expr, targeted_by_break, safety_mode: _ } =
22self.thir[ast_block];
23self.in_scope((region_scope, source_info), LintLevel::Inherited, move |this| {
24if targeted_by_break {
25this.in_breakable_scope(None, destination, span, |this| {
26Some(this.ast_block_stmts(destination, block, span, stmts, expr, region_scope))
27 })
28 } else {
29this.ast_block_stmts(destination, block, span, stmts, expr, region_scope)
30 }
31 })
32 }
3334fn ast_block_stmts(
35&mut self,
36 destination: Place<'tcx>,
37mut block: BasicBlock,
38 span: Span,
39 stmts: &[StmtId],
40 expr: Option<ExprId>,
41 region_scope: Scope,
42 ) -> BlockAnd<()> {
43let this = self; // See "LET_THIS_SELF".
4445 // This convoluted structure is to avoid using recursion as we walk down a list
46 // of statements. Basically, the structure we get back is something like:
47 //
48 // let x = <init> in {
49 // expr1;
50 // let y = <init> in {
51 // expr2;
52 // expr3;
53 // ...
54 // }
55 // }
56 //
57 // The let bindings are valid till the end of block so all we have to do is to pop all
58 // the let-scopes at the end.
59 //
60 // First we build all the statements in the block.
61let mut let_scope_stack = Vec::with_capacity(8);
62let outer_source_scope = this.source_scope;
63// This scope information is kept for breaking out of the parent remainder scope in case
64 // one let-else pattern matching fails.
65 // By doing so, we can be sure that even temporaries that receive extended lifetime
66 // assignments are dropped, too.
67let mut last_remainder_scope = region_scope;
6869let source_info = this.source_info(span);
70for stmt in stmts {
71let Stmt { ref kind } = this.thir[*stmt];
72match kind {
73 StmtKind::Expr { scope, expr } => {
74 this.block_context.push(BlockFrame::Statement { ignores_expr_result: true });
75let si = (*scope, source_info);
76 block = this
77 .in_scope(si, LintLevel::Inherited, |this| {
78 this.stmt_expr(block, *expr, Some(*scope))
79 })
80 .into_block();
81 }
82 StmtKind::Let {
83 remainder_scope,
84 init_scope,
85 pattern,
86 initializer: Some(initializer),
87 hir_id,
88 else_block: Some(else_block),
89 span: _,
90 } => {
91// When lowering the statement `let <pat> = <expr> else { <else> };`,
92 // the `<else>` block is nested in the parent scope enclosing this statement.
93 // That scope is usually either the enclosing block scope,
94 // or the remainder scope of the last statement.
95 // This is to make sure that temporaries instantiated in `<expr>` are dropped
96 // as well.
97 // In addition, even though bindings in `<pat>` only come into scope if
98 // the pattern matching passes, in the MIR building the storages for them
99 // are declared as live any way.
100 // This is similar to `let x;` statements without an initializer expression,
101 // where the value of `x` in this example may or may be assigned,
102 // because the storage for their values may not be live after all due to
103 // failure in pattern matching.
104 // For this reason, we declare those storages as live but we do not schedule
105 // any drop yet- they are scheduled later after the pattern matching.
106 // The generated MIR will have `StorageDead` whenever the control flow breaks out
107 // of the parent scope, regardless of the result of the pattern matching.
108 // However, the drops are inserted in MIR only when the control flow breaks out of
109 // the scope of the remainder scope associated with this `let .. else` statement.
110 // Pictorial explanation of the scope structure:
111 // ┌─────────────────────────────────┐
112 // │ Scope of the enclosing block, │
113 // │ or the last remainder scope │
114 // │ ┌───────────────────────────┐ │
115 // │ │ Scope for <else> block │ │
116 // │ └───────────────────────────┘ │
117 // │ ┌───────────────────────────┐ │
118 // │ │ Remainder scope of │ │
119 // │ │ this let-else statement │ │
120 // │ │ ┌─────────────────────┐ │ │
121 // │ │ │ <expr> scope │ │ │
122 // │ │ └─────────────────────┘ │ │
123 // │ │ extended temporaries in │ │
124 // │ │ <expr> lives in this │ │
125 // │ │ scope │ │
126 // │ │ ┌─────────────────────┐ │ │
127 // │ │ │ Scopes for the rest │ │ │
128 // │ │ └─────────────────────┘ │ │
129 // │ └───────────────────────────┘ │
130 // └─────────────────────────────────┘
131 // Generated control flow:
132 // │ let Some(x) = y() else { return; }
133 // │
134 // ┌────────▼───────┐
135 // │ evaluate y() │
136 // └────────┬───────┘
137 // │ ┌────────────────┐
138 // ┌────────▼───────┐ │Drop temporaries│
139 // │Test the pattern├──────►in y() │
140 // └────────┬───────┘ │because breaking│
141 // │ │out of <expr> │
142 // ┌────────▼───────┐ │scope │
143 // │Move value into │ └───────┬────────┘
144 // │binding x │ │
145 // └────────┬───────┘ ┌───────▼────────┐
146 // │ │Drop extended │
147 // ┌────────▼───────┐ │temporaries in │
148 // │Drop temporaries│ │<expr> because │
149 // │in y() │ │breaking out of │
150 // │because breaking│ │remainder scope │
151 // │out of <expr> │ └───────┬────────┘
152 // │scope │ │
153 // └────────┬───────┘ ┌───────▼────────┐
154 // │ │Enter <else> ├────────►
155 // ┌────────▼───────┐ │block │ return;
156 // │Continue... │ └────────────────┘
157 // └────────────────┘
158159let ignores_expr_result = #[allow(non_exhaustive_omitted_patterns)] match pattern.kind {
PatKind::Wild => true,
_ => false,
}matches!(pattern.kind, PatKind::Wild);
160 this.block_context.push(BlockFrame::Statement { ignores_expr_result });
161162// Lower the `else` block first because its parent scope is actually
163 // enclosing the rest of the `let .. else ..` parts.
164let else_block_span = this.thir[*else_block].span;
165// This place is not really used because this destination place
166 // should never be used to take values at the end of the failure
167 // block.
168let dummy_place = this.temp(this.tcx.types.never, else_block_span);
169let failure_entry = this.cfg.start_new_block();
170let failure_block;
171 failure_block = this
172 .ast_block(
173 dummy_place,
174 failure_entry,
175*else_block,
176 this.source_info(else_block_span),
177 )
178 .into_block();
179 this.cfg.terminate(
180 failure_block,
181 this.source_info(else_block_span),
182 TerminatorKind::Unreachable,
183 );
184185// Declare the bindings, which may create a source scope.
186let remainder_span = remainder_scope.span(this.tcx, this.region_scope_tree);
187 this.push_scope((*remainder_scope, source_info));
188 let_scope_stack.push(remainder_scope);
189190let visibility_scope =
191Some(this.new_source_scope(remainder_span, LintLevel::Inherited));
192193let initializer_span = this.thir[*initializer].span;
194let scope = (*init_scope, source_info);
195let lint_level = LintLevel::Explicit(*hir_id);
196let failure_and_block = this.in_scope(scope, lint_level, |this| {
197 this.declare_bindings(
198 visibility_scope,
199 remainder_span,
200 pattern,
201None,
202Some((Some(&destination), initializer_span)),
203 );
204let else_block_span = this.thir[*else_block].span;
205let (matching, failure) =
206 this.in_if_then_scope(last_remainder_scope, else_block_span, |this| {
207 this.lower_let_expr(
208 block,
209*initializer,
210 pattern,
211None,
212 initializer_span,
213 DeclareLetBindings::No,
214 )
215 });
216 matching.and(failure)
217 });
218let failure = { let BlockAnd(b, v) = failure_and_block; block = b; v }unpack!(block = failure_and_block);
219 this.cfg.goto(failure, source_info, failure_entry);
220221if let Some(source_scope) = visibility_scope {
222 this.source_scope = source_scope;
223 }
224 last_remainder_scope = *remainder_scope;
225 }
226 StmtKind::Let { init_scope, initializer: None, else_block: Some(_), .. } => {
227::rustc_middle::util::bug::span_bug_fmt(init_scope.span(this.tcx,
this.region_scope_tree),
format_args!("initializer is missing, but else block is present in this let binding"))span_bug!(
228 init_scope.span(this.tcx, this.region_scope_tree),
229"initializer is missing, but else block is present in this let binding",
230 )231 }
232 StmtKind::Let {
233 remainder_scope,
234 init_scope,
235 pattern,
236 initializer,
237 hir_id,
238 else_block: None,
239 span: _,
240 } => {
241let ignores_expr_result = #[allow(non_exhaustive_omitted_patterns)] match pattern.kind {
PatKind::Wild => true,
_ => false,
}matches!(pattern.kind, PatKind::Wild);
242 this.block_context.push(BlockFrame::Statement { ignores_expr_result });
243244// Enter the remainder scope, i.e., the bindings' destruction scope.
245this.push_scope((*remainder_scope, source_info));
246 let_scope_stack.push(remainder_scope);
247248// Declare the bindings, which may create a source scope.
249let remainder_span = remainder_scope.span(this.tcx, this.region_scope_tree);
250251let visibility_scope =
252Some(this.new_source_scope(remainder_span, LintLevel::Inherited));
253254// Evaluate the initializer, if present.
255let lint_level = LintLevel::Explicit(*hir_id);
256if let Some(init) = *initializer {
257let initializer_span = this.thir[init].span;
258let scope = (*init_scope, source_info);
259260 block = this
261 .in_scope(scope, lint_level, |this| {
262 this.declare_bindings(
263 visibility_scope,
264 remainder_span,
265 pattern,
266None,
267Some((None, initializer_span)),
268 );
269 this.expr_into_pattern(block, &pattern, init)
270// irrefutable pattern
271})
272 .into_block();
273 } else {
274let scope = (*init_scope, source_info);
275let _: BlockAnd<()> = this.in_scope(scope, lint_level, |this| {
276 this.declare_bindings(
277 visibility_scope,
278 remainder_span,
279 pattern,
280None,
281None,
282 );
283 block.unit()
284 });
285286{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_mir_build/src/builder/block.rs:286",
"rustc_mir_build::builder::block", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/builder/block.rs"),
::tracing_core::__macro_support::Option::Some(286u32),
::tracing_core::__macro_support::Option::Some("rustc_mir_build::builder::block"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("ast_block_stmts: pattern={0:?}",
pattern) as &dyn Value))])
});
} else { ; }
};debug!("ast_block_stmts: pattern={:?}", pattern);
287 this.visit_primary_bindings(pattern, &mut |this, node, span| {
288 this.storage_live_binding(
289 block,
290 node,
291 span,
292false,
293 OutsideGuard,
294 ScheduleDrops::Yes,
295 );
296 this.schedule_drop_for_binding(node, span, OutsideGuard);
297 })
298 }
299300// Enter the visibility scope, after evaluating the initializer.
301if let Some(source_scope) = visibility_scope {
302 this.source_scope = source_scope;
303 }
304 last_remainder_scope = *remainder_scope;
305 }
306 }
307308let popped = this.block_context.pop();
309if !popped.is_some_and(|bf| bf.is_statement()) {
::core::panicking::panic("assertion failed: popped.is_some_and(|bf| bf.is_statement())")
};assert!(popped.is_some_and(|bf| bf.is_statement()));
310 }
311312// Then, the block may have an optional trailing expression which is a “return” value
313 // of the block, which is stored into `destination`.
314let tcx = this.tcx;
315let destination_ty = destination.ty(&this.local_decls, tcx).ty;
316if let Some(expr_id) = expr {
317let expr = &this.thir[expr_id];
318let tail_result_is_ignored =
319destination_ty.is_unit() || this.block_context.currently_ignores_tail_results();
320this.block_context.push(BlockFrame::TailExpr {
321 info: BlockTailInfo { tail_result_is_ignored, span: expr.span },
322 });
323324block = this.expr_into_dest(destination, block, expr_id).into_block();
325let popped = this.block_context.pop();
326327if !popped.is_some_and(|bf| bf.is_tail_expr()) {
::core::panicking::panic("assertion failed: popped.is_some_and(|bf| bf.is_tail_expr())")
};assert!(popped.is_some_and(|bf| bf.is_tail_expr()));
328 } else {
329// If a block has no trailing expression, then it is given an implicit return type.
330 // This return type is usually `()`, unless the block is diverging, in which case the
331 // return type is `!`. For the unit type, we need to actually return the unit, but in
332 // the case of `!`, no return value is required, as the block will never return.
333 // Opaque types of empty bodies also need this unit assignment, in order to infer that their
334 // type is actually unit. Otherwise there will be no defining use found in the MIR.
335if destination_ty.is_unit()
336 || #[allow(non_exhaustive_omitted_patterns)] match destination_ty.kind() {
ty::Alias(ty::Opaque, ..) => true,
_ => false,
}matches!(destination_ty.kind(), ty::Alias(ty::Opaque, ..))337 {
338// We only want to assign an implicit `()` as the return value of the block if the
339 // block does not diverge. (Otherwise, we may try to assign a unit to a `!`-type.)
340this.cfg.push_assign_unit(block, source_info, destination, this.tcx);
341 }
342 }
343// Finally, we pop all the let scopes before exiting out from the scope of block
344 // itself.
345for scope in let_scope_stack.into_iter().rev() {
346 block = this.pop_scope((*scope, source_info), block).into_block();
347 }
348// Restore the original source scope.
349this.source_scope = outer_source_scope;
350block.unit()
351 }
352}