]> git.proxmox.com Git - rustc.git/blame - src/librustc_mir/build/expr/into.rs
New upstream version 1.14.0+dfsg1
[rustc.git] / src / librustc_mir / build / expr / into.rs
CommitLineData
e9174d1e
SL
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
92a42be0 13use build::{BlockAnd, BlockAndExtension, Builder};
e9174d1e 14use build::expr::category::{Category, RvalueFunc};
e9174d1e 15use hair::*;
54a0048b 16use rustc::ty;
c30ab7b3 17use rustc::mir::*;
e9174d1e 18
a7813a04 19impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
e9174d1e
SL
20 /// Compile `expr`, storing the result into `destination`, which
21 /// is assumed to be uninitialized.
22 pub fn into_expr(&mut self,
b039eaaf 23 destination: &Lvalue<'tcx>,
e9174d1e 24 mut block: BasicBlock,
b039eaaf 25 expr: Expr<'tcx>)
e9174d1e
SL
26 -> BlockAnd<()>
27 {
28 debug!("into_expr(destination={:?}, block={:?}, expr={:?})",
29 destination, block, expr);
30
31 // since we frequently have to reference `self` from within a
32 // closure, where `self` would be shadowed, it's easier to
33 // just use the name `this` uniformly
34 let this = self;
35 let expr_span = expr.span;
3157f602 36 let source_info = this.source_info(expr_span);
e9174d1e
SL
37
38 match expr.kind {
39 ExprKind::Scope { extent, value } => {
3157f602 40 this.in_scope(extent, block, |this| this.into(destination, block, value))
e9174d1e
SL
41 }
42 ExprKind::Block { body: ast_block } => {
a7813a04 43 this.ast_block(destination, expr.ty.is_nil(), block, ast_block)
e9174d1e
SL
44 }
45 ExprKind::Match { discriminant, arms } => {
46 this.match_expr(destination, expr_span, block, discriminant, arms)
47 }
5bcae85e
SL
48 ExprKind::NeverToAny { source } => {
49 let source = this.hir.mirror(source);
50 let is_call = match source.kind {
51 ExprKind::Call { .. } => true,
52 _ => false,
53 };
54
55 unpack!(block = this.as_rvalue(block, source));
56
57 // This is an optimization. If the expression was a call then we already have an
58 // unreachable block. Don't bother to terminate it and create a new one.
59 if is_call {
60 block.unit()
61 } else {
62 this.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
63 let end_block = this.cfg.start_new_block();
64 end_block.unit()
65 }
66 }
e9174d1e
SL
67 ExprKind::If { condition: cond_expr, then: then_expr, otherwise: else_expr } => {
68 let operand = unpack!(block = this.as_operand(block, cond_expr));
69
70 let mut then_block = this.cfg.start_new_block();
71 let mut else_block = this.cfg.start_new_block();
3157f602 72 this.cfg.terminate(block, source_info, TerminatorKind::If {
e9174d1e 73 cond: operand,
9cc50fc6 74 targets: (then_block, else_block)
e9174d1e
SL
75 });
76
77 unpack!(then_block = this.into(destination, then_block, then_expr));
9cc50fc6
SL
78 else_block = if let Some(else_expr) = else_expr {
79 unpack!(this.into(destination, else_block, else_expr))
80 } else {
81 // Body of the `if` expression without an `else` clause must return `()`, thus
82 // we implicitly generate a `else {}` if it is not specified.
3157f602 83 this.cfg.push_assign_unit(else_block, source_info, destination);
9cc50fc6
SL
84 else_block
85 };
e9174d1e
SL
86
87 let join_block = this.cfg.start_new_block();
3157f602 88 this.cfg.terminate(then_block, source_info,
54a0048b 89 TerminatorKind::Goto { target: join_block });
3157f602 90 this.cfg.terminate(else_block, source_info,
54a0048b 91 TerminatorKind::Goto { target: join_block });
e9174d1e
SL
92
93 join_block.unit()
94 }
95 ExprKind::LogicalOp { op, lhs, rhs } => {
96 // And:
97 //
98 // [block: If(lhs)] -true-> [else_block: If(rhs)] -true-> [true_block]
99 // | | (false)
100 // +----------false-----------+------------------> [false_block]
101 //
102 // Or:
103 //
104 // [block: If(lhs)] -false-> [else_block: If(rhs)] -true-> [true_block]
105 // | | (false)
106 // +----------true------------+-------------------> [false_block]
107
108 let (true_block, false_block, mut else_block, join_block) =
109 (this.cfg.start_new_block(), this.cfg.start_new_block(),
110 this.cfg.start_new_block(), this.cfg.start_new_block());
111
112 let lhs = unpack!(block = this.as_operand(block, lhs));
113 let blocks = match op {
9cc50fc6
SL
114 LogicalOp::And => (else_block, false_block),
115 LogicalOp::Or => (true_block, else_block),
e9174d1e 116 };
3157f602 117 this.cfg.terminate(block, source_info,
54a0048b 118 TerminatorKind::If { cond: lhs, targets: blocks });
e9174d1e
SL
119
120 let rhs = unpack!(else_block = this.as_operand(else_block, rhs));
3157f602 121 this.cfg.terminate(else_block, source_info, TerminatorKind::If {
e9174d1e 122 cond: rhs,
9cc50fc6 123 targets: (true_block, false_block)
e9174d1e
SL
124 });
125
126 this.cfg.push_assign_constant(
3157f602 127 true_block, source_info, destination,
e9174d1e
SL
128 Constant {
129 span: expr_span,
b039eaaf
SL
130 ty: this.hir.bool_ty(),
131 literal: this.hir.true_literal(),
e9174d1e
SL
132 });
133
134 this.cfg.push_assign_constant(
3157f602 135 false_block, source_info, destination,
e9174d1e
SL
136 Constant {
137 span: expr_span,
b039eaaf
SL
138 ty: this.hir.bool_ty(),
139 literal: this.hir.false_literal(),
e9174d1e
SL
140 });
141
3157f602 142 this.cfg.terminate(true_block, source_info,
54a0048b 143 TerminatorKind::Goto { target: join_block });
3157f602 144 this.cfg.terminate(false_block, source_info,
54a0048b 145 TerminatorKind::Goto { target: join_block });
e9174d1e
SL
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
3157f602 169 this.cfg.terminate(block, source_info,
54a0048b 170 TerminatorKind::Goto { target: loop_block });
e9174d1e 171
7453a54e 172 let might_break = this.in_loop_scope(loop_block, exit_block, move |this| {
e9174d1e
SL
173 // conduct the test, if necessary
174 let body_block;
e9174d1e 175 if let Some(cond_expr) = opt_cond_expr {
7453a54e
SL
176 // This loop has a condition, ergo its exit_block is reachable.
177 this.find_loop_scope(expr_span, None).might_break = true;
178
e9174d1e
SL
179 let loop_block_end;
180 let cond = unpack!(loop_block_end = this.as_operand(loop_block, cond_expr));
181 body_block = this.cfg.start_new_block();
3157f602 182 this.cfg.terminate(loop_block_end, source_info,
54a0048b 183 TerminatorKind::If {
e9174d1e 184 cond: cond,
9cc50fc6 185 targets: (body_block, exit_block)
e9174d1e
SL
186 });
187 } else {
188 body_block = loop_block;
189 }
190
7453a54e
SL
191 // The “return” value of the loop body must always be an unit, but we cannot
192 // reuse that as a “return” value of the whole loop expressions, because some
193 // loops are diverging (e.g. `loop {}`). Thus, we introduce a unit temporary as
194 // the destination for the loop body and assign the loop’s own “return” value
195 // immediately after the iteration is finished.
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));
3157f602 199 this.cfg.terminate(body_block_end, source_info,
54a0048b 200 TerminatorKind::Goto { target: loop_block });
7453a54e
SL
201 });
202 // If the loop may reach its exit_block, we assign an empty tuple to the
203 // destination to keep the MIR well-formed.
204 if might_break {
3157f602 205 this.cfg.push_assign_unit(exit_block, source_info, destination);
7453a54e
SL
206 }
207 exit_block.unit()
e9174d1e 208 }
9cc50fc6
SL
209 ExprKind::Call { ty, fun, args } => {
210 let diverges = match ty.sty {
54a0048b 211 ty::TyFnDef(_, _, ref f) | ty::TyFnPtr(ref f) => {
5bcae85e
SL
212 // FIXME(canndrew): This is_never should probably be an is_uninhabited
213 f.sig.0.output.is_never()
54a0048b 214 }
9cc50fc6
SL
215 _ => false
216 };
92a42be0 217 let fun = unpack!(block = this.as_operand(block, fun));
e9174d1e
SL
218 let args: Vec<_> =
219 args.into_iter()
92a42be0 220 .map(|arg| unpack!(block = this.as_operand(block, arg)))
e9174d1e 221 .collect();
9cc50fc6 222
e9174d1e 223 let success = this.cfg.start_new_block();
9cc50fc6 224 let cleanup = this.diverge_cleanup();
3157f602 225 this.cfg.terminate(block, source_info, TerminatorKind::Call {
9cc50fc6
SL
226 func: fun,
227 args: args,
7453a54e
SL
228 cleanup: cleanup,
229 destination: if diverges {
230 None
231 } else {
232 Some ((destination.clone(), success))
9cc50fc6
SL
233 }
234 });
e9174d1e
SL
235 success.unit()
236 }
237
a7813a04
XL
238 // These cases don't actually need a destination
239 ExprKind::Assign { .. } |
240 ExprKind::AssignOp { .. } |
241 ExprKind::Continue { .. } |
242 ExprKind::Break { .. } |
243 ExprKind::Return {.. } => {
244 this.stmt_expr(block, expr)
245 }
246
e9174d1e
SL
247 // these are the cases that are more naturally handled by some other mode
248 ExprKind::Unary { .. } |
249 ExprKind::Binary { .. } |
250 ExprKind::Box { .. } |
251 ExprKind::Cast { .. } |
1bb2cb6e 252 ExprKind::Use { .. } |
e9174d1e
SL
253 ExprKind::ReifyFnPointer { .. } |
254 ExprKind::UnsafeFnPointer { .. } |
255 ExprKind::Unsize { .. } |
256 ExprKind::Repeat { .. } |
257 ExprKind::Borrow { .. } |
258 ExprKind::VarRef { .. } |
259 ExprKind::SelfRef |
260 ExprKind::StaticRef { .. } |
261 ExprKind::Vec { .. } |
262 ExprKind::Tuple { .. } |
263 ExprKind::Adt { .. } |
264 ExprKind::Closure { .. } |
265 ExprKind::Index { .. } |
266 ExprKind::Deref { .. } |
267 ExprKind::Literal { .. } |
268 ExprKind::InlineAsm { .. } |
269 ExprKind::Field { .. } => {
270 debug_assert!(match Category::of(&expr.kind).unwrap() {
271 Category::Rvalue(RvalueFunc::Into) => false,
272 _ => true,
273 });
274
275 let rvalue = unpack!(block = this.as_rvalue(block, expr));
3157f602 276 this.cfg.push_assign(block, source_info, destination, rvalue);
e9174d1e
SL
277 block.unit()
278 }
279 }
280 }
e9174d1e 281}