// Copyright 2012-2014 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.

//! ## The Datum module
//!
//! A `Datum` encapsulates the result of evaluating a Rust expression.  It
//! contains a `ValueRef` indicating the result, a `Ty` describing
//! the Rust type, but also a *kind*. The kind indicates whether the datum
//! has cleanup scheduled (lvalue) or not (rvalue) and -- in the case of
//! rvalues -- whether or not the value is "by ref" or "by value".
//!
//! The datum API is designed to try and help you avoid memory errors like
//! forgetting to arrange cleanup or duplicating a value. The type of the
//! datum incorporates the kind, and thus reflects whether it has cleanup
//! scheduled:
//!
//! - `Datum<Lvalue>` -- by ref, cleanup scheduled
//! - `Datum<Rvalue>` -- by value or by ref, no cleanup scheduled
//! - `Datum<Expr>` -- either `Datum<Lvalue>` or `Datum<Rvalue>`
//!
//! Rvalue and expr datums are noncopyable, and most of the methods on
//! datums consume the datum itself (with some notable exceptions). This
//! reflects the fact that datums may represent affine values which ought
//! to be consumed exactly once, and if you were to try to (for example)
//! store an affine value multiple times, you would be duplicating it,
//! which would certainly be a bug.
//!
//! Some of the datum methods, however, are designed to work only on
//! copyable values such as ints or pointers. Those methods may borrow the
//! datum (`&self`) rather than consume it, but they always include
//! assertions on the type of the value represented to check that this
//! makes sense. An example is `shallow_copy()`, which duplicates
//! a datum value.
//!
//! Translating an expression always yields a `Datum<Expr>` result, but
//! the methods `to_[lr]value_datum()` can be used to coerce a
//! `Datum<Expr>` into a `Datum<Lvalue>` or `Datum<Rvalue>` as
//! needed. Coercing to an lvalue is fairly common, and generally occurs
//! whenever it is necessary to inspect a value and pull out its
//! subcomponents (for example, a match, or indexing expression). Coercing
//! to an rvalue is more unusual; it occurs when moving values from place
//! to place, such as in an assignment expression or parameter passing.
//!
//! ### Lvalues in detail
//!
//! An lvalue datum is one for which cleanup has been scheduled. Lvalue
//! datums are always located in memory, and thus the `ValueRef` for an
//! LLVM value is always a pointer to the actual Rust value. This means
//! that if the Datum has a Rust type of `int`, then the LLVM type of the
//! `ValueRef` will be `int*` (pointer to int).
//!
//! Because lvalues already have cleanups scheduled, the memory must be
//! zeroed to prevent the cleanup from taking place (presuming that the
//! Rust type needs drop in the first place, otherwise it doesn't
//! matter). The Datum code automatically performs this zeroing when the
//! value is stored to a new location, for example.
//!
//! Lvalues usually result from evaluating lvalue expressions. For
//! example, evaluating a local variable `x` yields an lvalue, as does a
//! reference to a field like `x.f` or an index `x[i]`.
//!
//! Lvalue datums can also arise by *converting* an rvalue into an lvalue.
//! This is done with the `to_lvalue_datum` method defined on
//! `Datum<Expr>`. Basically this method just schedules cleanup if the
//! datum is an rvalue, possibly storing the value into a stack slot first
//! if needed. Converting rvalues into lvalues occurs in constructs like
//! `&foo()` or `match foo() { ref x => ... }`, where the user is
//! implicitly requesting a temporary.
//!
//! ### Rvalues in detail
//!
//! Rvalues datums are values with no cleanup scheduled. One must be
//! careful with rvalue datums to ensure that cleanup is properly
//! arranged, usually by converting to an lvalue datum or by invoking the
//! `add_clean` method.
//!
//! ### Scratch datums
//!
//! Sometimes you need some temporary scratch space.  The functions
//! `[lr]value_scratch_datum()` can be used to get temporary stack
//! space. As their name suggests, they yield lvalues and rvalues
//! respectively. That is, the slot from `lvalue_scratch_datum` will have
//! cleanup arranged, and the slot from `rvalue_scratch_datum` does not.

