1 use crate::build
::matches
::ArmHasGuard
;
2 use crate::build
::ForGuard
::OutsideGuard
;
3 use crate::build
::{BlockAnd, BlockAndExtension, BlockFrame, Builder}
;
6 use rustc_middle
::mir
::*;
7 use rustc_session
::lint
::builtin
::UNSAFE_OP_IN_UNSAFE_FN
;
8 use rustc_session
::lint
::Level
;
11 impl<'a
, 'tcx
> Builder
<'a
, 'tcx
> {
14 destination
: Place
<'tcx
>,
16 ast_block
: &'tcx hir
::Block
<'tcx
>,
17 source_info
: SourceInfo
,
21 opt_destruction_scope
,
27 } = self.hir
.mirror(ast_block
);
28 self.in_opt_scope(opt_destruction_scope
.map(|de
| (de
, source_info
)), move |this
| {
29 this
.in_scope((region_scope
, source_info
), LintLevel
::Inherited
, move |this
| {
30 if targeted_by_break
{
31 // This is a `break`-able block
32 let exit_block
= this
.cfg
.start_new_block();
34 this
.in_breakable_scope(None
, exit_block
, destination
, |this
| {
35 this
.ast_block_stmts(destination
, block
, span
, stmts
, expr
, safety_mode
)
37 this
.cfg
.goto(unpack
!(block_exit
), source_info
, exit_block
);
40 this
.ast_block_stmts(destination
, block
, span
, stmts
, expr
, safety_mode
)
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 }
= this
.hir
.mirror(stmt
);
83 StmtKind
::Expr { scope, expr }
=> {
84 this
.block_context
.push(BlockFrame
::Statement { ignores_expr_result: true }
);
86 block
= this
.in_opt_scope(
87 opt_destruction_scope
.map(|de
| (de
, source_info
)),
89 let si
= (scope
, source_info
);
90 this
.in_scope(si
, LintLevel
::Inherited
, |this
| {
91 let expr
= this
.hir
.mirror(expr
);
92 this
.stmt_expr(block
, expr
, Some(scope
))
98 StmtKind
::Let { remainder_scope, init_scope, pattern, initializer, lint_level }
=> {
99 let ignores_expr_result
=
100 if let PatKind
::Wild
= *pattern
.kind { true }
else { false }
;
101 this
.block_context
.push(BlockFrame
::Statement { ignores_expr_result }
);
103 // Enter the remainder scope, i.e., the bindings' destruction scope.
104 this
.push_scope((remainder_scope
, source_info
));
105 let_scope_stack
.push(remainder_scope
);
107 // Declare the bindings, which may create a source scope.
109 remainder_scope
.span(this
.hir
.tcx(), &this
.hir
.region_scope_tree
);
111 let visibility_scope
=
112 Some(this
.new_source_scope(remainder_span
, LintLevel
::Inherited
, None
));
114 // Evaluate the initializer, if present.
115 if let Some(init
) = initializer
{
116 let initializer_span
= init
.span();
119 block
= this
.in_opt_scope(
120 opt_destruction_scope
.map(|de
| (de
, source_info
)),
122 let scope
= (init_scope
, source_info
);
123 this
.in_scope(scope
, lint_level
, |this
| {
124 this
.declare_bindings(
129 Some((None
, initializer_span
)),
131 this
.expr_into_pattern(block
, pattern
, init
)
137 let scope
= (init_scope
, source_info
);
138 unpack
!(this
.in_scope(scope
, lint_level
, |this
| {
139 this
.declare_bindings(
149 debug
!("ast_block_stmts: pattern={:?}", pattern
);
150 this
.visit_primary_bindings(
152 UserTypeProjections
::none(),
153 &mut |this
, _
, _
, _
, node
, span
, _
, _
| {
154 this
.storage_live_binding(block
, node
, span
, OutsideGuard
, true);
155 this
.schedule_drop_for_binding(node
, span
, OutsideGuard
);
160 // Enter the visibility scope, after evaluating the initializer.
161 if let Some(source_scope
) = visibility_scope
{
162 this
.source_scope
= source_scope
;
167 let popped
= this
.block_context
.pop();
168 assert
!(popped
.map_or(false, |bf
| bf
.is_statement()));
171 // Then, the block may have an optional trailing expression which is a “return” value
172 // of the block, which is stored into `destination`.
173 let tcx
= this
.hir
.tcx();
174 let destination_ty
= destination
.ty(&this
.local_decls
, tcx
).ty
;
175 if let Some(expr
) = expr
{
176 let tail_result_is_ignored
=
177 destination_ty
.is_unit() || this
.block_context
.currently_ignores_tail_results();
178 let span
= match expr
{
179 ExprRef
::Thir(expr
) => expr
.span
,
180 ExprRef
::Mirror(ref expr
) => expr
.span
,
182 this
.block_context
.push(BlockFrame
::TailExpr { tail_result_is_ignored, span }
);
184 unpack
!(block
= this
.into(destination
, block
, expr
));
185 let popped
= this
.block_context
.pop();
187 assert
!(popped
.map_or(false, |bf
| bf
.is_tail_expr()));
189 // If a block has no trailing expression, then it is given an implicit return type.
190 // This return type is usually `()`, unless the block is diverging, in which case the
191 // return type is `!`. For the unit type, we need to actually return the unit, but in
192 // the case of `!`, no return value is required, as the block will never return.
193 if destination_ty
.is_unit() {
194 // We only want to assign an implicit `()` as the return value of the block if the
195 // block does not diverge. (Otherwise, we may try to assign a unit to a `!`-type.)
196 this
.cfg
.push_assign_unit(block
, source_info
, destination
, this
.hir
.tcx());
199 // Finally, we pop all the let scopes before exiting out from the scope of block
201 for scope
in let_scope_stack
.into_iter().rev() {
202 unpack
!(block
= this
.pop_scope((scope
, source_info
), block
));
204 // Restore the original source scope.
205 this
.source_scope
= outer_source_scope
;
206 this
.push_unsafe_count
= outer_push_unsafe_count
;
207 this
.unpushed_unsafe
= outer_unpushed_unsafe
;
211 /// If we are changing the safety mode, create a new source scope
212 fn update_source_scope_for_safety_mode(&mut self, span
: Span
, safety_mode
: BlockSafety
) {
213 debug
!("update_source_scope_for({:?}, {:?})", span
, safety_mode
);
214 let new_unsafety
= match safety_mode
{
215 BlockSafety
::Safe
=> None
,
216 BlockSafety
::ExplicitUnsafe(hir_id
) => {
217 assert_eq
!(self.push_unsafe_count
, 0);
218 match self.unpushed_unsafe
{
220 // no longer treat `unsafe fn`s as `unsafe` contexts (see RFC #2585)
222 if self.hir
.tcx().lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN
, hir_id
).0
223 != Level
::Allow
=> {}
226 self.unpushed_unsafe
= Safety
::ExplicitUnsafe(hir_id
);
227 Some(Safety
::ExplicitUnsafe(hir_id
))
229 BlockSafety
::PushUnsafe
=> {
230 self.push_unsafe_count
+= 1;
231 Some(Safety
::BuiltinUnsafe
)
233 BlockSafety
::PopUnsafe
=> {
234 self.push_unsafe_count
= self
237 .unwrap_or_else(|| span_bug
!(span
, "unsafe count underflow"));
238 if self.push_unsafe_count
== 0 { Some(self.unpushed_unsafe) }
else { None }
242 if let Some(unsafety
) = new_unsafety
{
243 self.source_scope
= self.new_source_scope(span
, LintLevel
::Inherited
, Some(unsafety
));