1 use crate::build
::{BlockAnd, BlockAndExtension, BlockFrame, Builder}
;
2 use crate::build
::ForGuard
::OutsideGuard
;
3 use crate::build
::matches
::ArmHasGuard
;
9 impl<'a
, 'gcx
, 'tcx
> Builder
<'a
, 'gcx
, 'tcx
> {
10 pub fn ast_block(&mut self,
11 destination
: &Place
<'tcx
>,
13 ast_block
: &'tcx hir
::Block
,
14 source_info
: SourceInfo
)
18 opt_destruction_scope
,
25 self.hir
.mirror(ast_block
);
26 self.in_opt_scope(opt_destruction_scope
.map(|de
|(de
, source_info
)), block
, move |this
| {
27 this
.in_scope((region_scope
, source_info
), LintLevel
::Inherited
, block
, move |this
| {
28 if targeted_by_break
{
29 // This is a `break`-able block
30 let exit_block
= this
.cfg
.start_new_block();
31 let block_exit
= this
.in_breakable_scope(
32 None
, exit_block
, destination
.clone(), |this
| {
33 this
.ast_block_stmts(destination
, block
, span
, stmts
, expr
,
36 this
.cfg
.terminate(unpack
!(block_exit
), source_info
,
37 TerminatorKind
::Goto { target: exit_block }
);
40 this
.ast_block_stmts(destination
, block
, span
, stmts
, expr
,
47 fn ast_block_stmts(&mut self,
48 destination
: &Place
<'tcx
>,
49 mut block
: BasicBlock
,
51 stmts
: Vec
<StmtRef
<'tcx
>>,
52 expr
: Option
<ExprRef
<'tcx
>>,
53 safety_mode
: BlockSafety
)
57 // This convoluted structure is to avoid using recursion as we walk down a list
58 // of statements. Basically, the structure we get back is something like:
60 // let x = <init> in {
62 // let y = <init> in {
69 // The let bindings are valid till the end of block so all we have to do is to pop all
70 // the let-scopes at the end.
72 // First we build all the statements in the block.
73 let mut let_scope_stack
= Vec
::with_capacity(8);
74 let outer_source_scope
= this
.source_scope
;
75 let outer_push_unsafe_count
= this
.push_unsafe_count
;
76 let outer_unpushed_unsafe
= this
.unpushed_unsafe
;
77 this
.update_source_scope_for_safety_mode(span
, safety_mode
);
79 let source_info
= this
.source_info(span
);
81 let Stmt { kind, opt_destruction_scope, span: stmt_span }
= this
.hir
.mirror(stmt
);
83 StmtKind
::Expr { scope, expr }
=> {
84 this
.block_context
.push(BlockFrame
::Statement { ignores_expr_result: true }
);
85 unpack
!(block
= this
.in_opt_scope(
86 opt_destruction_scope
.map(|de
|(de
, source_info
)), block
, |this
| {
87 let si
= (scope
, source_info
);
88 this
.in_scope(si
, LintLevel
::Inherited
, block
, |this
| {
89 let expr
= this
.hir
.mirror(expr
);
90 this
.stmt_expr(block
, expr
, Some(stmt_span
))
101 let ignores_expr_result
= if let PatternKind
::Wild
= *pattern
.kind
{
106 this
.block_context
.push(BlockFrame
::Statement { ignores_expr_result }
);
108 // Enter the remainder scope, i.e., the bindings' destruction scope.
109 this
.push_scope((remainder_scope
, source_info
));
110 let_scope_stack
.push(remainder_scope
);
112 // Declare the bindings, which may create a source scope.
113 let remainder_span
= remainder_scope
.span(this
.hir
.tcx(),
114 &this
.hir
.region_scope_tree
);
118 // Evaluate the initializer, if present.
119 if let Some(init
) = initializer
{
120 let initializer_span
= init
.span();
122 scope
= this
.declare_bindings(
128 Some((None
, initializer_span
)),
130 unpack
!(block
= this
.in_opt_scope(
131 opt_destruction_scope
.map(|de
|(de
, source_info
)), block
, |this
| {
132 let scope
= (init_scope
, source_info
);
133 this
.in_scope(scope
, lint_level
, block
, |this
| {
134 this
.expr_into_pattern(block
, pattern
, init
)
138 scope
= this
.declare_bindings(
139 None
, remainder_span
, lint_level
, &pattern
,
140 ArmHasGuard(false), None
);
142 debug
!("ast_block_stmts: pattern={:?}", pattern
);
145 UserTypeProjections
::none(),
146 &mut |this
, _
, _
, _
, node
, span
, _
, _
| {
147 this
.storage_live_binding(block
, node
, span
, OutsideGuard
);
148 this
.schedule_drop_for_binding(node
, span
, OutsideGuard
);
152 // Enter the source scope, after evaluating the initializer.
153 if let Some(source_scope
) = scope
{
154 this
.source_scope
= source_scope
;
159 let popped
= this
.block_context
.pop();
160 assert
!(popped
.map_or(false, |bf
|bf
.is_statement()));
163 // Then, the block may have an optional trailing expression which is a “return” value
164 // of the block, which is stored into `destination`.
165 let tcx
= this
.hir
.tcx();
166 let destination_ty
= destination
.ty(&this
.local_decls
, tcx
).to_ty(tcx
);
167 if let Some(expr
) = expr
{
168 let tail_result_is_ignored
= destination_ty
.is_unit() ||
169 this
.block_context
.currently_ignores_tail_results();
170 this
.block_context
.push(BlockFrame
::TailExpr { tail_result_is_ignored }
);
172 unpack
!(block
= this
.into(destination
, block
, expr
));
173 let popped
= this
.block_context
.pop();
175 assert
!(popped
.map_or(false, |bf
|bf
.is_tail_expr()));
177 // If a block has no trailing expression, then it is given an implicit return type.
178 // This return type is usually `()`, unless the block is diverging, in which case the
179 // return type is `!`. For the unit type, we need to actually return the unit, but in
180 // the case of `!`, no return value is required, as the block will never return.
181 if destination_ty
.is_unit() {
182 // We only want to assign an implicit `()` as the return value of the block if the
183 // block does not diverge. (Otherwise, we may try to assign a unit to a `!`-type.)
184 this
.cfg
.push_assign_unit(block
, source_info
, destination
);
187 // Finally, we pop all the let scopes before exiting out from the scope of block
189 for scope
in let_scope_stack
.into_iter().rev() {
190 unpack
!(block
= this
.pop_scope((scope
, source_info
), block
));
192 // Restore the original source scope.
193 this
.source_scope
= outer_source_scope
;
194 this
.push_unsafe_count
= outer_push_unsafe_count
;
195 this
.unpushed_unsafe
= outer_unpushed_unsafe
;
199 /// If we are changing the safety mode, create a new source scope
200 fn update_source_scope_for_safety_mode(&mut self,
202 safety_mode
: BlockSafety
)
204 debug
!("update_source_scope_for({:?}, {:?})", span
, safety_mode
);
205 let new_unsafety
= match safety_mode
{
206 BlockSafety
::Safe
=> None
,
207 BlockSafety
::ExplicitUnsafe(node_id
) => {
208 assert_eq
!(self.push_unsafe_count
, 0);
209 match self.unpushed_unsafe
{
213 self.unpushed_unsafe
= Safety
::ExplicitUnsafe(node_id
);
214 Some(Safety
::ExplicitUnsafe(node_id
))
216 BlockSafety
::PushUnsafe
=> {
217 self.push_unsafe_count
+= 1;
218 Some(Safety
::BuiltinUnsafe
)
220 BlockSafety
::PopUnsafe
=> {
221 self.push_unsafe_count
=
222 self.push_unsafe_count
.checked_sub(1).unwrap_or_else(|| {
223 span_bug
!(span
, "unsafe count underflow")
225 if self.push_unsafe_count
== 0 {
226 Some(self.unpushed_unsafe
)
233 if let Some(unsafety
) = new_unsafety
{
234 self.source_scope
= self.new_source_scope(
235 span
, LintLevel
::Inherited
, Some(unsafety
));