pub use self::Expr::*;
pub use self::RvalueMode::*;

use llvm::ValueRef;
use adt;
use base::*;
use build::{Load, Store};
use common::*;
use cleanup;
use cleanup::{CleanupMethods, DropHintDatum, DropHintMethods};
use expr;
use tvec;
use value::Value;
use rustc::ty::Ty;

use std::fmt;
use syntax::ast;
use syntax::codemap::DUMMY_SP;

/// A `Datum` encapsulates the result of evaluating an expression.  It
/// describes where the value is stored, what Rust type the value has,
/// whether it is addressed by reference, and so forth. Please refer
/// the section on datums in `README.md` for more details.
#[derive(Clone, Copy)]
pub struct Datum<'tcx, K> {
    /// The llvm value.  This is either a pointer to the Rust value or
    /// the value itself, depending on `kind` below.
    pub val: ValueRef,

    /// The rust type of the value.
    pub ty: Ty<'tcx>,

    /// Indicates whether this is by-ref or by-value.
    pub kind: K,
}

impl<'tcx, K: fmt::Debug> fmt::Debug for Datum<'tcx, K> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Datum({:?}, {:?}, {:?})",
               Value(self.val), self.ty, self.kind)
    }
}

pub struct DatumBlock<'blk, 'tcx: 'blk, K> {
    pub bcx: Block<'blk, 'tcx>,
    pub datum: Datum<'tcx, K>,
}

#[derive(Debug)]
pub enum Expr {
    /// a fresh value that was produced and which has no cleanup yet
    /// because it has not yet "landed" into its permanent home
    RvalueExpr(Rvalue),

    /// `val` is a pointer into memory for which a cleanup is scheduled
    /// (and thus has type *T). If you move out of an Lvalue, you must
    /// zero out the memory (FIXME #5016).
    LvalueExpr(Lvalue),
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum DropFlagInfo {
    DontZeroJustUse(ast::NodeId),
    ZeroAndMaintain(ast::NodeId),
    None,
}

impl DropFlagInfo {
    pub fn must_zero(&self) -> bool {
        match *self {
            DropFlagInfo::DontZeroJustUse(..) => false,
            DropFlagInfo::ZeroAndMaintain(..) => true,
            DropFlagInfo::None => true,
        }
    }

    pub fn hint_datum<'blk, 'tcx>(&self, bcx: Block<'blk, 'tcx>)
                              -> Option<DropHintDatum<'tcx>> {
        let id = match *self {
            DropFlagInfo::None => return None,
            DropFlagInfo::DontZeroJustUse(id) |
            DropFlagInfo::ZeroAndMaintain(id) => id,
        };

        let hints = bcx.fcx.lldropflag_hints.borrow();
        let retval = hints.hint_datum(id);
        assert!(retval.is_some(), "An id (={}) means must have a hint", id);
        retval
    }
}

// FIXME: having Lvalue be `Copy` is a bit of a footgun, since clients
// may not realize that subparts of an Lvalue can have a subset of
// drop-flags associated with them, while this as written will just
// memcpy the drop_flag_info. But, it is an easier way to get `_match`
// off the ground to just let this be `Copy` for now.
#[derive(Copy, Clone, Debug)]
pub struct Lvalue {
    pub source: &'static str,
    pub drop_flag_info: DropFlagInfo
}

#[derive(Debug)]
pub struct Rvalue {
    pub mode: RvalueMode
}

/// Classifies what action we should take when a value is moved away
/// with respect to its drop-flag.
///
/// Long term there will be no need for this classification: all flags
/// (which will be stored on the stack frame) will have the same
/// interpretation and maintenance code associated with them.
#[derive(Copy, Clone, Debug)]
pub enum HintKind {
    /// When the value is moved, set the drop-flag to "dropped"
    /// (i.e. "zero the flag", even when the specific representation
    /// is not literally 0) and when it is reinitialized, set the
    /// drop-flag back to "initialized".
    ZeroAndMaintain,

