]>
Commit | Line | Data |
---|---|---|
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 | 13 | use build::{BlockAnd, BlockAndExtension, Builder}; |
e9174d1e | 14 | use build::expr::category::{Category, RvalueFunc}; |
e9174d1e | 15 | use hair::*; |
54a0048b | 16 | use rustc::ty; |
c30ab7b3 | 17 | use rustc::mir::*; |
e9174d1e | 18 | |
a7813a04 | 19 | impl<'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 | } |