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