    /// When the value is moved, do not set the drop-flag to "dropped"
    /// However, continue to read the drop-flag in deciding whether to
    /// drop. (In essence, the path/fragment in question will never
    /// need to be dropped at the points where it is moved away by
    /// this code, but we are defending against the scenario where
    /// some *other* code could move away (or drop) the value and thus
    /// zero-the-flag, which is why we will still read from it.
    DontZeroJustUse,
}

impl Lvalue { // Constructors for various Lvalues.
    pub fn new<'blk, 'tcx>(source: &'static str) -> Lvalue {
        debug!("Lvalue at {} no drop flag info", source);
        Lvalue { source: source, drop_flag_info: DropFlagInfo::None }
    }

    pub fn new_dropflag_hint(source: &'static str) -> Lvalue {
        debug!("Lvalue at {} is drop flag hint", source);
        Lvalue { source: source, drop_flag_info: DropFlagInfo::None }
    }

    pub fn new_with_hint<'blk, 'tcx>(source: &'static str,
                                     bcx: Block<'blk, 'tcx>,
                                     id: ast::NodeId,
                                     k: HintKind) -> Lvalue {
        let (opt_id, info) = {
            let hint_available = Lvalue::has_dropflag_hint(bcx, id) &&
                bcx.tcx().sess.nonzeroing_move_hints();
            let info = match k {
                HintKind::ZeroAndMaintain if hint_available =>
                    DropFlagInfo::ZeroAndMaintain(id),
                HintKind::DontZeroJustUse if hint_available =>
                    DropFlagInfo::DontZeroJustUse(id),
                _ =>
                    DropFlagInfo::None,
            };
            (Some(id), info)
        };
        debug!("Lvalue at {}, id: {:?} info: {:?}", source, opt_id, info);
        Lvalue { source: source, drop_flag_info: info }
    }
} // end Lvalue constructor methods.

impl Lvalue {
    fn has_dropflag_hint<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                     id: ast::NodeId) -> bool {
        let hints = bcx.fcx.lldropflag_hints.borrow();
        hints.has_hint(id)
    }
    pub fn dropflag_hint<'blk, 'tcx>(&self, bcx: Block<'blk, 'tcx>)
                                 -> Option<DropHintDatum<'tcx>> {
        self.drop_flag_info.hint_datum(bcx)
    }
}

impl Rvalue {
    pub fn new(m: RvalueMode) -> Rvalue {
        Rvalue { mode: m }
    }
}

// Make Datum linear for more type safety.
impl Drop for Rvalue {
    fn drop(&mut self) { }
}

#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum RvalueMode {
    /// `val` is a pointer to the actual value (and thus has type *T)
    ByRef,

    /// `val` is the actual value (*only used for immediates* like ints, ptrs)
    ByValue,
}

pub fn immediate_rvalue<'tcx>(val: ValueRef, ty: Ty<'tcx>) -> Datum<'tcx, Rvalue> {
    return Datum::new(val, ty, Rvalue::new(ByValue));
}

pub fn immediate_rvalue_bcx<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                        val: ValueRef,
                                        ty: Ty<'tcx>)
                                        -> DatumBlock<'blk, 'tcx, Rvalue> {
    return DatumBlock::new(bcx, immediate_rvalue(val, ty))
}

/// Allocates temporary space on the stack using alloca() and returns a by-ref Datum pointing to
/// it. The memory will be dropped upon exit from `scope`. The callback `populate` should
/// initialize the memory.
///
/// The flag `zero` indicates how the temporary space itself should be
/// initialized at the outset of the function; the only time that
/// `InitAlloca::Uninit` is a valid value for `zero` is when the
/// caller can prove that either (1.) the code injected by `populate`
/// onto `bcx` always dominates the end of `scope`, or (2.) the data
/// being allocated has no associated destructor.
pub fn lvalue_scratch_datum<'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
                                           ty: Ty<'tcx>,
                                           name: &str,
                                           zero: InitAlloca,
                                           scope: cleanup::ScopeId,
                                           populate: F)
                                           -> DatumBlock<'blk, 'tcx, Lvalue> where
    F: FnOnce(Block<'blk, 'tcx>, ValueRef) -> Block<'blk, 'tcx>,
{
    // Very subtle: potentially initialize the scratch memory at point where it is alloca'ed.
    // (See discussion at Issue 30530.)
    let scratch = alloc_ty_init(bcx, ty, zero, name);
    debug!("lvalue_scratch_datum scope={:?} scratch={:?} ty={:?}",
           scope, Value(scratch), ty);

    // Subtle. Populate the scratch memory *before* scheduling cleanup.
    let bcx = populate(bcx, scratch);
    bcx.fcx.schedule_drop_mem(scope, scratch, ty, None);

    DatumBlock::new(bcx, Datum::new(scratch, ty, Lvalue::new("datum::lvalue_scratch_datum")))
}

