1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 use build
::{BlockAnd, BlockAndExtension, Builder}
;
12 use build
::ForGuard
::OutsideGuard
;
13 use build
::matches
::ArmHasGuard
;
19 impl<'a
, 'gcx
, 'tcx
> Builder
<'a
, 'gcx
, 'tcx
> {
20 pub fn ast_block(&mut self,
21 destination
: &Place
<'tcx
>,
23 ast_block
: &'tcx hir
::Block
,
24 source_info
: SourceInfo
)
28 opt_destruction_scope
,
35 self.hir
.mirror(ast_block
);
36 self.in_opt_scope(opt_destruction_scope
.map(|de
|(de
, source_info
)), block
, move |this
| {
37 this
.in_scope((region_scope
, source_info
), LintLevel
::Inherited
, block
, move |this
| {
38 if targeted_by_break
{
39 // This is a `break`-able block
40 let exit_block
= this
.cfg
.start_new_block();
41 let block_exit
= this
.in_breakable_scope(
42 None
, exit_block
, destination
.clone(), |this
| {
43 this
.ast_block_stmts(destination
, block
, span
, stmts
, expr
,
46 this
.cfg
.terminate(unpack
!(block_exit
), source_info
,
47 TerminatorKind
::Goto { target: exit_block }
);
50 this
.ast_block_stmts(destination
, block
, span
, stmts
, expr
,
57 fn ast_block_stmts(&mut self,
58 destination
: &Place
<'tcx
>,
59 mut block
: BasicBlock
,
61 stmts
: Vec
<StmtRef
<'tcx
>>,
62 expr
: Option
<ExprRef
<'tcx
>>,
63 safety_mode
: BlockSafety
)
67 // This convoluted structure is to avoid using recursion as we walk down a list
68 // of statements. Basically, the structure we get back is something like:
70 // let x = <init> in {
72 // let y = <init> in {
79 // The let bindings are valid till the end of block so all we have to do is to pop all
80 // the let-scopes at the end.
82 // First we build all the statements in the block.
83 let mut let_scope_stack
= Vec
::with_capacity(8);
84 let outer_source_scope
= this
.source_scope
;
85 let outer_push_unsafe_count
= this
.push_unsafe_count
;
86 let outer_unpushed_unsafe
= this
.unpushed_unsafe
;
87 this
.update_source_scope_for_safety_mode(span
, safety_mode
);
89 let source_info
= this
.source_info(span
);
91 let Stmt { kind, opt_destruction_scope }
= this
.hir
.mirror(stmt
);
93 StmtKind
::Expr { scope, expr }
=> {
94 unpack
!(block
= this
.in_opt_scope(
95 opt_destruction_scope
.map(|de
|(de
, source_info
)), block
, |this
| {
96 let si
= (scope
, source_info
);
97 this
.in_scope(si
, LintLevel
::Inherited
, block
, |this
| {
98 let expr
= this
.hir
.mirror(expr
);
99 this
.stmt_expr(block
, expr
)
111 // Enter the remainder scope, i.e. the bindings' destruction scope.
112 this
.push_scope((remainder_scope
, source_info
));
113 let_scope_stack
.push(remainder_scope
);
115 // Declare the bindings, which may create a source scope.
116 let remainder_span
= remainder_scope
.span(this
.hir
.tcx(),
117 &this
.hir
.region_scope_tree
);
118 let scope
= this
.declare_bindings(None
, remainder_span
, lint_level
, &pattern
,
121 // Evaluate the initializer, if present.
122 if let Some(init
) = initializer
{
123 unpack
!(block
= this
.in_opt_scope(
124 opt_destruction_scope
.map(|de
|(de
, source_info
)), block
, |this
| {
125 let scope
= (init_scope
, source_info
);
126 this
.in_scope(scope
, lint_level
, block
, |this
| {
127 this
.expr_into_pattern(block
, ty
, pattern
, init
)
131 // FIXME(#47184): We currently only insert `UserAssertTy` statements for
132 // patterns that are bindings, this is as we do not want to deconstruct
133 // the type being assertion to match the pattern.
134 if let PatternKind
::Binding { var, .. }
= *pattern
.kind
{
135 if let Some(ty
) = ty
{
136 this
.user_assert_ty(block
, ty
, var
, span
);
140 this
.visit_bindings(&pattern
, &mut |this
, _
, _
, _
, node
, span
, _
| {
141 this
.storage_live_binding(block
, node
, span
, OutsideGuard
);
142 this
.schedule_drop_for_binding(node
, span
, OutsideGuard
);
146 // Enter the source scope, after evaluating the initializer.
147 if let Some(source_scope
) = scope
{
148 this
.source_scope
= source_scope
;
153 // Then, the block may have an optional trailing expression which is a “return” value
155 if let Some(expr
) = expr
{
156 unpack
!(block
= this
.into(destination
, block
, expr
));
158 // If a block has no trailing expression, then it is given an implicit return type.
159 // This return type is usually `()`, unless the block is diverging, in which case the
160 // return type is `!`. For the unit type, we need to actually return the unit, but in
161 // the case of `!`, no return value is required, as the block will never return.
162 let tcx
= this
.hir
.tcx();
163 let ty
= destination
.ty(&this
.local_decls
, tcx
).to_ty(tcx
);
165 // We only want to assign an implicit `()` as the return value of the block if the
166 // block does not diverge. (Otherwise, we may try to assign a unit to a `!`-type.)
167 this
.cfg
.push_assign_unit(block
, source_info
, destination
);
170 // Finally, we pop all the let scopes before exiting out from the scope of block
172 for scope
in let_scope_stack
.into_iter().rev() {
173 unpack
!(block
= this
.pop_scope((scope
, source_info
), block
));
175 // Restore the original source scope.
176 this
.source_scope
= outer_source_scope
;
177 this
.push_unsafe_count
= outer_push_unsafe_count
;
178 this
.unpushed_unsafe
= outer_unpushed_unsafe
;
182 /// If we are changing the safety mode, create a new source scope
183 fn update_source_scope_for_safety_mode(&mut self,
185 safety_mode
: BlockSafety
)
187 debug
!("update_source_scope_for({:?}, {:?})", span
, safety_mode
);
188 let new_unsafety
= match safety_mode
{
189 BlockSafety
::Safe
=> None
,
190 BlockSafety
::ExplicitUnsafe(node_id
) => {
191 assert_eq
!(self.push_unsafe_count
, 0);
192 match self.unpushed_unsafe
{
196 self.unpushed_unsafe
= Safety
::ExplicitUnsafe(node_id
);
197 Some(Safety
::ExplicitUnsafe(node_id
))
199 BlockSafety
::PushUnsafe
=> {
200 self.push_unsafe_count
+= 1;
201 Some(Safety
::BuiltinUnsafe
)
203 BlockSafety
::PopUnsafe
=> {
204 self.push_unsafe_count
=
205 self.push_unsafe_count
.checked_sub(1).unwrap_or_else(|| {
206 span_bug
!(span
, "unsafe count underflow")
208 if self.push_unsafe_count
== 0 {
209 Some(self.unpushed_unsafe
)
216 if let Some(unsafety
) = new_unsafety
{
217 self.source_scope
= self.new_source_scope(
218 span
, LintLevel
::Inherited
, Some(unsafety
));