]>
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 SL |
14 | use build::expr::category::{Category, RvalueFunc}; |
15 | use build::scope::LoopScope; | |
16 | use hair::*; | |
b039eaaf | 17 | use rustc::middle::region::CodeExtent; |
54a0048b | 18 | use rustc::ty; |
92a42be0 | 19 | use rustc::mir::repr::*; |
b039eaaf | 20 | use syntax::codemap::Span; |
e9174d1e | 21 | |
b039eaaf | 22 | impl<'a,'tcx> Builder<'a,'tcx> { |
e9174d1e SL |
23 | /// Compile `expr`, storing the result into `destination`, which |
24 | /// is assumed to be uninitialized. | |
25 | pub fn into_expr(&mut self, | |
b039eaaf | 26 | destination: &Lvalue<'tcx>, |
e9174d1e | 27 | mut block: BasicBlock, |
b039eaaf | 28 | expr: Expr<'tcx>) |
e9174d1e SL |
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; | |
54a0048b | 39 | let scope_id = this.innermost_scope_id(); |
e9174d1e SL |
40 | |
41 | match expr.kind { | |
42 | ExprKind::Scope { extent, value } => { | |
54a0048b | 43 | this.in_scope(extent, block, |this, _| this.into(destination, block, value)) |
e9174d1e SL |
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(); | |
54a0048b | 56 | this.cfg.terminate(block, scope_id, expr_span, TerminatorKind::If { |
e9174d1e | 57 | cond: operand, |
9cc50fc6 | 58 | targets: (then_block, else_block) |
e9174d1e SL |
59 | }); |
60 | ||
61 | unpack!(then_block = this.into(destination, then_block, then_expr)); | |
9cc50fc6 SL |
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. | |
54a0048b SL |
67 | let scope_id = this.innermost_scope_id(); |
68 | this.cfg.push_assign_unit(else_block, scope_id, expr_span, destination); | |
9cc50fc6 SL |
69 | else_block |
70 | }; | |
e9174d1e SL |
71 | |
72 | let join_block = this.cfg.start_new_block(); | |
54a0048b SL |
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 }); | |
e9174d1e SL |
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 { | |
9cc50fc6 SL |
103 | LogicalOp::And => (else_block, false_block), |
104 | LogicalOp::Or => (true_block, else_block), | |
e9174d1e | 105 | }; |
54a0048b SL |
106 | this.cfg.terminate(block, |
107 | scope_id, | |
108 | expr_span, | |
109 | TerminatorKind::If { cond: lhs, targets: blocks }); | |
e9174d1e SL |
110 | |
111 | let rhs = unpack!(else_block = this.as_operand(else_block, rhs)); | |
54a0048b | 112 | this.cfg.terminate(else_block, scope_id, expr_span, TerminatorKind::If { |
e9174d1e | 113 | cond: rhs, |
9cc50fc6 | 114 | targets: (true_block, false_block) |
e9174d1e SL |
115 | }); |
116 | ||
117 | this.cfg.push_assign_constant( | |
54a0048b | 118 | true_block, scope_id, expr_span, destination, |
e9174d1e SL |
119 | Constant { |
120 | span: expr_span, | |
b039eaaf SL |
121 | ty: this.hir.bool_ty(), |
122 | literal: this.hir.true_literal(), | |
e9174d1e SL |
123 | }); |
124 | ||
125 | this.cfg.push_assign_constant( | |
54a0048b | 126 | false_block, scope_id, expr_span, destination, |
e9174d1e SL |
127 | Constant { |
128 | span: expr_span, | |
b039eaaf SL |
129 | ty: this.hir.bool_ty(), |
130 | literal: this.hir.false_literal(), | |
e9174d1e SL |
131 | }); |
132 | ||
54a0048b SL |
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 }); | |
e9174d1e SL |
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 | |
54a0048b SL |
164 | this.cfg.terminate(block, |
165 | scope_id, | |
166 | expr_span, | |
167 | TerminatorKind::Goto { target: loop_block }); | |
e9174d1e | 168 | |
7453a54e | 169 | let might_break = this.in_loop_scope(loop_block, exit_block, move |this| { |
e9174d1e SL |
170 | // conduct the test, if necessary |
171 | let body_block; | |
e9174d1e | 172 | if let Some(cond_expr) = opt_cond_expr { |
7453a54e SL |
173 | // This loop has a condition, ergo its exit_block is reachable. |
174 | this.find_loop_scope(expr_span, None).might_break = true; | |
175 | ||
e9174d1e SL |
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, | |
54a0048b SL |
180 | scope_id, |
181 | expr_span, | |
182 | TerminatorKind::If { | |
e9174d1e | 183 | cond: cond, |
9cc50fc6 | 184 | targets: (body_block, exit_block) |
e9174d1e SL |
185 | }); |
186 | } else { | |
187 | body_block = loop_block; | |
188 | } | |
189 | ||
7453a54e SL |
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)); | |
54a0048b SL |
198 | this.cfg.terminate(body_block_end, |
199 | scope_id, | |
200 | expr_span, | |
201 | TerminatorKind::Goto { target: loop_block }); | |
7453a54e SL |
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 { | |
54a0048b | 206 | this.cfg.push_assign_unit(exit_block, scope_id, expr_span, destination); |
7453a54e SL |
207 | } |
208 | exit_block.unit() | |
e9174d1e SL |
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]. | |
54a0048b SL |
214 | let lhs = this.hir.mirror(lhs); |
215 | let lhs_span = lhs.span; | |
e9174d1e SL |
216 | let rhs = unpack!(block = this.as_operand(block, rhs)); |
217 | let lhs = unpack!(block = this.as_lvalue(block, lhs)); | |
54a0048b SL |
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)); | |
e9174d1e SL |
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). | |
54a0048b | 238 | this.cfg.push_assign(block, scope_id, expr_span, &lhs, |
e9174d1e SL |
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 } => { | |
7453a54e SL |
250 | this.break_or_continue(expr_span, label, block, |loop_scope| { |
251 | loop_scope.might_break = true; | |
252 | loop_scope.break_block | |
253 | }) | |
e9174d1e SL |
254 | } |
255 | ExprKind::Return { value } => { | |
9cc50fc6 SL |
256 | block = match value { |
257 | Some(value) => unpack!(this.into(&Lvalue::ReturnPointer, block, value)), | |
258 | None => { | |
54a0048b SL |
259 | this.cfg.push_assign_unit(block, scope_id, |
260 | expr_span, &Lvalue::ReturnPointer); | |
9cc50fc6 SL |
261 | block |
262 | } | |
263 | }; | |
54a0048b | 264 | let extent = this.extent_of_return_scope(); |
e9174d1e SL |
265 | this.exit_scope(expr_span, extent, block, END_BLOCK); |
266 | this.cfg.start_new_block().unit() | |
267 | } | |
9cc50fc6 SL |
268 | ExprKind::Call { ty, fun, args } => { |
269 | let diverges = match ty.sty { | |
54a0048b SL |
270 | ty::TyFnDef(_, _, ref f) | ty::TyFnPtr(ref f) => { |
271 | f.sig.0.output.diverges() | |
272 | } | |
9cc50fc6 SL |
273 | _ => false |
274 | }; | |
92a42be0 | 275 | let fun = unpack!(block = this.as_operand(block, fun)); |
e9174d1e SL |
276 | let args: Vec<_> = |
277 | args.into_iter() | |
92a42be0 | 278 | .map(|arg| unpack!(block = this.as_operand(block, arg))) |
e9174d1e | 279 | .collect(); |
9cc50fc6 | 280 | |
e9174d1e | 281 | let success = this.cfg.start_new_block(); |
9cc50fc6 | 282 | let cleanup = this.diverge_cleanup(); |
54a0048b | 283 | this.cfg.terminate(block, scope_id, expr_span, TerminatorKind::Call { |
9cc50fc6 SL |
284 | func: fun, |
285 | args: args, | |
7453a54e SL |
286 | cleanup: cleanup, |
287 | destination: if diverges { | |
288 | None | |
289 | } else { | |
290 | Some ((destination.clone(), success)) | |
9cc50fc6 SL |
291 | } |
292 | }); | |
e9174d1e SL |
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)); | |
54a0048b | 324 | this.cfg.push_assign(block, scope_id, expr_span, destination, rvalue); |
e9174d1e SL |
325 | block.unit() |
326 | } | |
327 | } | |
328 | } | |
329 | ||
330 | fn break_or_continue<F>(&mut self, | |
b039eaaf SL |
331 | span: Span, |
332 | label: Option<CodeExtent>, | |
e9174d1e SL |
333 | block: BasicBlock, |
334 | exit_selector: F) | |
335 | -> BlockAnd<()> | |
7453a54e | 336 | where F: FnOnce(&mut LoopScope) -> BasicBlock |
e9174d1e | 337 | { |
7453a54e SL |
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); | |
e9174d1e SL |
343 | self.cfg.start_new_block().unit() |
344 | } | |
345 | } |