/// Allocates temporary space on the stack using alloca() and returns a by-ref Datum pointing to
/// it.  If `zero` is true, the space will be zeroed when it is allocated; this is normally not
/// necessary, but in the case of automatic rooting in match statements it is possible to have
/// temporaries that may not get initialized if a certain arm is not taken, so we must zero them.
/// You must arrange any cleanups etc yourself!
pub fn rvalue_scratch_datum<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                        ty: Ty<'tcx>,
                                        name: &str)
                                        -> Datum<'tcx, Rvalue> {
    let scratch = alloc_ty(bcx, ty, name);
    call_lifetime_start(bcx, scratch);
    Datum::new(scratch, ty, Rvalue::new(ByRef))
}

/// Indicates the "appropriate" mode for this value, which is either by ref or by value, depending
/// on whether type is immediate or not.
pub fn appropriate_rvalue_mode<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                         ty: Ty<'tcx>) -> RvalueMode {
    if type_is_immediate(ccx, ty) {
        ByValue
    } else {
        ByRef
    }
}

fn add_rvalue_clean<'a, 'tcx>(mode: RvalueMode,
                              fcx: &FunctionContext<'a, 'tcx>,
                              scope: cleanup::ScopeId,
                              val: ValueRef,
                              ty: Ty<'tcx>) {
    debug!("add_rvalue_clean scope={:?} val={:?} ty={:?}",
           scope, Value(val), ty);
    match mode {
        ByValue => { fcx.schedule_drop_immediate(scope, val, ty); }
        ByRef => {
            fcx.schedule_lifetime_end(scope, val);
            fcx.schedule_drop_mem(scope, val, ty, None);
        }
    }
}

pub trait KindOps {

