]> git.proxmox.com Git - rustc.git/blame - src/librustc_mir_build/build/block.rs
New upstream version 1.47.0+dfsg1
[rustc.git] / src / librustc_mir_build / build / block.rs
CommitLineData
9fa01778 1use crate::build::matches::ArmHasGuard;
dfeec247
XL
2use crate::build::ForGuard::OutsideGuard;
3use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
3dfed10e 4use crate::thir::*;
dfeec247 5use rustc_hir as hir;
ba9703b0 6use rustc_middle::mir::*;
f9f354fc
XL
7use rustc_session::lint::builtin::UNSAFE_OP_IN_UNSAFE_FN;
8use rustc_session::lint::Level;
dfeec247 9use rustc_span::Span;
e9174d1e 10
dc9dc135 11impl<'a, 'tcx> Builder<'a, 'tcx> {
dfeec247
XL
12 crate fn ast_block(
13 &mut self,
ba9703b0 14 destination: Place<'tcx>,
dfeec247
XL
15 block: BasicBlock,
16 ast_block: &'tcx hir::Block<'tcx>,
17 source_info: SourceInfo,
18 ) -> BlockAnd<()> {
ea8adc8c
XL
19 let Block {
20 region_scope,
21 opt_destruction_scope,
22 span,
23 stmts,
24 expr,
25 targeted_by_break,
dfeec247
XL
26 safety_mode,
27 } = self.hir.mirror(ast_block);
28 self.in_opt_scope(opt_destruction_scope.map(|de| (de, source_info)), move |this| {
dc9dc135 29 this.in_scope((region_scope, source_info), LintLevel::Inherited, move |this| {
041b39d2 30 if targeted_by_break {
94b46f34 31 // This is a `break`-able block
041b39d2 32 let exit_block = this.cfg.start_new_block();
dfeec247 33 let block_exit =
ba9703b0 34 this.in_breakable_scope(None, exit_block, destination, |this| {
dfeec247 35 this.ast_block_stmts(destination, block, span, stmts, expr, safety_mode)
041b39d2 36 });
dfeec247 37 this.cfg.goto(unpack!(block_exit), source_info, exit_block);
041b39d2
XL
38 exit_block.unit()
39 } else {
dfeec247 40 this.ast_block_stmts(destination, block, span, stmts, expr, safety_mode)
041b39d2
XL
41 }
42 })
cc61c64b
XL
43 })
44 }
3157f602 45
dfeec247
XL
46 fn ast_block_stmts(
47 &mut self,
ba9703b0 48 destination: Place<'tcx>,
dfeec247
XL
49 mut block: BasicBlock,
50 span: Span,
51 stmts: Vec<StmtRef<'tcx>>,
52 expr: Option<ExprRef<'tcx>>,
53 safety_mode: BlockSafety,
54 ) -> BlockAnd<()> {
cc61c64b
XL
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.
ea8adc8c 73 let mut let_scope_stack = Vec::with_capacity(8);
94b46f34 74 let outer_source_scope = this.source_scope;
ea8adc8c
XL
75 let outer_push_unsafe_count = this.push_unsafe_count;
76 let outer_unpushed_unsafe = this.unpushed_unsafe;
94b46f34 77 this.update_source_scope_for_safety_mode(span, safety_mode);
ea8adc8c 78
041b39d2 79 let source_info = this.source_info(span);
cc61c64b 80 for stmt in stmts {
dc9dc135 81 let Stmt { kind, opt_destruction_scope } = this.hir.mirror(stmt);
cc61c64b
XL
82 match kind {
83 StmtKind::Expr { scope, expr } => {
0bf4aa26 84 this.block_context.push(BlockFrame::Statement { ignores_expr_result: true });
dfeec247
XL
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 );
cc61c64b 97 }
dfeec247
XL
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 };
0bf4aa26
XL
101 this.block_context.push(BlockFrame::Statement { ignores_expr_result });
102
0731742a 103 // Enter the remainder scope, i.e., the bindings' destruction scope.
3b2f2976 104 this.push_scope((remainder_scope, source_info));
ea8adc8c 105 let_scope_stack.push(remainder_scope);
3157f602 106
94b46f34 107 // Declare the bindings, which may create a source scope.
dfeec247
XL
108 let remainder_span =
109 remainder_scope.span(this.hir.tcx(), &this.hir.region_scope_tree);
8faf50e0 110
dc9dc135
XL
111 let visibility_scope =
112 Some(this.new_source_scope(remainder_span, LintLevel::Inherited, None));
3157f602 113
cc61c64b
XL
114 // Evaluate the initializer, if present.
115 if let Some(init) = initializer {
8faf50e0
XL
116 let initializer_span = init.span();
117
dfeec247
XL
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 );
cc61c64b 136 } else {
dc9dc135
XL
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 }));
8faf50e0 148
0731742a 149 debug!("ast_block_stmts: pattern={:?}", pattern);
f9f354fc 150 this.visit_primary_bindings(
0bf4aa26 151 &pattern,
0731742a 152 UserTypeProjections::none(),
0bf4aa26 153 &mut |this, _, _, _, node, span, _, _| {
74b04a01 154 this.storage_live_binding(block, node, span, OutsideGuard, true);
0bf4aa26 155 this.schedule_drop_for_binding(node, span, OutsideGuard);
dfeec247
XL
156 },
157 )
cc61c64b
XL
158 }
159
dc9dc135
XL
160 // Enter the visibility scope, after evaluating the initializer.
161 if let Some(source_scope) = visibility_scope {
94b46f34 162 this.source_scope = source_scope;
54a0048b 163 }
9cc50fc6
SL
164 }
165 }
0bf4aa26
XL
166
167 let popped = this.block_context.pop();
dfeec247 168 assert!(popped.map_or(false, |bf| bf.is_statement()));
cc61c64b 169 }
0bf4aa26 170
cc61c64b 171 // Then, the block may have an optional trailing expression which is a “return” value
0bf4aa26
XL
172 // of the block, which is stored into `destination`.
173 let tcx = this.hir.tcx();
532ac7d7 174 let destination_ty = destination.ty(&this.local_decls, tcx).ty;
cc61c64b 175 if let Some(expr) = expr {
dfeec247
XL
176 let tail_result_is_ignored =
177 destination_ty.is_unit() || this.block_context.currently_ignores_tail_results();
f9f354fc 178 let span = match expr {
3dfed10e 179 ExprRef::Thir(expr) => expr.span,
f9f354fc
XL
180 ExprRef::Mirror(ref expr) => expr.span,
181 };
182 this.block_context.push(BlockFrame::TailExpr { tail_result_is_ignored, span });
0bf4aa26 183
cc61c64b 184 unpack!(block = this.into(destination, block, expr));
0bf4aa26
XL
185 let popped = this.block_context.pop();
186
dfeec247 187 assert!(popped.map_or(false, |bf| bf.is_tail_expr()));
cc61c64b 188 } else {
2c00a5a8
XL
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.
0bf4aa26 193 if destination_ty.is_unit() {
2c00a5a8
XL
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.)
ba9703b0 196 this.cfg.push_assign_unit(block, source_info, destination, this.hir.tcx());
2c00a5a8 197 }
cc61c64b
XL
198 }
199 // Finally, we pop all the let scopes before exiting out from the scope of block
200 // itself.
ea8adc8c
XL
201 for scope in let_scope_stack.into_iter().rev() {
202 unpack!(block = this.pop_scope((scope, source_info), block));
cc61c64b 203 }
94b46f34
XL
204 // Restore the original source scope.
205 this.source_scope = outer_source_scope;
ea8adc8c
XL
206 this.push_unsafe_count = outer_push_unsafe_count;
207 this.unpushed_unsafe = outer_unpushed_unsafe;
cc61c64b 208 block.unit()
e9174d1e 209 }
ea8adc8c 210
94b46f34 211 /// If we are changing the safety mode, create a new source scope
dfeec247 212 fn update_source_scope_for_safety_mode(&mut self, span: Span, safety_mode: BlockSafety) {
94b46f34 213 debug!("update_source_scope_for({:?}, {:?})", span, safety_mode);
ea8adc8c
XL
214 let new_unsafety = match safety_mode {
215 BlockSafety::Safe => None,
532ac7d7 216 BlockSafety::ExplicitUnsafe(hir_id) => {
ea8adc8c
XL
217 assert_eq!(self.push_unsafe_count, 0);
218 match self.unpushed_unsafe {
219 Safety::Safe => {}
f9f354fc
XL
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 => {}
dfeec247 224 _ => return,
ea8adc8c 225 }
532ac7d7
XL
226 self.unpushed_unsafe = Safety::ExplicitUnsafe(hir_id);
227 Some(Safety::ExplicitUnsafe(hir_id))
ea8adc8c
XL
228 }
229 BlockSafety::PushUnsafe => {
230 self.push_unsafe_count += 1;
231 Some(Safety::BuiltinUnsafe)
232 }
233 BlockSafety::PopUnsafe => {
dfeec247
XL
234 self.push_unsafe_count = self
235 .push_unsafe_count
236 .checked_sub(1)
237 .unwrap_or_else(|| span_bug!(span, "unsafe count underflow"));
3dfed10e 238 if self.push_unsafe_count == 0 { Some(self.unpushed_unsafe) } else { None }
ea8adc8c
XL
239 }
240 };
241
242 if let Some(unsafety) = new_unsafety {
dfeec247 243 self.source_scope = self.new_source_scope(span, LintLevel::Inherited, Some(unsafety));
ea8adc8c
XL
244 }
245 }
e9174d1e 246}