summary refs log tree commit diff
path: root/src/librustc_mir/build/expr/as_lvalue.rs
blob: 4e03ed489eb9fcf93bcf9ca1828c521b3a42ac3d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! See docs in build/expr/mod.rs

use build::{BlockAnd, BlockAndExtension, Builder};
use build::expr::category::Category;
use hair::*;
use rustc::mir::repr::*;

impl<'a,'tcx> Builder<'a,'tcx> {
    /// Compile `expr`, yielding an lvalue that we can move from etc.
    pub fn as_lvalue<M>(&mut self,
                        block: BasicBlock,
                        expr: M)
                        -> BlockAnd<Lvalue<'tcx>>
        where M: Mirror<'tcx, Output=Expr<'tcx>>
    {
        let expr = self.hir.mirror(expr);
        self.expr_as_lvalue(block, expr)
    }

    fn expr_as_lvalue(&mut self,
                      mut block: BasicBlock,
                      expr: Expr<'tcx>)
                      -> BlockAnd<Lvalue<'tcx>> {
        debug!("expr_as_lvalue(block={:?}, expr={:?})", block, expr);

        let this = self;
        let expr_span = expr.span;
        match expr.kind {
            ExprKind::Scope { extent, value } => {
                this.in_scope(extent, block, |this| this.as_lvalue(block, value))
            }
            ExprKind::Field { lhs, name } => {
                let lvalue = unpack!(block = this.as_lvalue(block, lhs));
                let lvalue = lvalue.field(name);
                block.and(lvalue)
            }
            ExprKind::Deref { arg } => {
                let lvalue = unpack!(block = this.as_lvalue(block, arg));
                let lvalue = lvalue.deref();
                block.and(lvalue)
            }
            ExprKind::Index { lhs, index } => {
                let (usize_ty, bool_ty) = (this.hir.usize_ty(), this.hir.bool_ty());

                let slice = unpack!(block = this.as_lvalue(block, lhs));

                let idx = unpack!(block = this.as_operand(block, index));

                // bounds check:
                let (len, lt) = (this.temp(usize_ty.clone()), this.temp(bool_ty));
                this.cfg.push_assign(block, expr_span, // len = len(slice)
                                     &len, Rvalue::Len(slice.clone()));
                this.cfg.push_assign(block, expr_span, // lt = idx < len
                                     &lt, Rvalue::BinaryOp(BinOp::Lt,
                                                           idx.clone(),
                                                           Operand::Consume(len.clone())));

                let (success, failure) = (this.cfg.start_new_block(), this.cfg.start_new_block());
                this.cfg.terminate(block,
                                   Terminator::If {
                                       cond: Operand::Consume(lt),
                                       targets: (success, failure),
                                   });
                this.panic_bounds_check(failure, idx.clone(), Operand::Consume(len), expr_span);
                success.and(slice.index(idx))
            }
            ExprKind::SelfRef => {
                block.and(Lvalue::Arg(0))
            }
            ExprKind::VarRef { id } => {
                let index = this.var_indices[&id];
                block.and(Lvalue::Var(index))
            }
            ExprKind::StaticRef { id } => {
                block.and(Lvalue::Static(id))
            }

            ExprKind::Vec { .. } |
            ExprKind::Tuple { .. } |
            ExprKind::Adt { .. } |
            ExprKind::Closure { .. } |
            ExprKind::Unary { .. } |
            ExprKind::Binary { .. } |
            ExprKind::LogicalOp { .. } |
            ExprKind::Box { .. } |
            ExprKind::Cast { .. } |
            ExprKind::ReifyFnPointer { .. } |
            ExprKind::UnsafeFnPointer { .. } |
            ExprKind::Unsize { .. } |
            ExprKind::Repeat { .. } |
            ExprKind::Borrow { .. } |
            ExprKind::If { .. } |
            ExprKind::Match { .. } |
            ExprKind::Loop { .. } |
            ExprKind::Block { .. } |
            ExprKind::Assign { .. } |
            ExprKind::AssignOp { .. } |
            ExprKind::Break { .. } |
            ExprKind::Continue { .. } |
            ExprKind::Return { .. } |
            ExprKind::Literal { .. } |
            ExprKind::InlineAsm { .. } |
            ExprKind::Call { .. } => {
                // these are not lvalues, so we need to make a temporary.
                debug_assert!(match Category::of(&expr.kind) {
                    Some(Category::Lvalue) => false,
                    _ => true,
                });
                this.as_temp(block, expr)
            }
        }
    }
}