    /// Take appropriate action after the value in `datum` has been
    /// stored to a new location.
    fn post_store<'blk, 'tcx>(&self,
                              bcx: Block<'blk, 'tcx>,
                              val: ValueRef,
                              ty: Ty<'tcx>)
                              -> Block<'blk, 'tcx>;

    /// True if this mode is a reference mode, meaning that the datum's
    /// val field is a pointer to the actual value
    fn is_by_ref(&self) -> bool;

    /// Converts to an Expr kind
    fn to_expr_kind(self) -> Expr;

}

impl KindOps for Rvalue {
    fn post_store<'blk, 'tcx>(&self,
                              bcx: Block<'blk, 'tcx>,
                              _val: ValueRef,
                              _ty: Ty<'tcx>)
                              -> Block<'blk, 'tcx> {
        // No cleanup is scheduled for an rvalue, so we don't have
        // to do anything after a move to cancel or duplicate it.
        if self.is_by_ref() {
            call_lifetime_end(bcx, _val);
        }
        bcx
    }

    fn is_by_ref(&self) -> bool {
        self.mode == ByRef
    }

    fn to_expr_kind(self) -> Expr {
        RvalueExpr(self)
    }
}

impl KindOps for Lvalue {
    /// If an lvalue is moved, we must zero out the memory in which it resides so as to cancel
    /// cleanup. If an @T lvalue is copied, we must increment the reference count.
    fn post_store<'blk, 'tcx>(&self,
                              bcx: Block<'blk, 'tcx>,
                              val: ValueRef,
                              ty: Ty<'tcx>)
                              -> Block<'blk, 'tcx> {
        let _icx = push_ctxt("<Lvalue as KindOps>::post_store");
        if bcx.fcx.type_needs_drop(ty) {
            // cancel cleanup of affine values:
            // 1. if it has drop-hint, mark as moved; then code
            //    aware of drop-hint won't bother calling the
            //    drop-glue itself.
            if let Some(hint_datum) = self.drop_flag_info.hint_datum(bcx) {
                let moved_hint_byte = adt::DTOR_MOVED_HINT;
                let hint_llval = hint_datum.to_value().value();
                Store(bcx, C_u8(bcx.fcx.ccx, moved_hint_byte), hint_llval);
            }
            // 2. if the drop info says its necessary, drop-fill the memory.
            if self.drop_flag_info.must_zero() {
                let () = drop_done_fill_mem(bcx, val, ty);
            }
            bcx
        } else {
            // FIXME (#5016) would be nice to assert this, but we have
            // to allow for e.g. DontZeroJustUse flags, for now.
            //
            // (The dropflag hint construction should be taking
            // !type_needs_drop into account; earlier analysis phases
            // may not have all the info they need to include such
            // information properly, I think; in particular the
            // fragments analysis works on a non-monomorphized view of
            // the code.)
            //
            // assert_eq!(self.drop_flag_info, DropFlagInfo::None);
            bcx
        }
    }

    fn is_by_ref(&self) -> bool {
        true
    }

    fn to_expr_kind(self) -> Expr {
        LvalueExpr(self)
    }
}

impl KindOps for Expr {
    fn post_store<'blk, 'tcx>(&self,
                              bcx: Block<'blk, 'tcx>,
                              val: ValueRef,
                              ty: Ty<'tcx>)
                              -> Block<'blk, 'tcx> {
        match *self {
            LvalueExpr(ref l) => l.post_store(bcx, val, ty),
            RvalueExpr(ref r) => r.post_store(bcx, val, ty),
        }
    }

    fn is_by_ref(&self) -> bool {
        match *self {
            LvalueExpr(ref l) => l.is_by_ref(),
            RvalueExpr(ref r) => r.is_by_ref()
        }
    }

    fn to_expr_kind(self) -> Expr {
        self
    }
}

impl<'tcx> Datum<'tcx, Rvalue> {
    /// Schedules a cleanup for this datum in the given scope. That means that this datum is no
    /// longer an rvalue datum; hence, this function consumes the datum and returns the contained
    /// ValueRef.
    pub fn add_clean<'a>(self,
                         fcx: &FunctionContext<'a, 'tcx>,
                         scope: cleanup::ScopeId)
                         -> ValueRef {
        add_rvalue_clean(self.kind.mode, fcx, scope, self.val, self.ty);
        self.val
    }

    /// Returns an lvalue datum (that is, a by ref datum with cleanup scheduled). If `self` is not
    /// already an lvalue, cleanup will be scheduled in the temporary scope for `expr_id`.
    pub fn to_lvalue_datum_in_scope<'blk>(self,
                                          bcx: Block<'blk, 'tcx>,
                                          name: &str,
                                          scope: cleanup::ScopeId)
                                          -> DatumBlock<'blk, 'tcx, Lvalue> {
        let fcx = bcx.fcx;

        match self.kind.mode {
            ByRef => {
                add_rvalue_clean(ByRef, fcx, scope, self.val, self.ty);
                DatumBlock::new(bcx, Datum::new(
                    self.val,
                    self.ty,
                    Lvalue::new("datum::to_lvalue_datum_in_scope")))
            }

            ByValue => {
                lvalue_scratch_datum(
                    bcx, self.ty, name, InitAlloca::Dropped, scope,
                    |bcx, llval| {
                        debug!("populate call for Datum::to_lvalue_datum_in_scope \
                                self.ty={:?}", self.ty);
                        // do not call_lifetime_start here; the
                        // `InitAlloc::Dropped` will start scratch
                        // value's lifetime at open of function body.
                        let bcx = self.store_to(bcx, llval);
                        bcx.fcx.schedule_lifetime_end(scope, llval);
                        bcx
                    })
            }
        }
    }

