]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/build/expr/into.rs
Update the tools CI to use --no-fail-fast and --save-toolstates.
[rust.git] / src / librustc_mir / build / expr / into.rs
1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! See docs in build/expr/mod.rs
12
13 use build::{BlockAnd, BlockAndExtension, Builder};
14 use build::expr::category::{Category, RvalueFunc};
15 use hair::*;
16 use rustc::ty;
17 use rustc::mir::*;
18
19 use syntax::abi::Abi;
20
21 impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
22     /// Compile `expr`, storing the result into `destination`, which
23     /// is assumed to be uninitialized.
24     pub fn into_expr(&mut self,
25                      destination: &Place<'tcx>,
26                      mut block: BasicBlock,
27                      expr: Expr<'tcx>)
28                      -> BlockAnd<()>
29     {
30         debug!("into_expr(destination={:?}, block={:?}, expr={:?})",
31                destination, block, expr);
32
33         // since we frequently have to reference `self` from within a
34         // closure, where `self` would be shadowed, it's easier to
35         // just use the name `this` uniformly
36         let this = self;
37         let expr_span = expr.span;
38         let source_info = this.source_info(expr_span);
39
40         match expr.kind {
41             ExprKind::Scope { region_scope, lint_level, value } => {
42                 let region_scope = (region_scope, source_info);
43                 this.in_scope(region_scope, lint_level, block,
44                               |this| this.into(destination, block, value))
45             }
46             ExprKind::Block { body: ast_block } => {
47                 this.ast_block(destination, block, ast_block, source_info)
48             }
49             ExprKind::Match { discriminant, arms } => {
50                 this.match_expr(destination, expr_span, block, discriminant, arms)
51             }
52             ExprKind::NeverToAny { source } => {
53                 let source = this.hir.mirror(source);
54                 let is_call = match source.kind {
55                     ExprKind::Call { .. } => true,
56                     _ => false,
57                 };
58
59                 unpack!(block = this.as_local_rvalue(block, source));
60
61                 // This is an optimization. If the expression was a call then we already have an
62                 // unreachable block. Don't bother to terminate it and create a new one.
63                 if is_call {
64                     block.unit()
65                 } else {
66                     this.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
67                     let end_block = this.cfg.start_new_block();
68                     end_block.unit()
69                 }
70             }
71             ExprKind::If { condition: cond_expr, then: then_expr, otherwise: else_expr } => {
72                 let operand = unpack!(block = this.as_local_operand(block, cond_expr));
73
74                 let mut then_block = this.cfg.start_new_block();
75                 let mut else_block = this.cfg.start_new_block();
76                 let term = TerminatorKind::if_(this.hir.tcx(), operand, then_block, else_block);
77                 this.cfg.terminate(block, source_info, term);
78
79                 unpack!(then_block = this.into(destination, then_block, then_expr));
80                 else_block = if let Some(else_expr) = else_expr {
81                     unpack!(this.into(destination, else_block, else_expr))
82                 } else {
83                     // Body of the `if` expression without an `else` clause must return `()`, thus
84                     // we implicitly generate a `else {}` if it is not specified.
85                     this.cfg.push_assign_unit(else_block, source_info, destination);
86                     else_block
87                 };
88
89                 let join_block = this.cfg.start_new_block();
90                 this.cfg.terminate(then_block, source_info,
91                                    TerminatorKind::Goto { target: join_block });
92                 this.cfg.terminate(else_block, source_info,
93                                    TerminatorKind::Goto { target: join_block });
94
95                 join_block.unit()
96             }
97             ExprKind::LogicalOp { op, lhs, rhs } => {
98                 // And:
99                 //
100                 // [block: If(lhs)] -true-> [else_block: If(rhs)] -true-> [true_block]
101                 //        |                          | (false)
102                 //        +----------false-----------+------------------> [false_block]
103                 //
104                 // Or:
105                 //
106                 // [block: If(lhs)] -false-> [else_block: If(rhs)] -true-> [true_block]
107                 //        |                          | (false)
108                 //        +----------true------------+-------------------> [false_block]
109
110                 let (true_block, false_block, mut else_block, join_block) =
111                     (this.cfg.start_new_block(), this.cfg.start_new_block(),
112                      this.cfg.start_new_block(), this.cfg.start_new_block());
113
114                 let lhs = unpack!(block = this.as_local_operand(block, lhs));
115                 let blocks = match op {
116                     LogicalOp::And => (else_block, false_block),
117                     LogicalOp::Or => (true_block, else_block),
118                 };
119                 let term = TerminatorKind::if_(this.hir.tcx(), lhs, blocks.0, blocks.1);
120                 this.cfg.terminate(block, source_info, term);
121
122                 let rhs = unpack!(else_block = this.as_local_operand(else_block, rhs));
123                 let term = TerminatorKind::if_(this.hir.tcx(), rhs, true_block, false_block);
124                 this.cfg.terminate(else_block, source_info, term);
125
126                 this.cfg.push_assign_constant(
127                     true_block, source_info, destination,
128                     Constant {
129                         span: expr_span,
130                         ty: this.hir.bool_ty(),
131                         literal: this.hir.true_literal(),
132                     });
133
134                 this.cfg.push_assign_constant(
135                     false_block, source_info, destination,
136                     Constant {
137                         span: expr_span,
138                         ty: this.hir.bool_ty(),
139                         literal: this.hir.false_literal(),
140                     });
141
142                 this.cfg.terminate(true_block, source_info,
143                                    TerminatorKind::Goto { target: join_block });
144                 this.cfg.terminate(false_block, source_info,
145                                    TerminatorKind::Goto { target: join_block });
146
147                 join_block.unit()
148             }
149             ExprKind::Loop { condition: opt_cond_expr, body } => {
150                 // [block] --> [loop_block] ~~> [loop_block_end] -1-> [exit_block]
151                 //                  ^                  |
152                 //                  |                  0
153                 //                  |                  |
154                 //                  |                  v
155                 //           [body_block_end] <~~~ [body_block]
156                 //
157                 // If `opt_cond_expr` is `None`, then the graph is somewhat simplified:
158                 //
159                 // [block] --> [loop_block / body_block ] ~~> [body_block_end]    [exit_block]
160                 //                         ^                          |
161                 //                         |                          |
162                 //                         +--------------------------+
163                 //
164
165                 let loop_block = this.cfg.start_new_block();
166                 let exit_block = this.cfg.start_new_block();
167
168                 // start the loop
169                 this.cfg.terminate(block, source_info,
170                                    TerminatorKind::Goto { target: loop_block });
171
172                 this.in_breakable_scope(
173                     Some(loop_block), exit_block, destination.clone(),
174                     move |this| {
175                         // conduct the test, if necessary
176                         let body_block;
177                         if let Some(cond_expr) = opt_cond_expr {
178                             let loop_block_end;
179                             let cond = unpack!(
180                                 loop_block_end = this.as_local_operand(loop_block, cond_expr));
181                             body_block = this.cfg.start_new_block();
182                             let term = TerminatorKind::if_(this.hir.tcx(), cond,
183                                                            body_block, exit_block);
184                             this.cfg.terminate(loop_block_end, source_info, term);
185
186                             // if the test is false, there's no `break` to assign `destination`, so
187                             // we have to do it; this overwrites any `break`-assigned value but it's
188                             // always `()` anyway
189                             this.cfg.push_assign_unit(exit_block, source_info, destination);
190                         } else {
191                             body_block = loop_block;
192                         }
193
194                         // The “return” value of the loop body must always be an unit. We therefore
195                         // introduce a unit temporary as the destination for the loop body.
196                         let tmp = this.get_unit_temp();
197                         // Execute the body, branching back to the test.
198                         let body_block_end = unpack!(this.into(&tmp, body_block, body));
199                         this.cfg.terminate(body_block_end, source_info,
200                                            TerminatorKind::Goto { target: loop_block });
201                     }
202                 );
203                 exit_block.unit()
204             }
205             ExprKind::Call { ty, fun, args } => {
206                 // FIXME(canndrew): This is_never should probably be an is_uninhabited
207                 let diverges = expr.ty.is_never();
208                 let intrinsic = match ty.sty {
209                     ty::TyFnDef(def_id, _)  => {
210                         let f = ty.fn_sig(this.hir.tcx());
211                         if f.abi() == Abi::RustIntrinsic ||
212                            f.abi() == Abi::PlatformIntrinsic {
213                             Some(this.hir.tcx().item_name(def_id))
214                         } else {
215                             None
216                         }
217                     }
218                     _ => None
219                 };
220                 let intrinsic = intrinsic.as_ref().map(|s| &s[..]);
221                 let fun = unpack!(block = this.as_local_operand(block, fun));
222                 if intrinsic == Some("move_val_init") {
223                     // `move_val_init` has "magic" semantics - the second argument is
224                     // always evaluated "directly" into the first one.
225
226                     let mut args = args.into_iter();
227                     let ptr = args.next().expect("0 arguments to `move_val_init`");
228                     let val = args.next().expect("1 argument to `move_val_init`");
229                     assert!(args.next().is_none(), ">2 arguments to `move_val_init`");
230
231                     let ptr = this.hir.mirror(ptr);
232                     let ptr_ty = ptr.ty;
233                     // Create an *internal* temp for the pointer, so that unsafety
234                     // checking won't complain about the raw pointer assignment.
235                     let ptr_temp = this.local_decls.push(LocalDecl {
236                         mutability: Mutability::Mut,
237                         ty: ptr_ty,
238                         name: None,
239                         source_info,
240                         lexical_scope: source_info.scope,
241                         internal: true,
242                         is_user_variable: false
243                     });
244                     let ptr_temp = Place::Local(ptr_temp);
245                     let block = unpack!(this.into(&ptr_temp, block, ptr));
246                     this.into(&ptr_temp.deref(), block, val)
247                 } else {
248                     let args: Vec<_> =
249                         args.into_iter()
250                             .map(|arg| unpack!(block = this.as_local_operand(block, arg)))
251                             .collect();
252
253                     let success = this.cfg.start_new_block();
254                     let cleanup = this.diverge_cleanup();
255                     this.cfg.terminate(block, source_info, TerminatorKind::Call {
256                         func: fun,
257                         args,
258                         cleanup,
259                         destination: if diverges {
260                             None
261                         } else {
262                             Some ((destination.clone(), success))
263                         }
264                     });
265                     success.unit()
266                 }
267             }
268
269             // These cases don't actually need a destination
270             ExprKind::Assign { .. } |
271             ExprKind::AssignOp { .. } |
272             ExprKind::Continue { .. } |
273             ExprKind::Break { .. } |
274             ExprKind::InlineAsm { .. } |
275             ExprKind::Return {.. } => {
276                 this.stmt_expr(block, expr)
277             }
278
279             // these are the cases that are more naturally handled by some other mode
280             ExprKind::Unary { .. } |
281             ExprKind::Binary { .. } |
282             ExprKind::Box { .. } |
283             ExprKind::Cast { .. } |
284             ExprKind::Use { .. } |
285             ExprKind::ReifyFnPointer { .. } |
286             ExprKind::ClosureFnPointer { .. } |
287             ExprKind::UnsafeFnPointer { .. } |
288             ExprKind::Unsize { .. } |
289             ExprKind::Repeat { .. } |
290             ExprKind::Borrow { .. } |
291             ExprKind::VarRef { .. } |
292             ExprKind::SelfRef |
293             ExprKind::StaticRef { .. } |
294             ExprKind::Array { .. } |
295             ExprKind::Tuple { .. } |
296             ExprKind::Adt { .. } |
297             ExprKind::Closure { .. } |
298             ExprKind::Index { .. } |
299             ExprKind::Deref { .. } |
300             ExprKind::Literal { .. } |
301             ExprKind::Yield { .. } |
302             ExprKind::Field { .. } => {
303                 debug_assert!(match Category::of(&expr.kind).unwrap() {
304                     Category::Rvalue(RvalueFunc::Into) => false,
305                     _ => true,
306                 });
307
308                 let rvalue = unpack!(block = this.as_local_rvalue(block, expr));
309                 this.cfg.push_assign(block, source_info, destination, rvalue);
310                 block.unit()
311             }
312         }
313     }
314 }