| // 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_pos::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) -> Datum<'tcx, Lvalue> { |
| self.match_kind( |
| |d| d, |
| |_| 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(), |
| &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)) |
| } |
| } |