    pub fn to_ref_datum<'blk>(self, bcx: Block<'blk, 'tcx>)
                              -> DatumBlock<'blk, 'tcx, Rvalue> {
        let mut bcx = bcx;
        match self.kind.mode {
            ByRef => DatumBlock::new(bcx, self),
            ByValue => {
                let scratch = rvalue_scratch_datum(bcx, self.ty, "to_ref");
                bcx = self.store_to(bcx, scratch.val);
                DatumBlock::new(bcx, scratch)
            }
        }
    }

    pub fn to_appropriate_datum<'blk>(self, bcx: Block<'blk, 'tcx>)
                                      -> DatumBlock<'blk, 'tcx, Rvalue> {
        match self.appropriate_rvalue_mode(bcx.ccx()) {
            ByRef => {
                self.to_ref_datum(bcx)
            }
            ByValue => {
                match self.kind.mode {
                    ByValue => DatumBlock::new(bcx, self),
                    ByRef => {
                        let llval = load_ty(bcx, self.val, self.ty);
                        call_lifetime_end(bcx, self.val);
                        DatumBlock::new(bcx, Datum::new(llval, self.ty, Rvalue::new(ByValue)))
                    }
                }
            }
        }
    }
}

/// Methods suitable for "expr" datums that could be either lvalues or
/// rvalues. These include coercions into lvalues/rvalues but also a number
/// of more general operations. (Some of those operations could be moved to
/// the more general `impl<K> Datum<K>`, but it's convenient to have them
/// here since we can `match self.kind` rather than having to implement
/// generic methods in `KindOps`.)
impl<'tcx> Datum<'tcx, Expr> {
    fn match_kind<R, F, G>(self, if_lvalue: F, if_rvalue: G) -> R where
        F: FnOnce(Datum<'tcx, Lvalue>) -> R,
        G: FnOnce(Datum<'tcx, Rvalue>) -> R,
    {
        let Datum { val, ty, kind } = self;
        match kind {
            LvalueExpr(l) => if_lvalue(Datum::new(val, ty, l)),
            RvalueExpr(r) => if_rvalue(Datum::new(val, ty, r)),
        }
    }

    /// Asserts that this datum *is* an lvalue and returns it.
    #[allow(dead_code)] // potentially useful
    pub fn assert_lvalue(self, bcx: Block) -> Datum<'tcx, Lvalue> {
        self.match_kind(
            |d| d,
            |_| bcx.sess().bug("assert_lvalue given rvalue"))
    }

    pub fn store_to_dest<'blk>(self,
                               bcx: Block<'blk, 'tcx>,
                               dest: expr::Dest,
                               expr_id: ast::NodeId)
                               -> Block<'blk, 'tcx> {
        match dest {
            expr::Ignore => {
                self.add_clean_if_rvalue(bcx, expr_id);
                bcx
            }
            expr::SaveIn(addr) => {
                self.store_to(bcx, addr)
            }
        }
    }

    /// Arranges cleanup for `self` if it is an rvalue. Use when you are done working with a value
    /// that may need drop.
    pub fn add_clean_if_rvalue<'blk>(self,
                                     bcx: Block<'blk, 'tcx>,
                                     expr_id: ast::NodeId) {
        self.match_kind(
            |_| { /* Nothing to do, cleanup already arranged */ },
            |r| {
                let scope = cleanup::temporary_scope(bcx.tcx(), expr_id);
                r.add_clean(bcx.fcx, scope);
            })
    }

    pub fn to_lvalue_datum<'blk>(self,
                                 bcx: Block<'blk, 'tcx>,
                                 name: &str,
                                 expr_id: ast::NodeId)
                                 -> DatumBlock<'blk, 'tcx, Lvalue> {
        debug!("to_lvalue_datum self: {:?}", self);

        self.match_kind(
            |l| DatumBlock::new(bcx, l),
            |r| {
                let scope = cleanup::temporary_scope(bcx.tcx(), expr_id);
                r.to_lvalue_datum_in_scope(bcx, name, scope)
            })
    }

