]> git.proxmox.com Git - rustc.git/blob - src/librustc_mir/build/expr/into.rs
Imported Upstream version 1.9.0+dfsg1
[rustc.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 build::scope::LoopScope;
16 use hair::*;
17 use rustc::middle::region::CodeExtent;
18 use rustc::ty;
19 use rustc::mir::repr::*;
20 use syntax::codemap::Span;
21
22 impl<'a,'tcx> Builder<'a,'tcx> {
23 /// Compile `expr`, storing the result into `destination`, which
24 /// is assumed to be uninitialized.
25 pub fn into_expr(&mut self,
26 destination: &Lvalue<'tcx>,
27 mut block: BasicBlock,
28 expr: Expr<'tcx>)
29 -> BlockAnd<()>
30 {
31 debug!("into_expr(destination={:?}, block={:?}, expr={:?})",
32 destination, block, expr);
33
34 // since we frequently have to reference `self` from within a
35 // closure, where `self` would be shadowed, it's easier to
36 // just use the name `this` uniformly
37 let this = self;
38 let expr_span = expr.span;
39 let scope_id = this.innermost_scope_id();
40
41 match expr.kind {
42 ExprKind::Scope { extent, value } => {
43 this.in_scope(extent, block, |this, _| this.into(destination, block, value))
44 }
45 ExprKind::Block { body: ast_block } => {
46 this.ast_block(destination, block, ast_block)
47 }
48 ExprKind::Match { discriminant, arms } => {
49 this.match_expr(destination, expr_span, block, discriminant, arms)
50 }
51 ExprKind::If { condition: cond_expr, then: then_expr, otherwise: else_expr } => {
52 let operand = unpack!(block = this.as_operand(block, cond_expr));
53
54 let mut then_block = this.cfg.start_new_block();
55 let mut else_block = this.cfg.start_new_block();
56 this.cfg.terminate(block, scope_id, expr_span, TerminatorKind::If {
57 cond: operand,
58 targets: (then_block, else_block)
59 });
60
61 unpack!(then_block = this.into(destination, then_block, then_expr));
62 else_block = if let Some(else_expr) = else_expr {
63 unpack!(this.into(destination, else_block, else_expr))
64 } else {
65 // Body of the `if` expression without an `else` clause must return `()`, thus
66 // we implicitly generate a `else {}` if it is not specified.
67 let scope_id = this.innermost_scope_id();
68 this.cfg.push_assign_unit(else_block, scope_id, expr_span, destination);
69 else_block
70 };
71
72 let join_block = this.cfg.start_new_block();
73 this.cfg.terminate(then_block,
74 scope_id,
75 expr_span,
76 TerminatorKind::Goto { target: join_block });
77 this.cfg.terminate(else_block,
78 scope_id,
79 expr_span,
80 TerminatorKind::Goto { target: join_block });
81
82 join_block.unit()
83 }
84 ExprKind::LogicalOp { op, lhs, rhs } => {
85 // And:
86 //
87 // [block: If(lhs)] -true-> [else_block: If(rhs)] -true-> [true_block]
88 // | | (false)
89 // +----------false-----------+------------------> [false_block]
90 //
91 // Or:
92 //
93 // [block: If(lhs)] -false-> [else_block: If(rhs)] -true-> [true_block]
94 // | | (false)
95 // +----------true------------+-------------------> [false_block]
96
97 let (true_block, false_block, mut else_block, join_block) =
98 (this.cfg.start_new_block(), this.cfg.start_new_block(),
99 this.cfg.start_new_block(), this.cfg.start_new_block());
100
101 let lhs = unpack!(block = this.as_operand(block, lhs));
102 let blocks = match op {
103 LogicalOp::And => (else_block, false_block),
104 LogicalOp::Or => (true_block, else_block),
105 };
106 this.cfg.terminate(block,
107 scope_id,
108 expr_span,
109 TerminatorKind::If { cond: lhs, targets: blocks });
110
111 let rhs = unpack!(else_block = this.as_operand(else_block, rhs));
112 this.cfg.terminate(else_block, scope_id, expr_span, TerminatorKind::If {
113 cond: rhs,
114 targets: (true_block, false_block)
115 });
116
117 this.cfg.push_assign_constant(
118 true_block, scope_id, expr_span, destination,
119 Constant {
120 span: expr_span,
121 ty: this.hir.bool_ty(),
122 literal: this.hir.true_literal(),
123 });
124
125 this.cfg.push_assign_constant(
126 false_block, scope_id, expr_span, destination,
127 Constant {
128 span: expr_span,
129 ty: this.hir.bool_ty(),
130 literal: this.hir.false_literal(),
131 });
132
133 this.cfg.terminate(true_block,
134 scope_id,
135 expr_span,
136 TerminatorKind::Goto { target: join_block });
137 this.cfg.terminate(false_block,
138 scope_id,
139 expr_span,
140 TerminatorKind::Goto { target: join_block });
141
142 join_block.unit()
143 }
144 ExprKind::Loop { condition: opt_cond_expr, body } => {
145 // [block] --> [loop_block] ~~> [loop_block_end] -1-> [exit_block]
146 // ^ |
147 // | 0
148 // | |
149 // | v
150 // [body_block_end] <~~~ [body_block]
151 //
152 // If `opt_cond_expr` is `None`, then the graph is somewhat simplified:
153 //
154 // [block] --> [loop_block / body_block ] ~~> [body_block_end] [exit_block]
155 // ^ |
156 // | |
157 // +--------------------------+
158 //
159
160 let loop_block = this.cfg.start_new_block();
161 let exit_block = this.cfg.start_new_block();
162
163 // start the loop
164 this.cfg.terminate(block,
165 scope_id,
166 expr_span,
167 TerminatorKind::Goto { target: loop_block });
168
169 let might_break = this.in_loop_scope(loop_block, exit_block, move |this| {
170 // conduct the test, if necessary
171 let body_block;
172 if let Some(cond_expr) = opt_cond_expr {
173 // This loop has a condition, ergo its exit_block is reachable.
174 this.find_loop_scope(expr_span, None).might_break = true;
175
176 let loop_block_end;
177 let cond = unpack!(loop_block_end = this.as_operand(loop_block, cond_expr));
178 body_block = this.cfg.start_new_block();
179 this.cfg.terminate(loop_block_end,
180 scope_id,
181 expr_span,
182 TerminatorKind::If {
183 cond: cond,
184 targets: (body_block, exit_block)
185 });
186 } else {
187 body_block = loop_block;
188 }
189
190 // The “return” value of the loop body must always be an unit, but we cannot
191 // reuse that as a “return” value of the whole loop expressions, because some
192 // loops are diverging (e.g. `loop {}`). Thus, we introduce a unit temporary as
193 // the destination for the loop body and assign the loop’s own “return” value
194 // immediately after the iteration is finished.
195 let tmp = this.get_unit_temp();
196 // Execute the body, branching back to the test.
197 let body_block_end = unpack!(this.into(&tmp, body_block, body));
198 this.cfg.terminate(body_block_end,
199 scope_id,
200 expr_span,
201 TerminatorKind::Goto { target: loop_block });
202 });
203 // If the loop may reach its exit_block, we assign an empty tuple to the
204 // destination to keep the MIR well-formed.
205 if might_break {
206 this.cfg.push_assign_unit(exit_block, scope_id, expr_span, destination);
207 }
208 exit_block.unit()
209 }
210 ExprKind::Assign { lhs, rhs } => {
211 // Note: we evaluate assignments right-to-left. This
212 // is better for borrowck interaction with overloaded
213 // operators like x[j] = x[i].
214 let lhs = this.hir.mirror(lhs);
215 let lhs_span = lhs.span;
216 let rhs = unpack!(block = this.as_operand(block, rhs));
217 let lhs = unpack!(block = this.as_lvalue(block, lhs));
218 unpack!(block = this.build_drop(block, lhs_span, lhs.clone()));
219 this.cfg.push_assign(block, scope_id, expr_span, &lhs, Rvalue::Use(rhs));
220 block.unit()
221 }
222 ExprKind::AssignOp { op, lhs, rhs } => {
223 // FIXME(#28160) there is an interesting semantics
224 // question raised here -- should we "freeze" the
225 // value of the lhs here? I'm inclined to think not,
226 // since it seems closer to the semantics of the
227 // overloaded version, which takes `&mut self`. This
228 // only affects weird things like `x += {x += 1; x}`
229 // -- is that equal to `x + (x + 1)` or `2*(x+1)`?
230
231 // As above, RTL.
232 let rhs = unpack!(block = this.as_operand(block, rhs));
233 let lhs = unpack!(block = this.as_lvalue(block, lhs));
234
235 // we don't have to drop prior contents or anything
236 // because AssignOp is only legal for Copy types
237 // (overloaded ops should be desugared into a call).
238 this.cfg.push_assign(block, scope_id, expr_span, &lhs,
239 Rvalue::BinaryOp(op,
240 Operand::Consume(lhs.clone()),
241 rhs));
242
243 block.unit()
244 }
245 ExprKind::Continue { label } => {
246 this.break_or_continue(expr_span, label, block,
247 |loop_scope| loop_scope.continue_block)
248 }
249 ExprKind::Break { label } => {
250 this.break_or_continue(expr_span, label, block, |loop_scope| {
251 loop_scope.might_break = true;
252 loop_scope.break_block
253 })
254 }
255 ExprKind::Return { value } => {
256 block = match value {
257 Some(value) => unpack!(this.into(&Lvalue::ReturnPointer, block, value)),
258 None => {
259 this.cfg.push_assign_unit(block, scope_id,
260 expr_span, &Lvalue::ReturnPointer);
261 block
262 }
263 };
264 let extent = this.extent_of_return_scope();
265 this.exit_scope(expr_span, extent, block, END_BLOCK);
266 this.cfg.start_new_block().unit()
267 }
268 ExprKind::Call { ty, fun, args } => {
269 let diverges = match ty.sty {
270 ty::TyFnDef(_, _, ref f) | ty::TyFnPtr(ref f) => {
271 f.sig.0.output.diverges()
272 }
273 _ => false
274 };
275 let fun = unpack!(block = this.as_operand(block, fun));
276 let args: Vec<_> =
277 args.into_iter()
278 .map(|arg| unpack!(block = this.as_operand(block, arg)))
279 .collect();
280
281 let success = this.cfg.start_new_block();
282 let cleanup = this.diverge_cleanup();
283 this.cfg.terminate(block, scope_id, expr_span, TerminatorKind::Call {
284 func: fun,
285 args: args,
286 cleanup: cleanup,
287 destination: if diverges {
288 None
289 } else {
290 Some ((destination.clone(), success))
291 }
292 });
293 success.unit()
294 }
295
296 // these are the cases that are more naturally handled by some other mode
297 ExprKind::Unary { .. } |
298 ExprKind::Binary { .. } |
299 ExprKind::Box { .. } |
300 ExprKind::Cast { .. } |
301 ExprKind::ReifyFnPointer { .. } |
302 ExprKind::UnsafeFnPointer { .. } |
303 ExprKind::Unsize { .. } |
304 ExprKind::Repeat { .. } |
305 ExprKind::Borrow { .. } |
306 ExprKind::VarRef { .. } |
307 ExprKind::SelfRef |
308 ExprKind::StaticRef { .. } |
309 ExprKind::Vec { .. } |
310 ExprKind::Tuple { .. } |
311 ExprKind::Adt { .. } |
312 ExprKind::Closure { .. } |
313 ExprKind::Index { .. } |
314 ExprKind::Deref { .. } |
315 ExprKind::Literal { .. } |
316 ExprKind::InlineAsm { .. } |
317 ExprKind::Field { .. } => {
318 debug_assert!(match Category::of(&expr.kind).unwrap() {
319 Category::Rvalue(RvalueFunc::Into) => false,
320 _ => true,
321 });
322
323 let rvalue = unpack!(block = this.as_rvalue(block, expr));
324 this.cfg.push_assign(block, scope_id, expr_span, destination, rvalue);
325 block.unit()
326 }
327 }
328 }
329
330 fn break_or_continue<F>(&mut self,
331 span: Span,
332 label: Option<CodeExtent>,
333 block: BasicBlock,
334 exit_selector: F)
335 -> BlockAnd<()>
336 where F: FnOnce(&mut LoopScope) -> BasicBlock
337 {
338 let (exit_block, extent) = {
339 let loop_scope = self.find_loop_scope(span, label);
340 (exit_selector(loop_scope), loop_scope.extent)
341 };
342 self.exit_scope(span, extent, block, exit_block);
343 self.cfg.start_new_block().unit()
344 }
345 }