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