    /// Ensures that we have an rvalue datum (that is, a datum with no cleanup scheduled).
    pub fn to_rvalue_datum<'blk>(self,
                                 bcx: Block<'blk, 'tcx>,
                                 name: &'static str)
                                 -> DatumBlock<'blk, 'tcx, Rvalue> {
        self.match_kind(
            |l| {
                let mut bcx = bcx;
                match l.appropriate_rvalue_mode(bcx.ccx()) {
                    ByRef => {
                        let scratch = rvalue_scratch_datum(bcx, l.ty, name);
                        bcx = l.store_to(bcx, scratch.val);
                        DatumBlock::new(bcx, scratch)
                    }
                    ByValue => {
                        let v = load_ty(bcx, l.val, l.ty);
                        bcx = l.kind.post_store(bcx, l.val, l.ty);
                        DatumBlock::new(bcx, Datum::new(v, l.ty, Rvalue::new(ByValue)))
                    }
                }
            },
            |r| DatumBlock::new(bcx, r))
    }

}

/// Methods suitable only for lvalues. These include the various
/// operations to extract components out of compound data structures,
/// such as extracting the field from a struct or a particular element
/// from an array.
impl<'tcx> Datum<'tcx, Lvalue> {
    /// Converts a datum into a by-ref value. The datum type must be one which is always passed by
    /// reference.
    pub fn to_llref(self) -> ValueRef {
        self.val
    }

    // Extracts a component of a compound data structure (e.g., a field from a
    // struct). Note that if self is an opened, unsized type then the returned
    // datum may also be unsized _without the size information_. It is the
    // callers responsibility to package the result in some way to make a valid
    // datum in that case (e.g., by making a fat pointer or opened pair).
    pub fn get_element<'blk, F>(&self, bcx: Block<'blk, 'tcx>, ty: Ty<'tcx>,
                                gep: F)
                                -> Datum<'tcx, Lvalue> where
        F: FnOnce(adt::MaybeSizedValue) -> ValueRef,
    {
        let val = if type_is_sized(bcx.tcx(), self.ty) {
            let val = adt::MaybeSizedValue::sized(self.val);
            gep(val)
        } else {
            let val = adt::MaybeSizedValue::unsized_(
                Load(bcx, expr::get_dataptr(bcx, self.val)),
                Load(bcx, expr::get_meta(bcx, self.val)));
            gep(val)
        };
        Datum {
            val: val,
            kind: Lvalue::new("Datum::get_element"),
            ty: ty,
        }
    }

    pub fn get_vec_base_and_len<'blk>(&self, bcx: Block<'blk, 'tcx>)
                                      -> (ValueRef, ValueRef) {
        //! Converts a vector into the slice pair.

        tvec::get_base_and_len(bcx, self.val, self.ty)
    }
}

/// Generic methods applicable to any sort of datum.
impl<'tcx, K: KindOps + fmt::Debug> Datum<'tcx, K> {
    pub fn new(val: ValueRef, ty: Ty<'tcx>, kind: K) -> Datum<'tcx, K> {
        Datum { val: val, ty: ty, kind: kind }
    }

    pub fn to_expr_datum(self) -> Datum<'tcx, Expr> {
        let Datum { val, ty, kind } = self;
        Datum { val: val, ty: ty, kind: kind.to_expr_kind() }
    }

