]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir_build/src/build/block.rs
Remove useless references/dereferences
[rust.git] / compiler / rustc_mir_build / src / 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_middle::mir::*;
6 use rustc_session::lint::builtin::UNSAFE_OP_IN_UNSAFE_FN;
7 use rustc_session::lint::Level;
8 use rustc_span::Span;
9
10 impl<'a, 'tcx> Builder<'a, 'tcx> {
11     crate fn ast_block(
12         &mut self,
13         destination: Place<'tcx>,
14         block: BasicBlock,
15         ast_block: &Block<'_, 'tcx>,
16         source_info: SourceInfo,
17     ) -> BlockAnd<()> {
18         let Block {
19             region_scope,
20             opt_destruction_scope,
21             span,
22             stmts,
23             expr,
24             targeted_by_break,
25             safety_mode,
26         } = *ast_block;
27         self.in_opt_scope(opt_destruction_scope.map(|de| (de, source_info)), move |this| {
28             this.in_scope((region_scope, source_info), LintLevel::Inherited, move |this| {
29                 if targeted_by_break {
30                     this.in_breakable_scope(None, destination, span, |this| {
31                         Some(this.ast_block_stmts(
32                             destination,
33                             block,
34                             span,
35                             stmts,
36                             expr,
37                             safety_mode,
38                         ))
39                     })
40                 } else {
41                     this.ast_block_stmts(destination, block, span, stmts, expr, safety_mode)
42                 }
43             })
44         })
45     }
46
47     fn ast_block_stmts(
48         &mut self,
49         destination: Place<'tcx>,
50         mut block: BasicBlock,
51         span: Span,
52         stmts: &[Stmt<'_, 'tcx>],
53         expr: Option<&Expr<'_, 'tcx>>,
54         safety_mode: BlockSafety,
55     ) -> BlockAnd<()> {
56         let this = self;
57
58         // This convoluted structure is to avoid using recursion as we walk down a list
59         // of statements. Basically, the structure we get back is something like:
60         //
61         //    let x = <init> in {
62         //       expr1;
63         //       let y = <init> in {
64         //           expr2;
65         //           expr3;
66         //           ...
67         //       }
68         //    }
69         //
70         // The let bindings are valid till the end of block so all we have to do is to pop all
71         // the let-scopes at the end.
72         //
73         // First we build all the statements in the block.
74         let mut let_scope_stack = Vec::with_capacity(8);
75         let outer_source_scope = this.source_scope;
76         let outer_push_unsafe_count = this.push_unsafe_count;
77         let outer_unpushed_unsafe = this.unpushed_unsafe;
78         this.update_source_scope_for_safety_mode(span, safety_mode);
79
80         let source_info = this.source_info(span);
81         for Stmt { kind, opt_destruction_scope } in stmts {
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                                     this.stmt_expr(block, expr, Some(scope))
92                                 })
93                             }
94                         )
95                     );
96                 }
97                 StmtKind::Let { remainder_scope, init_scope, pattern, initializer, lint_level } => {
98                     let ignores_expr_result = matches!(*pattern.kind, PatKind::Wild);
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 = remainder_scope.span(this.tcx, this.region_scope_tree);
107
108                     let visibility_scope =
109                         Some(this.new_source_scope(remainder_span, LintLevel::Inherited, None));
110
111                     // Evaluate the initializer, if present.
112                     if let Some(init) = initializer {
113                         let initializer_span = init.span;
114
115                         unpack!(
116                             block = this.in_opt_scope(
117                                 opt_destruction_scope.map(|de| (de, source_info)),
118                                 |this| {
119                                     let scope = (*init_scope, source_info);
120                                     this.in_scope(scope, *lint_level, |this| {
121                                         this.declare_bindings(
122                                             visibility_scope,
123                                             remainder_span,
124                                             pattern,
125                                             ArmHasGuard(false),
126                                             Some((None, initializer_span)),
127                                         );
128                                         this.expr_into_pattern(block, pattern.clone(), init)
129                                     })
130                                 }
131                             )
132                         );
133                     } else {
134                         let scope = (*init_scope, source_info);
135                         unpack!(this.in_scope(scope, *lint_level, |this| {
136                             this.declare_bindings(
137                                 visibility_scope,
138                                 remainder_span,
139                                 pattern,
140                                 ArmHasGuard(false),
141                                 None,
142                             );
143                             block.unit()
144                         }));
145
146                         debug!("ast_block_stmts: pattern={:?}", pattern);
147                         this.visit_primary_bindings(
148                             &pattern,
149                             UserTypeProjections::none(),
150                             &mut |this, _, _, _, node, span, _, _| {
151                                 this.storage_live_binding(block, node, span, OutsideGuard, true);
152                                 this.schedule_drop_for_binding(node, span, OutsideGuard);
153                             },
154                         )
155                     }
156
157                     // Enter the visibility scope, after evaluating the initializer.
158                     if let Some(source_scope) = visibility_scope {
159                         this.source_scope = source_scope;
160                     }
161                 }
162             }
163
164             let popped = this.block_context.pop();
165             assert!(popped.map_or(false, |bf| bf.is_statement()));
166         }
167
168         // Then, the block may have an optional trailing expression which is a “return” value
169         // of the block, which is stored into `destination`.
170         let tcx = this.tcx;
171         let destination_ty = destination.ty(&this.local_decls, tcx).ty;
172         if let Some(expr) = expr {
173             let tail_result_is_ignored =
174                 destination_ty.is_unit() || this.block_context.currently_ignores_tail_results();
175             this.block_context
176                 .push(BlockFrame::TailExpr { tail_result_is_ignored, span: expr.span });
177
178             unpack!(block = this.expr_into_dest(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, this.tcx);
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                     // no longer treat `unsafe fn`s as `unsafe` contexts (see RFC #2585)
215                     Safety::FnUnsafe
216                         if self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, hir_id).0
217                             != Level::Allow => {}
218                     _ => return,
219                 }
220                 self.unpushed_unsafe = Safety::ExplicitUnsafe(hir_id);
221                 Some(Safety::ExplicitUnsafe(hir_id))
222             }
223             BlockSafety::PushUnsafe => {
224                 self.push_unsafe_count += 1;
225                 Some(Safety::BuiltinUnsafe)
226             }
227             BlockSafety::PopUnsafe => {
228                 self.push_unsafe_count = self
229                     .push_unsafe_count
230                     .checked_sub(1)
231                     .unwrap_or_else(|| span_bug!(span, "unsafe count underflow"));
232                 if self.push_unsafe_count == 0 { Some(self.unpushed_unsafe) } else { None }
233             }
234         };
235
236         if let Some(unsafety) = new_unsafety {
237             self.source_scope = self.new_source_scope(span, LintLevel::Inherited, Some(unsafety));
238         }
239     }
240 }