|
| 1 | +// Copyright 2016 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 | +use build::{BlockAnd, BlockAndExtension, Builder}; |
| 12 | +use build::scope::LoopScope; |
| 13 | +use hair::*; |
| 14 | +use rustc::middle::region::CodeExtent; |
| 15 | +use rustc::mir::repr::*; |
| 16 | +use syntax::codemap::Span; |
| 17 | + |
| 18 | +impl<'a,'tcx> Builder<'a,'tcx> { |
| 19 | + |
| 20 | + pub fn stmt_expr(&mut self, mut block: BasicBlock, expr: Expr<'tcx>) -> BlockAnd<()> { |
| 21 | + let this = self; |
| 22 | + let expr_span = expr.span; |
| 23 | + let scope_id = this.innermost_scope_id(); |
| 24 | + // Handle a number of expressions that don't need a destination at all. This |
| 25 | + // avoids needing a mountain of temporary `()` variables. |
| 26 | + match expr.kind { |
| 27 | + ExprKind::Scope { extent, value } => { |
| 28 | + let value = this.hir.mirror(value); |
| 29 | + this.in_scope(extent, block, |this, _| this.stmt_expr(block, value)) |
| 30 | + } |
| 31 | + ExprKind::Assign { lhs, rhs } => { |
| 32 | + let lhs = this.hir.mirror(lhs); |
| 33 | + let rhs = this.hir.mirror(rhs); |
| 34 | + let scope_id = this.innermost_scope_id(); |
| 35 | + let lhs_span = lhs.span; |
| 36 | + |
| 37 | + let lhs_ty = lhs.ty; |
| 38 | + let rhs_ty = rhs.ty; |
| 39 | + |
| 40 | + let lhs_needs_drop = this.hir.needs_drop(lhs_ty); |
| 41 | + let rhs_needs_drop = this.hir.needs_drop(rhs_ty); |
| 42 | + |
| 43 | + // Note: we evaluate assignments right-to-left. This |
| 44 | + // is better for borrowck interaction with overloaded |
| 45 | + // operators like x[j] = x[i]. |
| 46 | + |
| 47 | + // Generate better code for things that don't need to be |
| 48 | + // dropped. |
| 49 | + let rhs = if lhs_needs_drop || rhs_needs_drop { |
| 50 | + let op = unpack!(block = this.as_operand(block, rhs)); |
| 51 | + Rvalue::Use(op) |
| 52 | + } else { |
| 53 | + unpack!(block = this.as_rvalue(block, rhs)) |
| 54 | + }; |
| 55 | + |
| 56 | + let lhs = unpack!(block = this.as_lvalue(block, lhs)); |
| 57 | + unpack!(block = this.build_drop(block, lhs_span, lhs.clone(), lhs_ty)); |
| 58 | + this.cfg.push_assign(block, scope_id, expr_span, &lhs, rhs); |
| 59 | + block.unit() |
| 60 | + } |
| 61 | + ExprKind::AssignOp { op, lhs, rhs } => { |
| 62 | + // FIXME(#28160) there is an interesting semantics |
| 63 | + // question raised here -- should we "freeze" the |
| 64 | + // value of the lhs here? I'm inclined to think not, |
| 65 | + // since it seems closer to the semantics of the |
| 66 | + // overloaded version, which takes `&mut self`. This |
| 67 | + // only affects weird things like `x += {x += 1; x}` |
| 68 | + // -- is that equal to `x + (x + 1)` or `2*(x+1)`? |
| 69 | + |
| 70 | + // As above, RTL. |
| 71 | + let rhs = unpack!(block = this.as_operand(block, rhs)); |
| 72 | + let lhs = unpack!(block = this.as_lvalue(block, lhs)); |
| 73 | + |
| 74 | + // we don't have to drop prior contents or anything |
| 75 | + // because AssignOp is only legal for Copy types |
| 76 | + // (overloaded ops should be desugared into a call). |
| 77 | + this.cfg.push_assign(block, scope_id, expr_span, &lhs, |
| 78 | + Rvalue::BinaryOp(op, |
| 79 | + Operand::Consume(lhs.clone()), |
| 80 | + rhs)); |
| 81 | + |
| 82 | + block.unit() |
| 83 | + } |
| 84 | + ExprKind::Continue { label } => { |
| 85 | + this.break_or_continue(expr_span, label, block, |
| 86 | + |loop_scope| loop_scope.continue_block) |
| 87 | + } |
| 88 | + ExprKind::Break { label } => { |
| 89 | + this.break_or_continue(expr_span, label, block, |loop_scope| { |
| 90 | + loop_scope.might_break = true; |
| 91 | + loop_scope.break_block |
| 92 | + }) |
| 93 | + } |
| 94 | + ExprKind::Return { value } => { |
| 95 | + block = match value { |
| 96 | + Some(value) => unpack!(this.into(&Lvalue::ReturnPointer, block, value)), |
| 97 | + None => { |
| 98 | + this.cfg.push_assign_unit(block, scope_id, |
| 99 | + expr_span, &Lvalue::ReturnPointer); |
| 100 | + block |
| 101 | + } |
| 102 | + }; |
| 103 | + let extent = this.extent_of_return_scope(); |
| 104 | + let return_block = this.return_block(); |
| 105 | + this.exit_scope(expr_span, extent, block, return_block); |
| 106 | + this.cfg.start_new_block().unit() |
| 107 | + } |
| 108 | + _ => { |
| 109 | + let expr_span = expr.span; |
| 110 | + let expr_ty = expr.ty; |
| 111 | + let temp = this.temp(expr.ty.clone()); |
| 112 | + unpack!(block = this.into(&temp, block, expr)); |
| 113 | + unpack!(block = this.build_drop(block, expr_span, temp, expr_ty)); |
| 114 | + block.unit() |
| 115 | + } |
| 116 | + } |
| 117 | + } |
| 118 | + |
| 119 | + fn break_or_continue<F>(&mut self, |
| 120 | + span: Span, |
| 121 | + label: Option<CodeExtent>, |
| 122 | + block: BasicBlock, |
| 123 | + exit_selector: F) |
| 124 | + -> BlockAnd<()> |
| 125 | + where F: FnOnce(&mut LoopScope) -> BasicBlock |
| 126 | + { |
| 127 | + let (exit_block, extent) = { |
| 128 | + let loop_scope = self.find_loop_scope(span, label); |
| 129 | + (exit_selector(loop_scope), loop_scope.extent) |
| 130 | + }; |
| 131 | + self.exit_scope(span, extent, block, exit_block); |
| 132 | + self.cfg.start_new_block().unit() |
| 133 | + } |
| 134 | + |
| 135 | +} |
0 commit comments