]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir_build/build/block.rs
Auto merge of #68030 - cuviper:llvm-9.0.1, r=nikic
[rust.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::hair::*;
5 use rustc::mir::*;
6 use rustc_hir as hir;
7 use rustc_span::Span;
8
9 impl<'a, 'tcx> Builder<'a, 'tcx> {
10     crate fn ast_block(
11         &mut self,
12         destination: &Place<'tcx>,
13         block: BasicBlock,
14         ast_block: &'tcx hir::Block<'tcx>,
15         source_info: SourceInfo,
16     ) -> BlockAnd<()> {
17         let Block {
18             region_scope,
19             opt_destruction_scope,
20             span,
21             stmts,
22             expr,
23             targeted_by_break,
24             safety_mode,
25         } = self.hir.mirror(ast_block);
26         self.in_opt_scope(opt_destruction_scope.map(|de| (de, source_info)), move |this| {
27             this.in_scope((region_scope, source_info), LintLevel::Inherited, 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 =
32                         this.in_breakable_scope(None, exit_block, destination.clone(), |this| {
33                             this.ast_block_stmts(destination, block, span, stmts, expr, safety_mode)
34                         });
35                     this.cfg.goto(unpack!(block_exit), source_info, exit_block);
36                     exit_block.unit()
37                 } else {
38                     this.ast_block_stmts(destination, block, span, stmts, expr, safety_mode)
39                 }
40             })
41         })
42     }
43
44     fn ast_block_stmts(
45         &mut self,
46         destination: &Place<'tcx>,
47         mut block: BasicBlock,
48         span: Span,
49         stmts: Vec<StmtRef<'tcx>>,
50         expr: Option<ExprRef<'tcx>>,
51         safety_mode: BlockSafety,
52     ) -> BlockAnd<()> {
53         let this = self;
54
55         // This convoluted structure is to avoid using recursion as we walk down a list
56         // of statements. Basically, the structure we get back is something like:
57         //
58         //    let x = <init> in {
59         //       expr1;
60         //       let y = <init> in {
61         //           expr2;
62         //           expr3;
63         //           ...
64         //       }
65         //    }
66         //
67         // The let bindings are valid till the end of block so all we have to do is to pop all
68         // the let-scopes at the end.
69         //
70         // First we build all the statements in the block.
71         let mut let_scope_stack = Vec::with_capacity(8);
72         let outer_source_scope = this.source_scope;
73         let outer_push_unsafe_count = this.push_unsafe_count;
74         let outer_unpushed_unsafe = this.unpushed_unsafe;
75         this.update_source_scope_for_safety_mode(span, safety_mode);
76
77         let source_info = this.source_info(span);
78         for stmt in stmts {
79             let Stmt { kind, opt_destruction_scope } = this.hir.mirror(stmt);
80             match kind {
81                 StmtKind::Expr { scope, expr } => {
82                     this.block_context.push(BlockFrame::Statement { ignores_expr_result: true });
83                     unpack!(
84                         block = this.in_opt_scope(
85                             opt_destruction_scope.map(|de| (de, source_info)),
86                             |this| {
87                                 let si = (scope, source_info);
88                                 this.in_scope(si, LintLevel::Inherited, |this| {
89                                     let expr = this.hir.mirror(expr);
90                                     this.stmt_expr(block, expr, Some(scope))
91                                 })
92                             }
93                         )
94                     );
95                 }
96                 StmtKind::Let { remainder_scope, init_scope, pattern, initializer, lint_level } => {
97                     let ignores_expr_result =
98                         if let PatKind::Wild = *pattern.kind { true } else { false };
99                     this.block_context.push(BlockFrame::Statement { ignores_expr_result });
100
101                     // Enter the remainder scope, i.e., the bindings' destruction scope.
102                     this.push_scope((remainder_scope, source_info));
103                     let_scope_stack.push(remainder_scope);
104
105                     // Declare the bindings, which may create a source scope.
106                     let remainder_span =
107                         remainder_scope.span(this.hir.tcx(), &this.hir.region_scope_tree);
108
109                     let visibility_scope =
110                         Some(this.new_source_scope(remainder_span, LintLevel::Inherited, None));
111
112                     // Evaluate the initializer, if present.
113                     if let Some(init) = initializer {
114                         let initializer_span = init.span();
115
116                         unpack!(
117                             block = this.in_opt_scope(
118                                 opt_destruction_scope.map(|de| (de, source_info)),
119                                 |this| {
120                                     let scope = (init_scope, source_info);
121                                     this.in_scope(scope, lint_level, |this| {
122                                         this.declare_bindings(
123                                             visibility_scope,
124                                             remainder_span,
125                                             &pattern,
126                                             ArmHasGuard(false),
127                                             Some((None, initializer_span)),
128                                         );
129                                         this.expr_into_pattern(block, pattern, init)
130                                     })
131                                 }
132                             )
133                         );
134                     } else {
135                         let scope = (init_scope, source_info);
136                         unpack!(this.in_scope(scope, lint_level, |this| {
137                             this.declare_bindings(
138                                 visibility_scope,
139                                 remainder_span,
140                                 &pattern,
141                                 ArmHasGuard(false),
142                                 None,
143                             );
144                             block.unit()
145                         }));
146
147                         debug!("ast_block_stmts: pattern={:?}", pattern);
148                         this.visit_bindings(
149                             &pattern,
150                             UserTypeProjections::none(),
151                             &mut |this, _, _, _, node, span, _, _| {
152                                 this.storage_live_binding(block, node, span, OutsideGuard);
153                                 this.schedule_drop_for_binding(node, span, OutsideGuard);
154                             },
155                         )
156                     }
157
158                     // Enter the visibility scope, after evaluating the initializer.
159                     if let Some(source_scope) = visibility_scope {
160                         this.source_scope = source_scope;
161                     }
162                 }
163             }
164
165             let popped = this.block_context.pop();
166             assert!(popped.map_or(false, |bf| bf.is_statement()));
167         }
168
169         // Then, the block may have an optional trailing expression which is a “return” value
170         // of the block, which is stored into `destination`.
171         let tcx = this.hir.tcx();
172         let destination_ty = destination.ty(&this.local_decls, tcx).ty;
173         if let Some(expr) = expr {
174             let tail_result_is_ignored =
175                 destination_ty.is_unit() || this.block_context.currently_ignores_tail_results();
176             this.block_context.push(BlockFrame::TailExpr { tail_result_is_ignored });
177
178             unpack!(block = this.into(destination, block, expr));
179             let popped = this.block_context.pop();
180
181             assert!(popped.map_or(false, |bf| bf.is_tail_expr()));
182         } else {
183             // If a block has no trailing expression, then it is given an implicit return type.
184             // This return type is usually `()`, unless the block is diverging, in which case the
185             // return type is `!`. For the unit type, we need to actually return the unit, but in
186             // the case of `!`, no return value is required, as the block will never return.
187             if destination_ty.is_unit() {
188                 // We only want to assign an implicit `()` as the return value of the block if the
189                 // block does not diverge. (Otherwise, we may try to assign a unit to a `!`-type.)
190                 this.cfg.push_assign_unit(block, source_info, destination);
191             }
192         }
193         // Finally, we pop all the let scopes before exiting out from the scope of block
194         // itself.
195         for scope in let_scope_stack.into_iter().rev() {
196             unpack!(block = this.pop_scope((scope, source_info), block));
197         }
198         // Restore the original source scope.
199         this.source_scope = outer_source_scope;
200         this.push_unsafe_count = outer_push_unsafe_count;
201         this.unpushed_unsafe = outer_unpushed_unsafe;
202         block.unit()
203     }
204
205     /// If we are changing the safety mode, create a new source scope
206     fn update_source_scope_for_safety_mode(&mut self, span: Span, safety_mode: BlockSafety) {
207         debug!("update_source_scope_for({:?}, {:?})", span, safety_mode);
208         let new_unsafety = match safety_mode {
209             BlockSafety::Safe => None,
210             BlockSafety::ExplicitUnsafe(hir_id) => {
211                 assert_eq!(self.push_unsafe_count, 0);
212                 match self.unpushed_unsafe {
213                     Safety::Safe => {}
214                     _ => return,
215                 }
216                 self.unpushed_unsafe = Safety::ExplicitUnsafe(hir_id);
217                 Some(Safety::ExplicitUnsafe(hir_id))
218             }
219             BlockSafety::PushUnsafe => {
220                 self.push_unsafe_count += 1;
221                 Some(Safety::BuiltinUnsafe)
222             }
223             BlockSafety::PopUnsafe => {
224                 self.push_unsafe_count = self
225                     .push_unsafe_count
226                     .checked_sub(1)
227                     .unwrap_or_else(|| span_bug!(span, "unsafe count underflow"));
228                 if self.push_unsafe_count == 0 { Some(self.unpushed_unsafe) } else { None }
229             }
230         };
231
232         if let Some(unsafety) = new_unsafety {
233             self.source_scope = self.new_source_scope(span, LintLevel::Inherited, Some(unsafety));
234         }
235     }
236 }