]> git.lizzy.rs Git - rust.git/blob - src/librustc_trans/trans/datum.rs
/*! -> //!
[rust.git] / src / librustc_trans / trans / datum.rs
1 // Copyright 2012-2014 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 the section on datums in `doc.rs` for an overview of what Datums are and how they are
12 //! intended to be used.
13
14 pub use self::Expr::*;
15 pub use self::RvalueMode::*;
16
17 use llvm::ValueRef;
18 use trans::base::*;
19 use trans::build::Load;
20 use trans::common::*;
21 use trans::cleanup;
22 use trans::cleanup::CleanupMethods;
23 use trans::expr;
24 use trans::tvec;
25 use trans::type_of;
26 use middle::ty::{mod, Ty};
27 use util::ppaux::{ty_to_string};
28
29 use std::fmt;
30 use syntax::ast;
31
32 /**
33  * A `Datum` encapsulates the result of evaluating an expression.  It
34  * describes where the value is stored, what Rust type the value has,
35  * whether it is addressed by reference, and so forth. Please refer
36  * the section on datums in `doc.rs` for more details.
37  */
38 #[deriving(Clone)]
39 pub struct Datum<'tcx, K> {
40     /// The llvm value.  This is either a pointer to the Rust value or
41     /// the value itself, depending on `kind` below.
42     pub val: ValueRef,
43
44     /// The rust type of the value.
45     pub ty: Ty<'tcx>,
46
47     /// Indicates whether this is by-ref or by-value.
48     pub kind: K,
49 }
50
51 pub struct DatumBlock<'blk, 'tcx: 'blk, K> {
52     pub bcx: Block<'blk, 'tcx>,
53     pub datum: Datum<'tcx, K>,
54 }
55
56 #[deriving(Show)]
57 pub enum Expr {
58     /// a fresh value that was produced and which has no cleanup yet
59     /// because it has not yet "landed" into its permanent home
60     RvalueExpr(Rvalue),
61
62     /// `val` is a pointer into memory for which a cleanup is scheduled
63     /// (and thus has type *T). If you move out of an Lvalue, you must
64     /// zero out the memory (FIXME #5016).
65     LvalueExpr,
66 }
67
68 #[deriving(Clone, Show)]
69 pub struct Lvalue;
70
71 #[deriving(Show)]
72 pub struct Rvalue {
73     pub mode: RvalueMode
74 }
75
76 impl Rvalue {
77     pub fn new(m: RvalueMode) -> Rvalue {
78         Rvalue { mode: m }
79     }
80 }
81
82 // Make Datum linear for more type safety.
83 impl Drop for Rvalue {
84     fn drop(&mut self) { }
85 }
86
87 #[deriving(PartialEq, Eq, Hash, Show)]
88 pub enum RvalueMode {
89     /// `val` is a pointer to the actual value (and thus has type *T)
90     ByRef,
91
92     /// `val` is the actual value (*only used for immediates* like ints, ptrs)
93     ByValue,
94 }
95
96 pub fn immediate_rvalue<'tcx>(val: ValueRef, ty: Ty<'tcx>) -> Datum<'tcx, Rvalue> {
97     return Datum::new(val, ty, Rvalue::new(ByValue));
98 }
99
100 pub fn immediate_rvalue_bcx<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
101                                         val: ValueRef,
102                                         ty: Ty<'tcx>)
103                                         -> DatumBlock<'blk, 'tcx, Rvalue> {
104     return DatumBlock::new(bcx, immediate_rvalue(val, ty))
105 }
106
107
108 /// Allocates temporary space on the stack using alloca() and returns a by-ref Datum pointing to
109 /// it. The memory will be dropped upon exit from `scope`. The callback `populate` should
110 /// initialize the memory. If `zero` is true, the space will be zeroed when it is allocated; this
111 /// is not necessary unless `bcx` does not dominate the end of `scope`.
112 pub fn lvalue_scratch_datum<'blk, 'tcx, A>(bcx: Block<'blk, 'tcx>,
113                                            ty: Ty<'tcx>,
114                                            name: &str,
115                                            zero: bool,
116                                            scope: cleanup::ScopeId,
117                                            arg: A,
118                                            populate: |A, Block<'blk, 'tcx>, ValueRef|
119                                                       -> Block<'blk, 'tcx>)
120                                           -> DatumBlock<'blk, 'tcx, Lvalue> {
121     let scratch = if zero {
122         alloca_zeroed(bcx, ty, name)
123     } else {
124         let llty = type_of::type_of(bcx.ccx(), ty);
125         alloca(bcx, llty, name)
126     };
127
128     // Subtle. Populate the scratch memory *before* scheduling cleanup.
129     let bcx = populate(arg, bcx, scratch);
130     bcx.fcx.schedule_lifetime_end(scope, scratch);
131     bcx.fcx.schedule_drop_mem(scope, scratch, ty);
132
133     DatumBlock::new(bcx, Datum::new(scratch, ty, Lvalue))
134 }
135
136 /// Allocates temporary space on the stack using alloca() and returns a by-ref Datum pointing to
137 /// it.  If `zero` is true, the space will be zeroed when it is allocated; this is normally not
138 /// necessary, but in the case of automatic rooting in match statements it is possible to have
139 /// temporaries that may not get initialized if a certain arm is not taken, so we must zero them.
140 /// You must arrange any cleanups etc yourself!
141 pub fn rvalue_scratch_datum<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
142                                         ty: Ty<'tcx>,
143                                         name: &str)
144                                         -> Datum<'tcx, Rvalue> {
145     let llty = type_of::type_of(bcx.ccx(), ty);
146     let scratch = alloca(bcx, llty, name);
147     Datum::new(scratch, ty, Rvalue::new(ByRef))
148 }
149
150 /// Indicates the "appropriate" mode for this value, which is either by ref or by value, depending
151 /// on whether type is immediate or not.
152 pub fn appropriate_rvalue_mode<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
153                                          ty: Ty<'tcx>) -> RvalueMode {
154     if type_is_immediate(ccx, ty) {
155         ByValue
156     } else {
157         ByRef
158     }
159 }
160
161 fn add_rvalue_clean<'a, 'tcx>(mode: RvalueMode,
162                               fcx: &FunctionContext<'a, 'tcx>,
163                               scope: cleanup::ScopeId,
164                               val: ValueRef,
165                               ty: Ty<'tcx>) {
166     match mode {
167         ByValue => { fcx.schedule_drop_immediate(scope, val, ty); }
168         ByRef => {
169             fcx.schedule_lifetime_end(scope, val);
170             fcx.schedule_drop_mem(scope, val, ty);
171         }
172     }
173 }
174
175 pub trait KindOps {
176
177     /**
178      * Take appropriate action after the value in `datum` has been
179      * stored to a new location.
180      */
181     fn post_store<'blk, 'tcx>(&self,
182                               bcx: Block<'blk, 'tcx>,
183                               val: ValueRef,
184                               ty: Ty<'tcx>)
185                               -> Block<'blk, 'tcx>;
186
187     /**
188      * True if this mode is a reference mode, meaning that the datum's
189      * val field is a pointer to the actual value
190      */
191     fn is_by_ref(&self) -> bool;
192
193     /**
194      * Converts to an Expr kind
195      */
196     fn to_expr_kind(self) -> Expr;
197
198 }
199
200 impl KindOps for Rvalue {
201     fn post_store<'blk, 'tcx>(&self,
202                               bcx: Block<'blk, 'tcx>,
203                               _val: ValueRef,
204                               _ty: Ty<'tcx>)
205                               -> Block<'blk, 'tcx> {
206         // No cleanup is scheduled for an rvalue, so we don't have
207         // to do anything after a move to cancel or duplicate it.
208         bcx
209     }
210
211     fn is_by_ref(&self) -> bool {
212         self.mode == ByRef
213     }
214
215     fn to_expr_kind(self) -> Expr {
216         RvalueExpr(self)
217     }
218 }
219
220 impl KindOps for Lvalue {
221     /// If an lvalue is moved, we must zero out the memory in which it resides so as to cancel
222     /// cleanup. If an @T lvalue is copied, we must increment the reference count.
223     fn post_store<'blk, 'tcx>(&self,
224                               bcx: Block<'blk, 'tcx>,
225                               val: ValueRef,
226                               ty: Ty<'tcx>)
227                               -> Block<'blk, 'tcx> {
228         if ty::type_needs_drop(bcx.tcx(), ty) {
229             // cancel cleanup of affine values by zeroing out
230             let () = zero_mem(bcx, val, ty);
231             bcx
232         } else {
233             bcx
234         }
235     }
236
237     fn is_by_ref(&self) -> bool {
238         true
239     }
240
241     fn to_expr_kind(self) -> Expr {
242         LvalueExpr
243     }
244 }
245
246 impl KindOps for Expr {
247     fn post_store<'blk, 'tcx>(&self,
248                               bcx: Block<'blk, 'tcx>,
249                               val: ValueRef,
250                               ty: Ty<'tcx>)
251                               -> Block<'blk, 'tcx> {
252         match *self {
253             LvalueExpr => Lvalue.post_store(bcx, val, ty),
254             RvalueExpr(ref r) => r.post_store(bcx, val, ty),
255         }
256     }
257
258     fn is_by_ref(&self) -> bool {
259         match *self {
260             LvalueExpr => Lvalue.is_by_ref(),
261             RvalueExpr(ref r) => r.is_by_ref()
262         }
263     }
264
265     fn to_expr_kind(self) -> Expr {
266         self
267     }
268 }
269
270 impl<'tcx> Datum<'tcx, Rvalue> {
271     /// Schedules a cleanup for this datum in the given scope. That means that this datum is no
272     /// longer an rvalue datum; hence, this function consumes the datum and returns the contained
273     /// ValueRef.
274     pub fn add_clean<'a>(self,
275                          fcx: &FunctionContext<'a, 'tcx>,
276                          scope: cleanup::ScopeId)
277                          -> ValueRef {
278         add_rvalue_clean(self.kind.mode, fcx, scope, self.val, self.ty);
279         self.val
280     }
281
282     /// Returns an lvalue datum (that is, a by ref datum with cleanup scheduled). If `self` is not
283     /// already an lvalue, cleanup will be scheduled in the temporary scope for `expr_id`.
284     pub fn to_lvalue_datum_in_scope<'blk>(self,
285                                           bcx: Block<'blk, 'tcx>,
286                                           name: &str,
287                                           scope: cleanup::ScopeId)
288                                           -> DatumBlock<'blk, 'tcx, Lvalue> {
289         let fcx = bcx.fcx;
290
291         match self.kind.mode {
292             ByRef => {
293                 add_rvalue_clean(ByRef, fcx, scope, self.val, self.ty);
294                 DatumBlock::new(bcx, Datum::new(self.val, self.ty, Lvalue))
295             }
296
297             ByValue => {
298                 lvalue_scratch_datum(
299                     bcx, self.ty, name, false, scope, self,
300                     |this, bcx, llval| this.store_to(bcx, llval))
301             }
302         }
303     }
304
305     pub fn to_ref_datum<'blk>(self, bcx: Block<'blk, 'tcx>)
306                               -> DatumBlock<'blk, 'tcx, Rvalue> {
307         let mut bcx = bcx;
308         match self.kind.mode {
309             ByRef => DatumBlock::new(bcx, self),
310             ByValue => {
311                 let scratch = rvalue_scratch_datum(bcx, self.ty, "to_ref");
312                 bcx = self.store_to(bcx, scratch.val);
313                 DatumBlock::new(bcx, scratch)
314             }
315         }
316     }
317
318     pub fn to_appropriate_datum<'blk>(self, bcx: Block<'blk, 'tcx>)
319                                       -> DatumBlock<'blk, 'tcx, Rvalue> {
320         match self.appropriate_rvalue_mode(bcx.ccx()) {
321             ByRef => {
322                 self.to_ref_datum(bcx)
323             }
324             ByValue => {
325                 match self.kind.mode {
326                     ByValue => DatumBlock::new(bcx, self),
327                     ByRef => {
328                         let llval = load_ty(bcx, self.val, self.ty);
329                         DatumBlock::new(bcx, Datum::new(llval, self.ty, Rvalue::new(ByValue)))
330                     }
331                 }
332             }
333         }
334     }
335 }
336
337 /**
338  * Methods suitable for "expr" datums that could be either lvalues or
339  * rvalues. These include coercions into lvalues/rvalues but also a number
340  * of more general operations. (Some of those operations could be moved to
341  * the more general `impl<K> Datum<K>`, but it's convenient to have them
342  * here since we can `match self.kind` rather than having to implement
343  * generic methods in `KindOps`.)
344  */
345 impl<'tcx> Datum<'tcx, Expr> {
346     fn match_kind<R>(self,
347                      if_lvalue: |Datum<'tcx, Lvalue>| -> R,
348                      if_rvalue: |Datum<'tcx, Rvalue>| -> R)
349                      -> R {
350         let Datum { val, ty, kind } = self;
351         match kind {
352             LvalueExpr => if_lvalue(Datum::new(val, ty, Lvalue)),
353             RvalueExpr(r) => if_rvalue(Datum::new(val, ty, r)),
354         }
355     }
356
357     /// Asserts that this datum *is* an lvalue and returns it.
358     #[allow(dead_code)] // potentially useful
359     pub fn assert_lvalue(self, bcx: Block) -> Datum<'tcx, Lvalue> {
360         self.match_kind(
361             |d| d,
362             |_| bcx.sess().bug("assert_lvalue given rvalue"))
363     }
364
365     /// Asserts that this datum *is* an lvalue and returns it.
366     pub fn assert_rvalue(self, bcx: Block) -> Datum<'tcx, Rvalue> {
367         self.match_kind(
368             |_| bcx.sess().bug("assert_rvalue given lvalue"),
369             |r| r)
370     }
371
372     pub fn store_to_dest<'blk>(self,
373                                bcx: Block<'blk, 'tcx>,
374                                dest: expr::Dest,
375                                expr_id: ast::NodeId)
376                                -> Block<'blk, 'tcx> {
377         match dest {
378             expr::Ignore => {
379                 self.add_clean_if_rvalue(bcx, expr_id);
380                 bcx
381             }
382             expr::SaveIn(addr) => {
383                 self.store_to(bcx, addr)
384             }
385         }
386     }
387
388     /// Arranges cleanup for `self` if it is an rvalue. Use when you are done working with a value
389     /// that may need drop.
390     pub fn add_clean_if_rvalue<'blk>(self,
391                                      bcx: Block<'blk, 'tcx>,
392                                      expr_id: ast::NodeId) {
393         self.match_kind(
394             |_| { /* Nothing to do, cleanup already arranged */ },
395             |r| {
396                 let scope = cleanup::temporary_scope(bcx.tcx(), expr_id);
397                 r.add_clean(bcx.fcx, scope);
398             })
399     }
400
401     /// Ensures that `self` will get cleaned up, if it is not an lvalue already.
402     pub fn clean<'blk>(self,
403                        bcx: Block<'blk, 'tcx>,
404                        name: &'static str,
405                        expr_id: ast::NodeId)
406                        -> Block<'blk, 'tcx> {
407         self.to_lvalue_datum(bcx, name, expr_id).bcx
408     }
409
410     pub fn to_lvalue_datum<'blk>(self,
411                                  bcx: Block<'blk, 'tcx>,
412                                  name: &str,
413                                  expr_id: ast::NodeId)
414                                  -> DatumBlock<'blk, 'tcx, Lvalue> {
415         debug!("to_lvalue_datum self: {}", self.to_string(bcx.ccx()));
416
417         assert!(ty::lltype_is_sized(bcx.tcx(), self.ty),
418                 "Trying to convert unsized value to lval");
419         self.match_kind(
420             |l| DatumBlock::new(bcx, l),
421             |r| {
422                 let scope = cleanup::temporary_scope(bcx.tcx(), expr_id);
423                 r.to_lvalue_datum_in_scope(bcx, name, scope)
424             })
425     }
426
427     /// Ensures that we have an rvalue datum (that is, a datum with no cleanup scheduled).
428     pub fn to_rvalue_datum<'blk>(self,
429                                  bcx: Block<'blk, 'tcx>,
430                                  name: &'static str)
431                                  -> DatumBlock<'blk, 'tcx, Rvalue> {
432         self.match_kind(
433             |l| {
434                 let mut bcx = bcx;
435                 match l.appropriate_rvalue_mode(bcx.ccx()) {
436                     ByRef => {
437                         let scratch = rvalue_scratch_datum(bcx, l.ty, name);
438                         bcx = l.store_to(bcx, scratch.val);
439                         DatumBlock::new(bcx, scratch)
440                     }
441                     ByValue => {
442                         let v = load_ty(bcx, l.val, l.ty);
443                         bcx = l.kind.post_store(bcx, l.val, l.ty);
444                         DatumBlock::new(bcx, Datum::new(v, l.ty, Rvalue::new(ByValue)))
445                     }
446                 }
447             },
448             |r| DatumBlock::new(bcx, r))
449     }
450
451 }
452
453 /**
454  * Methods suitable only for lvalues. These include the various
455  * operations to extract components out of compound data structures,
456  * such as extracting the field from a struct or a particular element
457  * from an array.
458  */
459 impl<'tcx> Datum<'tcx, Lvalue> {
460     /// Converts a datum into a by-ref value. The datum type must be one which is always passed by
461     /// reference.
462     pub fn to_llref(self) -> ValueRef {
463         self.val
464     }
465
466     // Extracts a component of a compound data structure (e.g., a field from a
467     // struct). Note that if self is an opened, unsized type then the returned
468     // datum may also be unsized _without the size information_. It is the
469     // callers responsibility to package the result in some way to make a valid
470     // datum in that case (e.g., by making a fat pointer or opened pair).
471     pub fn get_element<'blk>(&self, bcx: Block<'blk, 'tcx>, ty: Ty<'tcx>,
472                              gep: |ValueRef| -> ValueRef)
473                              -> Datum<'tcx, Lvalue> {
474         let val = match self.ty.sty {
475             _ if ty::type_is_sized(bcx.tcx(), self.ty) => gep(self.val),
476             ty::ty_open(_) => {
477                 let base = Load(bcx, expr::get_dataptr(bcx, self.val));
478                 gep(base)
479             }
480             _ => bcx.tcx().sess.bug(
481                 format!("Unexpected unsized type in get_element: {}",
482                         bcx.ty_to_string(self.ty)).as_slice())
483         };
484         Datum {
485             val: val,
486             kind: Lvalue,
487             ty: ty,
488         }
489     }
490
491     pub fn get_vec_base_and_len(&self, bcx: Block) -> (ValueRef, ValueRef) {
492         //! Converts a vector into the slice pair.
493
494         tvec::get_base_and_len(bcx, self.val, self.ty)
495     }
496 }
497
498 /**
499  * Generic methods applicable to any sort of datum.
500  */
501 impl<'tcx, K: KindOps + fmt::Show> Datum<'tcx, K> {
502     pub fn new(val: ValueRef, ty: Ty<'tcx>, kind: K) -> Datum<'tcx, K> {
503         Datum { val: val, ty: ty, kind: kind }
504     }
505
506     pub fn to_expr_datum(self) -> Datum<'tcx, Expr> {
507         let Datum { val, ty, kind } = self;
508         Datum { val: val, ty: ty, kind: kind.to_expr_kind() }
509     }
510
511     /// Moves or copies this value into a new home, as appropriate depending on the type of the
512     /// datum. This method consumes the datum, since it would be incorrect to go on using the datum
513     /// if the value represented is affine (and hence the value is moved).
514     pub fn store_to<'blk>(self,
515                           bcx: Block<'blk, 'tcx>,
516                           dst: ValueRef)
517                           -> Block<'blk, 'tcx> {
518         self.shallow_copy_raw(bcx, dst);
519
520         self.kind.post_store(bcx, self.val, self.ty)
521     }
522
523     /// Helper function that performs a shallow copy of this value into `dst`, which should be a
524     /// pointer to a memory location suitable for `self.ty`. `dst` should contain uninitialized
525     /// memory (either newly allocated, zeroed, or dropped).
526     ///
527     /// This function is private to datums because it leaves memory in an unstable state, where the
528     /// source value has been copied but not zeroed. Public methods are `store_to` (if you no
529     /// longer need the source value) or `shallow_copy` (if you wish the source value to remain
530     /// valid).
531     fn shallow_copy_raw<'blk>(&self,
532                               bcx: Block<'blk, 'tcx>,
533                               dst: ValueRef)
534                               -> Block<'blk, 'tcx> {
535         let _icx = push_ctxt("copy_to_no_check");
536
537         if type_is_zero_size(bcx.ccx(), self.ty) {
538             return bcx;
539         }
540
541         if self.kind.is_by_ref() {
542             memcpy_ty(bcx, dst, self.val, self.ty);
543         } else {
544             store_ty(bcx, self.val, dst, self.ty);
545         }
546
547         return bcx;
548     }
549
550     /// Copies the value into a new location. This function always preserves the existing datum as
551     /// a valid value. Therefore, it does not consume `self` and, also, cannot be applied to affine
552     /// values (since they must never be duplicated).
553     pub fn shallow_copy<'blk>(&self,
554                               bcx: Block<'blk, 'tcx>,
555                               dst: ValueRef)
556                               -> Block<'blk, 'tcx> {
557         assert!(!ty::type_moves_by_default(bcx.tcx(), self.ty));
558         self.shallow_copy_raw(bcx, dst)
559     }
560
561     #[allow(dead_code)] // useful for debugging
562     pub fn to_string<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> String {
563         format!("Datum({}, {}, {})",
564                 ccx.tn().val_to_string(self.val),
565                 ty_to_string(ccx.tcx(), self.ty),
566                 self.kind)
567     }
568
569     //! See the `appropriate_rvalue_mode()` function
570     pub fn appropriate_rvalue_mode<'a>(&self, ccx: &CrateContext<'a, 'tcx>)
571                                        -> RvalueMode {
572         appropriate_rvalue_mode(ccx, self.ty)
573     }
574
575     /// Converts `self` into a by-value `ValueRef`. Consumes this datum (i.e., absolves you of
576     /// responsibility to cleanup the value). For this to work, the value must be something
577     /// scalar-ish (like an int or a pointer) which (1) does not require drop glue and (2) is
578     /// naturally passed around by value, and not by reference.
579     pub fn to_llscalarish<'blk>(self, bcx: Block<'blk, 'tcx>) -> ValueRef {
580         assert!(!ty::type_needs_drop(bcx.tcx(), self.ty));
581         assert!(self.appropriate_rvalue_mode(bcx.ccx()) == ByValue);
582         if self.kind.is_by_ref() {
583             load_ty(bcx, self.val, self.ty)
584         } else {
585             self.val
586         }
587     }
588
589     pub fn to_llbool<'blk>(self, bcx: Block<'blk, 'tcx>) -> ValueRef {
590         assert!(ty::type_is_bool(self.ty))
591         self.to_llscalarish(bcx)
592     }
593 }
594
595 impl<'blk, 'tcx, K> DatumBlock<'blk, 'tcx, K> {
596     pub fn new(bcx: Block<'blk, 'tcx>, datum: Datum<'tcx, K>)
597                -> DatumBlock<'blk, 'tcx, K> {
598         DatumBlock { bcx: bcx, datum: datum }
599     }
600 }
601
602 impl<'blk, 'tcx, K: KindOps + fmt::Show> DatumBlock<'blk, 'tcx, K> {
603     pub fn to_expr_datumblock(self) -> DatumBlock<'blk, 'tcx, Expr> {
604         DatumBlock::new(self.bcx, self.datum.to_expr_datum())
605     }
606 }
607
608 impl<'blk, 'tcx> DatumBlock<'blk, 'tcx, Expr> {
609     pub fn store_to_dest(self,
610                          dest: expr::Dest,
611                          expr_id: ast::NodeId) -> Block<'blk, 'tcx> {
612         let DatumBlock { bcx, datum } = self;
613         datum.store_to_dest(bcx, dest, expr_id)
614     }
615
616     pub fn to_llbool(self) -> Result<'blk, 'tcx> {
617         let DatumBlock { datum, bcx } = self;
618         Result::new(bcx, datum.to_llbool(bcx))
619     }
620 }