    /// Moves or copies this value into a new home, as appropriate depending on the type of the
    /// datum. This method consumes the datum, since it would be incorrect to go on using the datum
    /// if the value represented is affine (and hence the value is moved).
    pub fn store_to<'blk>(self,
                          bcx: Block<'blk, 'tcx>,
                          dst: ValueRef)
                          -> Block<'blk, 'tcx> {
        self.shallow_copy_raw(bcx, dst);

        self.kind.post_store(bcx, self.val, self.ty)
    }

    /// Helper function that performs a shallow copy of this value into `dst`, which should be a
    /// pointer to a memory location suitable for `self.ty`. `dst` should contain uninitialized
    /// memory (either newly allocated, zeroed, or dropped).
    ///
    /// This function is private to datums because it leaves memory in an unstable state, where the
    /// source value has been copied but not zeroed. Public methods are `store_to` (if you no
    /// longer need the source value) or `shallow_copy` (if you wish the source value to remain
    /// valid).
    fn shallow_copy_raw<'blk>(&self,
                              bcx: Block<'blk, 'tcx>,
                              dst: ValueRef)
                              -> Block<'blk, 'tcx> {
        let _icx = push_ctxt("copy_to_no_check");

        if type_is_zero_size(bcx.ccx(), self.ty) {
            return bcx;
        }

        if self.kind.is_by_ref() {
            memcpy_ty(bcx, dst, self.val, self.ty);
        } else {
            store_ty(bcx, self.val, dst, self.ty);
        }

        return bcx;
    }

    /// Copies the value into a new location. This function always preserves the existing datum as
    /// a valid value. Therefore, it does not consume `self` and, also, cannot be applied to affine
    /// values (since they must never be duplicated).
    pub fn shallow_copy<'blk>(&self,
                              bcx: Block<'blk, 'tcx>,
                              dst: ValueRef)
                              -> Block<'blk, 'tcx> {
        /*!
         * Copies the value into a new location. This function always
         * preserves the existing datum as a valid value. Therefore,
         * it does not consume `self` and, also, cannot be applied to
         * affine values (since they must never be duplicated).
         */

        assert!(!self.ty
                     .moves_by_default(&bcx.tcx().empty_parameter_environment(), DUMMY_SP));
        self.shallow_copy_raw(bcx, dst)
    }

    /// See the `appropriate_rvalue_mode()` function
    pub fn appropriate_rvalue_mode<'a>(&self, ccx: &CrateContext<'a, 'tcx>)
                                       -> RvalueMode {
        appropriate_rvalue_mode(ccx, self.ty)
    }

    /// Converts `self` into a by-value `ValueRef`. Consumes this datum (i.e., absolves you of
    /// responsibility to cleanup the value). For this to work, the value must be something
    /// scalar-ish (like an int or a pointer) which (1) does not require drop glue and (2) is
    /// naturally passed around by value, and not by reference.
    pub fn to_llscalarish<'blk>(self, bcx: Block<'blk, 'tcx>) -> ValueRef {
        assert!(!bcx.fcx.type_needs_drop(self.ty));
        assert!(self.appropriate_rvalue_mode(bcx.ccx()) == ByValue);
        if self.kind.is_by_ref() {
            load_ty(bcx, self.val, self.ty)
        } else {
            self.val
        }
    }

    pub fn to_llbool<'blk>(self, bcx: Block<'blk, 'tcx>) -> ValueRef {
        assert!(self.ty.is_bool());
        self.to_llscalarish(bcx)
    }
}

impl<'blk, 'tcx, K> DatumBlock<'blk, 'tcx, K> {
    pub fn new(bcx: Block<'blk, 'tcx>, datum: Datum<'tcx, K>)
               -> DatumBlock<'blk, 'tcx, K> {
        DatumBlock { bcx: bcx, datum: datum }
    }
}

impl<'blk, 'tcx, K: KindOps + fmt::Debug> DatumBlock<'blk, 'tcx, K> {
    pub fn to_expr_datumblock(self) -> DatumBlock<'blk, 'tcx, Expr> {
        DatumBlock::new(self.bcx, self.datum.to_expr_datum())
    }
}

impl<'blk, 'tcx> DatumBlock<'blk, 'tcx, Expr> {
    pub fn store_to_dest(self,
                         dest: expr::Dest,
                         expr_id: ast::NodeId) -> Block<'blk, 'tcx> {
        let DatumBlock { bcx, datum } = self;
        datum.store_to_dest(bcx, dest, expr_id)
    }

    pub fn to_llbool(self) -> Result<'blk, 'tcx> {
        let DatumBlock { datum, bcx } = self;
        Result::new(bcx, datum.to_llbool(bcx))
    }
}
