]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/build/expr/into.rs
Convert Place's projection to a boxed slice
[rust.git] / src / librustc_mir / build / expr / into.rs
1 //! See docs in build/expr/mod.rs
2
3 use crate::build::expr::category::{Category, RvalueFunc};
4 use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
5 use crate::hair::*;
6 use rustc::mir::*;
7 use rustc::ty;
8
9 use rustc_target::spec::abi::Abi;
10
11 impl<'a, 'tcx> Builder<'a, 'tcx> {
12     /// Compile `expr`, storing the result into `destination`, which
13     /// is assumed to be uninitialized.
14     pub fn into_expr(
15         &mut self,
16         destination: &Place<'tcx>,
17         mut block: BasicBlock,
18         expr: Expr<'tcx>,
19     ) -> BlockAnd<()> {
20         debug!(
21             "into_expr(destination={:?}, block={:?}, expr={:?})",
22             destination, block, expr
23         );
24
25         // since we frequently have to reference `self` from within a
26         // closure, where `self` would be shadowed, it's easier to
27         // just use the name `this` uniformly
28         let this = self;
29         let expr_span = expr.span;
30         let source_info = this.source_info(expr_span);
31
32         let expr_is_block_or_scope = match expr.kind {
33             ExprKind::Block { .. } => true,
34             ExprKind::Scope { .. } => true,
35             _ => false,
36         };
37
38         if !expr_is_block_or_scope {
39             this.block_context.push(BlockFrame::SubExpr);
40         }
41
42         let block_and = match expr.kind {
43             ExprKind::Scope {
44                 region_scope,
45                 lint_level,
46                 value,
47             } => {
48                 let region_scope = (region_scope, source_info);
49                 this.in_scope(region_scope, lint_level, |this| {
50                     this.into(destination, block, value)
51                 })
52             }
53             ExprKind::Block { body: ast_block } => {
54                 this.ast_block(destination, block, ast_block, source_info)
55             }
56             ExprKind::Match { scrutinee, arms } => {
57                 this.match_expr(destination, expr_span, block, scrutinee, arms)
58             }
59             ExprKind::NeverToAny { source } => {
60                 let source = this.hir.mirror(source);
61                 let is_call = match source.kind {
62                     ExprKind::Call { .. } => true,
63                     _ => false,
64                 };
65
66                 unpack!(block = this.as_local_rvalue(block, source));
67
68                 // This is an optimization. If the expression was a call then we already have an
69                 // unreachable block. Don't bother to terminate it and create a new one.
70                 if is_call {
71                     block.unit()
72                 } else {
73                     this.cfg
74                         .terminate(block, source_info, TerminatorKind::Unreachable);
75                     let end_block = this.cfg.start_new_block();
76                     end_block.unit()
77                 }
78             }
79             ExprKind::LogicalOp { op, lhs, rhs } => {
80                 // And:
81                 //
82                 // [block: If(lhs)] -true-> [else_block: If(rhs)] -true-> [true_block]
83                 //        |                          | (false)
84                 //        +----------false-----------+------------------> [false_block]
85                 //
86                 // Or:
87                 //
88                 // [block: If(lhs)] -false-> [else_block: If(rhs)] -true-> [true_block]
89                 //        | (true)                   | (false)
90                 //  [true_block]               [false_block]
91
92                 let (true_block, false_block, mut else_block, join_block) = (
93                     this.cfg.start_new_block(),
94                     this.cfg.start_new_block(),
95                     this.cfg.start_new_block(),
96                     this.cfg.start_new_block(),
97                 );
98
99                 let lhs = unpack!(block = this.as_local_operand(block, lhs));
100                 let blocks = match op {
101                     LogicalOp::And => (else_block, false_block),
102                     LogicalOp::Or => (true_block, else_block),
103                 };
104                 let term = TerminatorKind::if_(this.hir.tcx(), lhs, blocks.0, blocks.1);
105                 this.cfg.terminate(block, source_info, term);
106
107                 let rhs = unpack!(else_block = this.as_local_operand(else_block, rhs));
108                 let term = TerminatorKind::if_(this.hir.tcx(), rhs, true_block, false_block);
109                 this.cfg.terminate(else_block, source_info, term);
110
111                 this.cfg.push_assign_constant(
112                     true_block,
113                     source_info,
114                     destination,
115                     Constant {
116                         span: expr_span,
117                         user_ty: None,
118                         literal: this.hir.true_literal(),
119                     },
120                 );
121
122                 this.cfg.push_assign_constant(
123                     false_block,
124                     source_info,
125                     destination,
126                     Constant {
127                         span: expr_span,
128                         user_ty: None,
129                         literal: this.hir.false_literal(),
130                     },
131                 );
132
133                 this.cfg.terminate(
134                     true_block,
135                     source_info,
136                     TerminatorKind::Goto { target: join_block },
137                 );
138                 this.cfg.terminate(
139                     false_block,
140                     source_info,
141                     TerminatorKind::Goto { target: join_block },
142                 );
143
144                 join_block.unit()
145             }
146             ExprKind::Loop { body } => {
147                 // [block]
148                 //    |
149                 //   [loop_block] -> [body_block] -/eval. body/-> [body_block_end]
150                 //    |        ^                                         |
151                 // false link  |                                         |
152                 //    |        +-----------------------------------------+
153                 //    +-> [diverge_cleanup]
154                 // The false link is required to make sure borrowck considers unwinds through the
155                 // body, even when the exact code in the body cannot unwind
156
157                 let loop_block = this.cfg.start_new_block();
158                 let exit_block = this.cfg.start_new_block();
159
160                 // start the loop
161                 this.cfg.terminate(
162                     block,
163                     source_info,
164                     TerminatorKind::Goto { target: loop_block },
165                 );
166
167                 this.in_breakable_scope(
168                     Some(loop_block),
169                     exit_block,
170                     destination.clone(),
171                     move |this| {
172                         // conduct the test, if necessary
173                         let body_block = this.cfg.start_new_block();
174                         let diverge_cleanup = this.diverge_cleanup();
175                         this.cfg.terminate(
176                             loop_block,
177                             source_info,
178                             TerminatorKind::FalseUnwind {
179                                 real_target: body_block,
180                                 unwind: Some(diverge_cleanup),
181                             },
182                         );
183
184                         // The “return” value of the loop body must always be an unit. We therefore
185                         // introduce a unit temporary as the destination for the loop body.
186                         let tmp = this.get_unit_temp();
187                         // Execute the body, branching back to the test.
188                         let body_block_end = unpack!(this.into(&tmp, body_block, body));
189                         this.cfg.terminate(
190                             body_block_end,
191                             source_info,
192                             TerminatorKind::Goto { target: loop_block },
193                         );
194                     },
195                 );
196                 exit_block.unit()
197             }
198             ExprKind::Call { ty, fun, args, from_hir_call } => {
199                 let intrinsic = match ty.sty {
200                     ty::FnDef(def_id, _) => {
201                         let f = ty.fn_sig(this.hir.tcx());
202                         if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic {
203                             Some(this.hir.tcx().item_name(def_id).as_str())
204                         } else {
205                             None
206                         }
207                     }
208                     _ => None,
209                 };
210                 let intrinsic = intrinsic.as_ref().map(|s| &s[..]);
211                 let fun = unpack!(block = this.as_local_operand(block, fun));
212                 if intrinsic == Some("move_val_init") {
213                     // `move_val_init` has "magic" semantics - the second argument is
214                     // always evaluated "directly" into the first one.
215
216                     let mut args = args.into_iter();
217                     let ptr = args.next().expect("0 arguments to `move_val_init`");
218                     let val = args.next().expect("1 argument to `move_val_init`");
219                     assert!(args.next().is_none(), ">2 arguments to `move_val_init`");
220
221                     let ptr = this.hir.mirror(ptr);
222                     let ptr_ty = ptr.ty;
223                     // Create an *internal* temp for the pointer, so that unsafety
224                     // checking won't complain about the raw pointer assignment.
225                     let ptr_temp = this.local_decls.push(LocalDecl {
226                         mutability: Mutability::Mut,
227                         ty: ptr_ty,
228                         user_ty: UserTypeProjections::none(),
229                         name: None,
230                         source_info,
231                         visibility_scope: source_info.scope,
232                         internal: true,
233                         is_user_variable: None,
234                         is_block_tail: None,
235                     });
236                     let ptr_temp = Place::from(ptr_temp);
237                     let block = unpack!(this.into(&ptr_temp, block, ptr));
238                     this.into(&ptr_temp.deref(), block, val)
239                 } else {
240                     let args: Vec<_> = args
241                         .into_iter()
242                         .map(|arg| unpack!(block = this.as_local_operand(block, arg)))
243                         .collect();
244
245                     let success = this.cfg.start_new_block();
246                     let cleanup = this.diverge_cleanup();
247                     this.cfg.terminate(
248                         block,
249                         source_info,
250                         TerminatorKind::Call {
251                             func: fun,
252                             args,
253                             cleanup: Some(cleanup),
254                             // FIXME(varkor): replace this with an uninhabitedness-based check.
255                             // This requires getting access to the current module to call
256                             // `tcx.is_ty_uninhabited_from`, which is currently tricky to do.
257                             destination: if expr.ty.is_never() {
258                                 None
259                             } else {
260                                 Some((destination.clone(), success))
261                             },
262                             from_hir_call,
263                         },
264                     );
265                     success.unit()
266                 }
267             }
268             ExprKind::Use { source } => {
269                 this.into(destination, block, source)
270             }
271
272             // These cases don't actually need a destination
273             ExprKind::Assign { .. }
274             | ExprKind::AssignOp { .. }
275             | ExprKind::Continue { .. }
276             | ExprKind::Break { .. }
277             | ExprKind::InlineAsm { .. }
278             | ExprKind::Return { .. } => {
279                 unpack!(block = this.stmt_expr(block, expr, None));
280                 this.cfg.push_assign_unit(block, source_info, destination);
281                 block.unit()
282             }
283
284             // Avoid creating a temporary
285             ExprKind::VarRef { .. } |
286             ExprKind::SelfRef |
287             ExprKind::StaticRef { .. } |
288             ExprKind::PlaceTypeAscription { .. } |
289             ExprKind::ValueTypeAscription { .. } => {
290                 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
291
292                 let place = unpack!(block = this.as_place(block, expr));
293                 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
294                 this.cfg
295                     .push_assign(block, source_info, destination, rvalue);
296                 block.unit()
297             }
298             ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => {
299                 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
300
301                 // Create a "fake" temporary variable so that we check that the
302                 // value is Sized. Usually, this is caught in type checking, but
303                 // in the case of box expr there is no such check.
304                 if !destination.projection.is_empty() {
305                     this.local_decls
306                         .push(LocalDecl::new_temp(expr.ty, expr.span));
307                 }
308
309                 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
310
311                 let place = unpack!(block = this.as_place(block, expr));
312                 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
313                 this.cfg
314                     .push_assign(block, source_info, destination, rvalue);
315                 block.unit()
316             }
317
318             // these are the cases that are more naturally handled by some other mode
319             ExprKind::Unary { .. }
320             | ExprKind::Binary { .. }
321             | ExprKind::Box { .. }
322             | ExprKind::Cast { .. }
323             | ExprKind::Pointer { .. }
324             | ExprKind::Repeat { .. }
325             | ExprKind::Borrow { .. }
326             | ExprKind::Array { .. }
327             | ExprKind::Tuple { .. }
328             | ExprKind::Adt { .. }
329             | ExprKind::Closure { .. }
330             | ExprKind::Literal { .. }
331             | ExprKind::Yield { .. } => {
332                 debug_assert!(match Category::of(&expr.kind).unwrap() {
333                     // should be handled above
334                     Category::Rvalue(RvalueFunc::Into) => false,
335
336                     // must be handled above or else we get an
337                     // infinite loop in the builder; see
338                     // e.g., `ExprKind::VarRef` above
339                     Category::Place => false,
340
341                     _ => true,
342                 });
343
344                 let rvalue = unpack!(block = this.as_local_rvalue(block, expr));
345                 this.cfg.push_assign(block, source_info, destination, rvalue);
346                 block.unit()
347             }
348         };
349
350         if !expr_is_block_or_scope {
351             let popped = this.block_context.pop();
352             assert!(popped.is_some());
353         }
354
355         block_and
356     }
357 }