]> git.proxmox.com Git - rustc.git/blob - src/librustc_mir_build/build/block.rs
New upstream version 1.47.0+dfsg1
[rustc.git] / src / librustc_mir_build / build / block.rs
1 use crate::build::matches::ArmHasGuard;
2 use crate::build::ForGuard::OutsideGuard;
3 use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
4 use crate::thir::*;
5 use rustc_hir as hir;
6 use rustc_middle::mir::*;
7 use rustc_session::lint::builtin::UNSAFE_OP_IN_UNSAFE_FN;
8 use rustc_session::lint::Level;
9 use rustc_span::Span;
10
11 impl<'a, 'tcx> Builder<'a, 'tcx> {
12 crate fn ast_block(
13 &mut self,
14 destination: Place<'tcx>,
15 block: BasicBlock,
16 ast_block: &'tcx hir::Block<'tcx>,
17 source_info: SourceInfo,
18 ) -> BlockAnd<()> {
19 let Block {
20 region_scope,
21 opt_destruction_scope,
22 span,
23 stmts,
24 expr,
25 targeted_by_break,
26 safety_mode,
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();
33 let block_exit =
34 this.in_breakable_scope(None, exit_block, destination, |this| {
35 this.ast_block_stmts(destination, block, span, stmts, expr, safety_mode)
36 });
37 this.cfg.goto(unpack!(block_exit), source_info, exit_block);
38 exit_block.unit()
39 } else {
40 this.ast_block_stmts(destination, block, span, stmts, expr, safety_mode)
41 }
42 })
43 })
44 }
45
46 fn ast_block_stmts(
47 &mut self,
48 destination: Place<'tcx>,
49 mut block: BasicBlock,
50 span: Span,
51 stmts: Vec<StmtRef<'tcx>>,
52 expr: Option<ExprRef<'tcx>>,
53 safety_mode: BlockSafety,
54 ) -> BlockAnd<()> {
55 let this = self;
56
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:
59 //
60 // let x = <init> in {
61 // expr1;
62 // let y = <init> in {
63 // expr2;
64 // expr3;
65 // ...
66 // }
67 // }
68 //
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.
71 //
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);
78
79 let source_info = this.source_info(span);
80 for stmt in stmts {
81 let Stmt { kind, opt_destruction_scope } = this.hir.mirror(stmt);
82 match kind {
83 StmtKind::Expr { scope, expr } => {
84 this.block_context.push(BlockFrame::Statement { ignores_expr_result: true });
85 unpack!(
86 block = this.in_opt_scope(
87 opt_destruction_scope.map(|de| (de, source_info)),
88 |this| {
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))
93 })
94 }
95 )
96 );
97 }
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 });
102
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);
106
107 // Declare the bindings, which may create a source scope.
108 let remainder_span =
109 remainder_scope.span(this.hir.tcx(), &this.hir.region_scope_tree);
110
111 let visibility_scope =
112 Some(this.new_source_scope(remainder_span, LintLevel::Inherited, None));
113
114 // Evaluate the initializer, if present.
115 if let Some(init) = initializer {
116 let initializer_span = init.span();
117
118 unpack!(
119 block = this.in_opt_scope(
120 opt_destruction_scope.map(|de| (de, source_info)),
121 |this| {
122 let scope = (init_scope, source_info);
123 this.in_scope(scope, lint_level, |this| {
124 this.declare_bindings(
125 visibility_scope,
126 remainder_span,
127 &pattern,
128 ArmHasGuard(false),
129 Some((None, initializer_span)),
130 );
131 this.expr_into_pattern(block, pattern, init)
132 })
133 }
134 )
135 );
136 } else {
137 let scope = (init_scope, source_info);
138 unpack!(this.in_scope(scope, lint_level, |this| {
139 this.declare_bindings(
140 visibility_scope,
141 remainder_span,
142 &pattern,
143 ArmHasGuard(false),
144 None,
145 );
146 block.unit()
147 }));
148
149 debug!("ast_block_stmts: pattern={:?}", pattern);
150 this.visit_primary_bindings(
151 &pattern,
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);
156 },
157 )
158 }
159
160 // Enter the visibility scope, after evaluating the initializer.
161 if let Some(source_scope) = visibility_scope {
162 this.source_scope = source_scope;
163 }
164 }
165 }
166
167 let popped = this.block_context.pop();
168 assert!(popped.map_or(false, |bf| bf.is_statement()));
169 }
170
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,
181 };
182 this.block_context.push(BlockFrame::TailExpr { tail_result_is_ignored, span });
183
184 unpack!(block = this.into(destination, block, expr));
185 let popped = this.block_context.pop();
186
187 assert!(popped.map_or(false, |bf| bf.is_tail_expr()));
188 } else {
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());
197 }
198 }
199 // Finally, we pop all the let scopes before exiting out from the scope of block
200 // itself.
201 for scope in let_scope_stack.into_iter().rev() {
202 unpack!(block = this.pop_scope((scope, source_info), block));
203 }
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;
208 block.unit()
209 }
210
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 {
219 Safety::Safe => {}
220 // no longer treat `unsafe fn`s as `unsafe` contexts (see RFC #2585)
221 Safety::FnUnsafe
222 if self.hir.tcx().lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, hir_id).0
223 != Level::Allow => {}
224 _ => return,
225 }
226 self.unpushed_unsafe = Safety::ExplicitUnsafe(hir_id);
227 Some(Safety::ExplicitUnsafe(hir_id))
228 }
229 BlockSafety::PushUnsafe => {
230 self.push_unsafe_count += 1;
231 Some(Safety::BuiltinUnsafe)
232 }
233 BlockSafety::PopUnsafe => {
234 self.push_unsafe_count = self
235 .push_unsafe_count
236 .checked_sub(1)
237 .unwrap_or_else(|| span_bug!(span, "unsafe count underflow"));
238 if self.push_unsafe_count == 0 { Some(self.unpushed_unsafe) } else { None }
239 }
240 };
241
242 if let Some(unsafety) = new_unsafety {
243 self.source_scope = self.new_source_scope(span, LintLevel::Inherited, Some(unsafety));
244 }
245 }
246 }