]>
Commit | Line | Data |
---|---|---|
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 | ||
85aaf69f SL |
11 | //! ## The Datum module |
12 | //! | |
13 | //! A `Datum` encapsulates the result of evaluating a Rust expression. It | |
14 | //! contains a `ValueRef` indicating the result, a `Ty` describing | |
15 | //! the Rust type, but also a *kind*. The kind indicates whether the datum | |
16 | //! has cleanup scheduled (lvalue) or not (rvalue) and -- in the case of | |
17 | //! rvalues -- whether or not the value is "by ref" or "by value". | |
18 | //! | |
19 | //! The datum API is designed to try and help you avoid memory errors like | |
20 | //! forgetting to arrange cleanup or duplicating a value. The type of the | |
21 | //! datum incorporates the kind, and thus reflects whether it has cleanup | |
22 | //! scheduled: | |
23 | //! | |
24 | //! - `Datum<Lvalue>` -- by ref, cleanup scheduled | |
25 | //! - `Datum<Rvalue>` -- by value or by ref, no cleanup scheduled | |
26 | //! - `Datum<Expr>` -- either `Datum<Lvalue>` or `Datum<Rvalue>` | |
27 | //! | |
28 | //! Rvalue and expr datums are noncopyable, and most of the methods on | |
29 | //! datums consume the datum itself (with some notable exceptions). This | |
30 | //! reflects the fact that datums may represent affine values which ought | |
31 | //! to be consumed exactly once, and if you were to try to (for example) | |
32 | //! store an affine value multiple times, you would be duplicating it, | |
33 | //! which would certainly be a bug. | |
34 | //! | |
35 | //! Some of the datum methods, however, are designed to work only on | |
36 | //! copyable values such as ints or pointers. Those methods may borrow the | |
37 | //! datum (`&self`) rather than consume it, but they always include | |
38 | //! assertions on the type of the value represented to check that this | |
39 | //! makes sense. An example is `shallow_copy()`, which duplicates | |
40 | //! a datum value. | |
41 | //! | |
42 | //! Translating an expression always yields a `Datum<Expr>` result, but | |
43 | //! the methods `to_[lr]value_datum()` can be used to coerce a | |
44 | //! `Datum<Expr>` into a `Datum<Lvalue>` or `Datum<Rvalue>` as | |
45 | //! needed. Coercing to an lvalue is fairly common, and generally occurs | |
46 | //! whenever it is necessary to inspect a value and pull out its | |
47 | //! subcomponents (for example, a match, or indexing expression). Coercing | |
48 | //! to an rvalue is more unusual; it occurs when moving values from place | |
49 | //! to place, such as in an assignment expression or parameter passing. | |
50 | //! | |
51 | //! ### Lvalues in detail | |
52 | //! | |
53 | //! An lvalue datum is one for which cleanup has been scheduled. Lvalue | |
54 | //! datums are always located in memory, and thus the `ValueRef` for an | |
55 | //! LLVM value is always a pointer to the actual Rust value. This means | |
56 | //! that if the Datum has a Rust type of `int`, then the LLVM type of the | |
57 | //! `ValueRef` will be `int*` (pointer to int). | |
58 | //! | |
59 | //! Because lvalues already have cleanups scheduled, the memory must be | |
60 | //! zeroed to prevent the cleanup from taking place (presuming that the | |
61 | //! Rust type needs drop in the first place, otherwise it doesn't | |
62 | //! matter). The Datum code automatically performs this zeroing when the | |
63 | //! value is stored to a new location, for example. | |
64 | //! | |
65 | //! Lvalues usually result from evaluating lvalue expressions. For | |
66 | //! example, evaluating a local variable `x` yields an lvalue, as does a | |
67 | //! reference to a field like `x.f` or an index `x[i]`. | |
68 | //! | |
69 | //! Lvalue datums can also arise by *converting* an rvalue into an lvalue. | |
70 | //! This is done with the `to_lvalue_datum` method defined on | |
71 | //! `Datum<Expr>`. Basically this method just schedules cleanup if the | |
72 | //! datum is an rvalue, possibly storing the value into a stack slot first | |
73 | //! if needed. Converting rvalues into lvalues occurs in constructs like | |
74 | //! `&foo()` or `match foo() { ref x => ... }`, where the user is | |
75 | //! implicitly requesting a temporary. | |
76 | //! | |
85aaf69f SL |
77 | //! ### Rvalues in detail |
78 | //! | |
79 | //! Rvalues datums are values with no cleanup scheduled. One must be | |
80 | //! careful with rvalue datums to ensure that cleanup is properly | |
81 | //! arranged, usually by converting to an lvalue datum or by invoking the | |
82 | //! `add_clean` method. | |
83 | //! | |
84 | //! ### Scratch datums | |
85 | //! | |
86 | //! Sometimes you need some temporary scratch space. The functions | |
87 | //! `[lr]value_scratch_datum()` can be used to get temporary stack | |
88 | //! space. As their name suggests, they yield lvalues and rvalues | |
89 | //! respectively. That is, the slot from `lvalue_scratch_datum` will have | |
90 | //! cleanup arranged, and the slot from `rvalue_scratch_datum` does not. | |
1a4d82fc JJ |
91 | |
92 | pub use self::Expr::*; | |
93 | pub use self::RvalueMode::*; | |
94 | ||
95 | use llvm::ValueRef; | |
c1a9b12d | 96 | use trans::adt; |
1a4d82fc | 97 | use trans::base::*; |
c1a9b12d | 98 | use trans::build::{Load, Store}; |
1a4d82fc JJ |
99 | use trans::common::*; |
100 | use trans::cleanup; | |
c1a9b12d | 101 | use trans::cleanup::{CleanupMethods, DropHintDatum, DropHintMethods}; |
1a4d82fc JJ |
102 | use trans::expr; |
103 | use trans::tvec; | |
104 | use trans::type_of; | |
c1a9b12d | 105 | use middle::ty::Ty; |
1a4d82fc JJ |
106 | |
107 | use std::fmt; | |
108 | use syntax::ast; | |
109 | use syntax::codemap::DUMMY_SP; | |
110 | ||
111 | /// A `Datum` encapsulates the result of evaluating an expression. It | |
112 | /// describes where the value is stored, what Rust type the value has, | |
113 | /// whether it is addressed by reference, and so forth. Please refer | |
c34b1796 | 114 | /// the section on datums in `README.md` for more details. |
c1a9b12d | 115 | #[derive(Clone, Copy, Debug)] |
1a4d82fc JJ |
116 | pub struct Datum<'tcx, K> { |
117 | /// The llvm value. This is either a pointer to the Rust value or | |
118 | /// the value itself, depending on `kind` below. | |
119 | pub val: ValueRef, | |
120 | ||
121 | /// The rust type of the value. | |
122 | pub ty: Ty<'tcx>, | |
123 | ||
124 | /// Indicates whether this is by-ref or by-value. | |
125 | pub kind: K, | |
126 | } | |
127 | ||
128 | pub struct DatumBlock<'blk, 'tcx: 'blk, K> { | |
129 | pub bcx: Block<'blk, 'tcx>, | |
130 | pub datum: Datum<'tcx, K>, | |
131 | } | |
132 | ||
85aaf69f | 133 | #[derive(Debug)] |
1a4d82fc JJ |
134 | pub enum Expr { |
135 | /// a fresh value that was produced and which has no cleanup yet | |
136 | /// because it has not yet "landed" into its permanent home | |
137 | RvalueExpr(Rvalue), | |
138 | ||
139 | /// `val` is a pointer into memory for which a cleanup is scheduled | |
140 | /// (and thus has type *T). If you move out of an Lvalue, you must | |
141 | /// zero out the memory (FIXME #5016). | |
c1a9b12d | 142 | LvalueExpr(Lvalue), |
1a4d82fc JJ |
143 | } |
144 | ||
c1a9b12d SL |
145 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] |
146 | pub enum DropFlagInfo { | |
147 | DontZeroJustUse(ast::NodeId), | |
148 | ZeroAndMaintain(ast::NodeId), | |
149 | None, | |
150 | } | |
151 | ||
152 | impl DropFlagInfo { | |
153 | pub fn must_zero(&self) -> bool { | |
154 | match *self { | |
155 | DropFlagInfo::DontZeroJustUse(..) => false, | |
156 | DropFlagInfo::ZeroAndMaintain(..) => true, | |
157 | DropFlagInfo::None => true, | |
158 | } | |
159 | } | |
160 | ||
161 | pub fn hint_datum<'blk, 'tcx>(&self, bcx: Block<'blk, 'tcx>) | |
162 | -> Option<DropHintDatum<'tcx>> { | |
163 | let id = match *self { | |
164 | DropFlagInfo::None => return None, | |
165 | DropFlagInfo::DontZeroJustUse(id) | | |
166 | DropFlagInfo::ZeroAndMaintain(id) => id, | |
167 | }; | |
168 | ||
169 | let hints = bcx.fcx.lldropflag_hints.borrow(); | |
170 | let retval = hints.hint_datum(id); | |
171 | assert!(retval.is_some(), "An id (={}) means must have a hint", id); | |
172 | retval | |
173 | } | |
174 | } | |
175 | ||
176 | // FIXME: having Lvalue be `Copy` is a bit of a footgun, since clients | |
177 | // may not realize that subparts of an Lvalue can have a subset of | |
178 | // drop-flags associated with them, while this as written will just | |
179 | // memcpy the drop_flag_info. But, it is an easier way to get `_match` | |
180 | // off the ground to just let this be `Copy` for now. | |
181 | #[derive(Copy, Clone, Debug)] | |
182 | pub struct Lvalue { | |
183 | pub source: &'static str, | |
184 | pub drop_flag_info: DropFlagInfo | |
185 | } | |
1a4d82fc | 186 | |
85aaf69f | 187 | #[derive(Debug)] |
1a4d82fc JJ |
188 | pub struct Rvalue { |
189 | pub mode: RvalueMode | |
190 | } | |
191 | ||
c1a9b12d SL |
192 | /// Classifies what action we should take when a value is moved away |
193 | /// with respect to its drop-flag. | |
194 | /// | |
195 | /// Long term there will be no need for this classification: all flags | |
196 | /// (which will be stored on the stack frame) will have the same | |
197 | /// interpretation and maintenance code associated with them. | |
198 | #[derive(Copy, Clone, Debug)] | |
199 | pub enum HintKind { | |
200 | /// When the value is moved, set the drop-flag to "dropped" | |
201 | /// (i.e. "zero the flag", even when the specific representation | |
202 | /// is not literally 0) and when it is reinitialized, set the | |
203 | /// drop-flag back to "initialized". | |
204 | ZeroAndMaintain, | |
205 | ||
206 | /// When the value is moved, do not set the drop-flag to "dropped" | |
207 | /// However, continue to read the drop-flag in deciding whether to | |
208 | /// drop. (In essence, the path/fragment in question will never | |
209 | /// need to be dropped at the points where it is moved away by | |
210 | /// this code, but we are defending against the scenario where | |
211 | /// some *other* code could move away (or drop) the value and thus | |
212 | /// zero-the-flag, which is why we will still read from it. | |
213 | DontZeroJustUse, | |
214 | } | |
215 | ||
216 | impl Lvalue { // Constructors for various Lvalues. | |
217 | pub fn new<'blk, 'tcx>(source: &'static str) -> Lvalue { | |
218 | debug!("Lvalue at {} no drop flag info", source); | |
219 | Lvalue { source: source, drop_flag_info: DropFlagInfo::None } | |
220 | } | |
221 | ||
222 | pub fn new_dropflag_hint(source: &'static str) -> Lvalue { | |
223 | debug!("Lvalue at {} is drop flag hint", source); | |
224 | Lvalue { source: source, drop_flag_info: DropFlagInfo::None } | |
225 | } | |
226 | ||
227 | pub fn new_with_hint<'blk, 'tcx>(source: &'static str, | |
228 | bcx: Block<'blk, 'tcx>, | |
229 | id: ast::NodeId, | |
230 | k: HintKind) -> Lvalue { | |
231 | let (opt_id, info) = { | |
232 | let hint_available = Lvalue::has_dropflag_hint(bcx, id) && | |
233 | bcx.tcx().sess.nonzeroing_move_hints(); | |
234 | let info = match k { | |
235 | HintKind::ZeroAndMaintain if hint_available => | |
236 | DropFlagInfo::ZeroAndMaintain(id), | |
237 | HintKind::DontZeroJustUse if hint_available => | |
238 | DropFlagInfo::DontZeroJustUse(id), | |
239 | _ => | |
240 | DropFlagInfo::None, | |
241 | }; | |
242 | (Some(id), info) | |
243 | }; | |
244 | debug!("Lvalue at {}, id: {:?} info: {:?}", source, opt_id, info); | |
245 | Lvalue { source: source, drop_flag_info: info } | |
246 | } | |
247 | } // end Lvalue constructor methods. | |
248 | ||
249 | impl Lvalue { | |
250 | fn has_dropflag_hint<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, | |
251 | id: ast::NodeId) -> bool { | |
252 | let hints = bcx.fcx.lldropflag_hints.borrow(); | |
253 | hints.has_hint(id) | |
254 | } | |
255 | pub fn dropflag_hint<'blk, 'tcx>(&self, bcx: Block<'blk, 'tcx>) | |
256 | -> Option<DropHintDatum<'tcx>> { | |
257 | self.drop_flag_info.hint_datum(bcx) | |
258 | } | |
259 | } | |
260 | ||
1a4d82fc JJ |
261 | impl Rvalue { |
262 | pub fn new(m: RvalueMode) -> Rvalue { | |
263 | Rvalue { mode: m } | |
264 | } | |
265 | } | |
266 | ||
267 | // Make Datum linear for more type safety. | |
268 | impl Drop for Rvalue { | |
269 | fn drop(&mut self) { } | |
270 | } | |
271 | ||
c34b1796 | 272 | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] |
1a4d82fc JJ |
273 | pub enum RvalueMode { |
274 | /// `val` is a pointer to the actual value (and thus has type *T) | |
275 | ByRef, | |
276 | ||
277 | /// `val` is the actual value (*only used for immediates* like ints, ptrs) | |
278 | ByValue, | |
279 | } | |
280 | ||
281 | pub fn immediate_rvalue<'tcx>(val: ValueRef, ty: Ty<'tcx>) -> Datum<'tcx, Rvalue> { | |
282 | return Datum::new(val, ty, Rvalue::new(ByValue)); | |
283 | } | |
284 | ||
285 | pub fn immediate_rvalue_bcx<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, | |
286 | val: ValueRef, | |
287 | ty: Ty<'tcx>) | |
288 | -> DatumBlock<'blk, 'tcx, Rvalue> { | |
289 | return DatumBlock::new(bcx, immediate_rvalue(val, ty)) | |
290 | } | |
291 | ||
292 | ||
293 | /// Allocates temporary space on the stack using alloca() and returns a by-ref Datum pointing to | |
294 | /// it. The memory will be dropped upon exit from `scope`. The callback `populate` should | |
c34b1796 | 295 | /// initialize the memory. |
1a4d82fc JJ |
296 | pub fn lvalue_scratch_datum<'blk, 'tcx, A, F>(bcx: Block<'blk, 'tcx>, |
297 | ty: Ty<'tcx>, | |
298 | name: &str, | |
1a4d82fc JJ |
299 | scope: cleanup::ScopeId, |
300 | arg: A, | |
301 | populate: F) | |
302 | -> DatumBlock<'blk, 'tcx, Lvalue> where | |
303 | F: FnOnce(A, Block<'blk, 'tcx>, ValueRef) -> Block<'blk, 'tcx>, | |
304 | { | |
c34b1796 AL |
305 | let llty = type_of::type_of(bcx.ccx(), ty); |
306 | let scratch = alloca(bcx, llty, name); | |
1a4d82fc JJ |
307 | |
308 | // Subtle. Populate the scratch memory *before* scheduling cleanup. | |
309 | let bcx = populate(arg, bcx, scratch); | |
310 | bcx.fcx.schedule_lifetime_end(scope, scratch); | |
c1a9b12d | 311 | bcx.fcx.schedule_drop_mem(scope, scratch, ty, None); |
1a4d82fc | 312 | |
c1a9b12d | 313 | DatumBlock::new(bcx, Datum::new(scratch, ty, Lvalue::new("datum::lvalue_scratch_datum"))) |
1a4d82fc JJ |
314 | } |
315 | ||
316 | /// Allocates temporary space on the stack using alloca() and returns a by-ref Datum pointing to | |
317 | /// it. If `zero` is true, the space will be zeroed when it is allocated; this is normally not | |
318 | /// necessary, but in the case of automatic rooting in match statements it is possible to have | |
319 | /// temporaries that may not get initialized if a certain arm is not taken, so we must zero them. | |
320 | /// You must arrange any cleanups etc yourself! | |
321 | pub fn rvalue_scratch_datum<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, | |
322 | ty: Ty<'tcx>, | |
323 | name: &str) | |
324 | -> Datum<'tcx, Rvalue> { | |
325 | let llty = type_of::type_of(bcx.ccx(), ty); | |
326 | let scratch = alloca(bcx, llty, name); | |
327 | Datum::new(scratch, ty, Rvalue::new(ByRef)) | |
328 | } | |
329 | ||
330 | /// Indicates the "appropriate" mode for this value, which is either by ref or by value, depending | |
331 | /// on whether type is immediate or not. | |
332 | pub fn appropriate_rvalue_mode<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, | |
333 | ty: Ty<'tcx>) -> RvalueMode { | |
334 | if type_is_immediate(ccx, ty) { | |
335 | ByValue | |
336 | } else { | |
337 | ByRef | |
338 | } | |
339 | } | |
340 | ||
341 | fn add_rvalue_clean<'a, 'tcx>(mode: RvalueMode, | |
342 | fcx: &FunctionContext<'a, 'tcx>, | |
343 | scope: cleanup::ScopeId, | |
344 | val: ValueRef, | |
345 | ty: Ty<'tcx>) { | |
346 | match mode { | |
347 | ByValue => { fcx.schedule_drop_immediate(scope, val, ty); } | |
348 | ByRef => { | |
349 | fcx.schedule_lifetime_end(scope, val); | |
c1a9b12d | 350 | fcx.schedule_drop_mem(scope, val, ty, None); |
1a4d82fc JJ |
351 | } |
352 | } | |
353 | } | |
354 | ||
355 | pub trait KindOps { | |
356 | ||
357 | /// Take appropriate action after the value in `datum` has been | |
358 | /// stored to a new location. | |
359 | fn post_store<'blk, 'tcx>(&self, | |
360 | bcx: Block<'blk, 'tcx>, | |
361 | val: ValueRef, | |
362 | ty: Ty<'tcx>) | |
363 | -> Block<'blk, 'tcx>; | |
364 | ||
365 | /// True if this mode is a reference mode, meaning that the datum's | |
366 | /// val field is a pointer to the actual value | |
367 | fn is_by_ref(&self) -> bool; | |
368 | ||
369 | /// Converts to an Expr kind | |
370 | fn to_expr_kind(self) -> Expr; | |
371 | ||
372 | } | |
373 | ||
374 | impl KindOps for Rvalue { | |
375 | fn post_store<'blk, 'tcx>(&self, | |
376 | bcx: Block<'blk, 'tcx>, | |
377 | _val: ValueRef, | |
378 | _ty: Ty<'tcx>) | |
379 | -> Block<'blk, 'tcx> { | |
380 | // No cleanup is scheduled for an rvalue, so we don't have | |
381 | // to do anything after a move to cancel or duplicate it. | |
85aaf69f SL |
382 | if self.is_by_ref() { |
383 | call_lifetime_end(bcx, _val); | |
384 | } | |
1a4d82fc JJ |
385 | bcx |
386 | } | |
387 | ||
388 | fn is_by_ref(&self) -> bool { | |
389 | self.mode == ByRef | |
390 | } | |
391 | ||
392 | fn to_expr_kind(self) -> Expr { | |
393 | RvalueExpr(self) | |
394 | } | |
395 | } | |
396 | ||
397 | impl KindOps for Lvalue { | |
398 | /// If an lvalue is moved, we must zero out the memory in which it resides so as to cancel | |
399 | /// cleanup. If an @T lvalue is copied, we must increment the reference count. | |
400 | fn post_store<'blk, 'tcx>(&self, | |
401 | bcx: Block<'blk, 'tcx>, | |
402 | val: ValueRef, | |
403 | ty: Ty<'tcx>) | |
404 | -> Block<'blk, 'tcx> { | |
c34b1796 AL |
405 | let _icx = push_ctxt("<Lvalue as KindOps>::post_store"); |
406 | if bcx.fcx.type_needs_drop(ty) { | |
c1a9b12d SL |
407 | // cancel cleanup of affine values: |
408 | // 1. if it has drop-hint, mark as moved; then code | |
409 | // aware of drop-hint won't bother calling the | |
410 | // drop-glue itself. | |
411 | if let Some(hint_datum) = self.drop_flag_info.hint_datum(bcx) { | |
412 | let moved_hint_byte = adt::DTOR_MOVED_HINT as usize; | |
413 | let hint_llval = hint_datum.to_value().value(); | |
414 | Store(bcx, C_u8(bcx.fcx.ccx, moved_hint_byte), hint_llval); | |
415 | } | |
416 | // 2. if the drop info says its necessary, drop-fill the memory. | |
417 | if self.drop_flag_info.must_zero() { | |
418 | let () = drop_done_fill_mem(bcx, val, ty); | |
419 | } | |
1a4d82fc JJ |
420 | bcx |
421 | } else { | |
c1a9b12d SL |
422 | // FIXME (#5016) would be nice to assert this, but we have |
423 | // to allow for e.g. DontZeroJustUse flags, for now. | |
424 | // | |
425 | // (The dropflag hint construction should be taking | |
426 | // !type_needs_drop into account; earlier analysis phases | |
427 | // may not have all the info they need to include such | |
428 | // information properly, I think; in particular the | |
429 | // fragments analysis works on a non-monomorphized view of | |
430 | // the code.) | |
431 | // | |
432 | // assert_eq!(self.drop_flag_info, DropFlagInfo::None); | |
1a4d82fc JJ |
433 | bcx |
434 | } | |
435 | } | |
436 | ||
437 | fn is_by_ref(&self) -> bool { | |
438 | true | |
439 | } | |
440 | ||
441 | fn to_expr_kind(self) -> Expr { | |
c1a9b12d | 442 | LvalueExpr(self) |
1a4d82fc JJ |
443 | } |
444 | } | |
445 | ||
446 | impl KindOps for Expr { | |
447 | fn post_store<'blk, 'tcx>(&self, | |
448 | bcx: Block<'blk, 'tcx>, | |
449 | val: ValueRef, | |
450 | ty: Ty<'tcx>) | |
451 | -> Block<'blk, 'tcx> { | |
452 | match *self { | |
c1a9b12d | 453 | LvalueExpr(ref l) => l.post_store(bcx, val, ty), |
1a4d82fc JJ |
454 | RvalueExpr(ref r) => r.post_store(bcx, val, ty), |
455 | } | |
456 | } | |
457 | ||
458 | fn is_by_ref(&self) -> bool { | |
459 | match *self { | |
c1a9b12d | 460 | LvalueExpr(ref l) => l.is_by_ref(), |
1a4d82fc JJ |
461 | RvalueExpr(ref r) => r.is_by_ref() |
462 | } | |
463 | } | |
464 | ||
465 | fn to_expr_kind(self) -> Expr { | |
466 | self | |
467 | } | |
468 | } | |
469 | ||
470 | impl<'tcx> Datum<'tcx, Rvalue> { | |
471 | /// Schedules a cleanup for this datum in the given scope. That means that this datum is no | |
472 | /// longer an rvalue datum; hence, this function consumes the datum and returns the contained | |
473 | /// ValueRef. | |
474 | pub fn add_clean<'a>(self, | |
475 | fcx: &FunctionContext<'a, 'tcx>, | |
476 | scope: cleanup::ScopeId) | |
477 | -> ValueRef { | |
478 | add_rvalue_clean(self.kind.mode, fcx, scope, self.val, self.ty); | |
479 | self.val | |
480 | } | |
481 | ||
482 | /// Returns an lvalue datum (that is, a by ref datum with cleanup scheduled). If `self` is not | |
483 | /// already an lvalue, cleanup will be scheduled in the temporary scope for `expr_id`. | |
484 | pub fn to_lvalue_datum_in_scope<'blk>(self, | |
485 | bcx: Block<'blk, 'tcx>, | |
486 | name: &str, | |
487 | scope: cleanup::ScopeId) | |
488 | -> DatumBlock<'blk, 'tcx, Lvalue> { | |
489 | let fcx = bcx.fcx; | |
490 | ||
491 | match self.kind.mode { | |
492 | ByRef => { | |
493 | add_rvalue_clean(ByRef, fcx, scope, self.val, self.ty); | |
c1a9b12d SL |
494 | DatumBlock::new(bcx, Datum::new( |
495 | self.val, | |
496 | self.ty, | |
497 | Lvalue::new("datum::to_lvalue_datum_in_scope"))) | |
1a4d82fc JJ |
498 | } |
499 | ||
500 | ByValue => { | |
501 | lvalue_scratch_datum( | |
c34b1796 | 502 | bcx, self.ty, name, scope, self, |
1a4d82fc JJ |
503 | |this, bcx, llval| this.store_to(bcx, llval)) |
504 | } | |
505 | } | |
506 | } | |
507 | ||
508 | pub fn to_ref_datum<'blk>(self, bcx: Block<'blk, 'tcx>) | |
509 | -> DatumBlock<'blk, 'tcx, Rvalue> { | |
510 | let mut bcx = bcx; | |
511 | match self.kind.mode { | |
512 | ByRef => DatumBlock::new(bcx, self), | |
513 | ByValue => { | |
514 | let scratch = rvalue_scratch_datum(bcx, self.ty, "to_ref"); | |
515 | bcx = self.store_to(bcx, scratch.val); | |
516 | DatumBlock::new(bcx, scratch) | |
517 | } | |
518 | } | |
519 | } | |
520 | ||
521 | pub fn to_appropriate_datum<'blk>(self, bcx: Block<'blk, 'tcx>) | |
522 | -> DatumBlock<'blk, 'tcx, Rvalue> { | |
523 | match self.appropriate_rvalue_mode(bcx.ccx()) { | |
524 | ByRef => { | |
525 | self.to_ref_datum(bcx) | |
526 | } | |
527 | ByValue => { | |
528 | match self.kind.mode { | |
529 | ByValue => DatumBlock::new(bcx, self), | |
530 | ByRef => { | |
531 | let llval = load_ty(bcx, self.val, self.ty); | |
85aaf69f | 532 | call_lifetime_end(bcx, self.val); |
1a4d82fc JJ |
533 | DatumBlock::new(bcx, Datum::new(llval, self.ty, Rvalue::new(ByValue))) |
534 | } | |
535 | } | |
536 | } | |
537 | } | |
538 | } | |
539 | } | |
540 | ||
541 | /// Methods suitable for "expr" datums that could be either lvalues or | |
542 | /// rvalues. These include coercions into lvalues/rvalues but also a number | |
543 | /// of more general operations. (Some of those operations could be moved to | |
544 | /// the more general `impl<K> Datum<K>`, but it's convenient to have them | |
545 | /// here since we can `match self.kind` rather than having to implement | |
546 | /// generic methods in `KindOps`.) | |
547 | impl<'tcx> Datum<'tcx, Expr> { | |
548 | fn match_kind<R, F, G>(self, if_lvalue: F, if_rvalue: G) -> R where | |
549 | F: FnOnce(Datum<'tcx, Lvalue>) -> R, | |
550 | G: FnOnce(Datum<'tcx, Rvalue>) -> R, | |
551 | { | |
552 | let Datum { val, ty, kind } = self; | |
553 | match kind { | |
c1a9b12d | 554 | LvalueExpr(l) => if_lvalue(Datum::new(val, ty, l)), |
1a4d82fc JJ |
555 | RvalueExpr(r) => if_rvalue(Datum::new(val, ty, r)), |
556 | } | |
557 | } | |
558 | ||
559 | /// Asserts that this datum *is* an lvalue and returns it. | |
560 | #[allow(dead_code)] // potentially useful | |
561 | pub fn assert_lvalue(self, bcx: Block) -> Datum<'tcx, Lvalue> { | |
562 | self.match_kind( | |
563 | |d| d, | |
564 | |_| bcx.sess().bug("assert_lvalue given rvalue")) | |
565 | } | |
566 | ||
567 | pub fn store_to_dest<'blk>(self, | |
568 | bcx: Block<'blk, 'tcx>, | |
569 | dest: expr::Dest, | |
570 | expr_id: ast::NodeId) | |
571 | -> Block<'blk, 'tcx> { | |
572 | match dest { | |
573 | expr::Ignore => { | |
574 | self.add_clean_if_rvalue(bcx, expr_id); | |
575 | bcx | |
576 | } | |
577 | expr::SaveIn(addr) => { | |
578 | self.store_to(bcx, addr) | |
579 | } | |
580 | } | |
581 | } | |
582 | ||
583 | /// Arranges cleanup for `self` if it is an rvalue. Use when you are done working with a value | |
584 | /// that may need drop. | |
585 | pub fn add_clean_if_rvalue<'blk>(self, | |
586 | bcx: Block<'blk, 'tcx>, | |
587 | expr_id: ast::NodeId) { | |
588 | self.match_kind( | |
589 | |_| { /* Nothing to do, cleanup already arranged */ }, | |
590 | |r| { | |
591 | let scope = cleanup::temporary_scope(bcx.tcx(), expr_id); | |
592 | r.add_clean(bcx.fcx, scope); | |
593 | }) | |
594 | } | |
595 | ||
1a4d82fc JJ |
596 | pub fn to_lvalue_datum<'blk>(self, |
597 | bcx: Block<'blk, 'tcx>, | |
598 | name: &str, | |
599 | expr_id: ast::NodeId) | |
600 | -> DatumBlock<'blk, 'tcx, Lvalue> { | |
601 | debug!("to_lvalue_datum self: {}", self.to_string(bcx.ccx())); | |
602 | ||
1a4d82fc JJ |
603 | self.match_kind( |
604 | |l| DatumBlock::new(bcx, l), | |
605 | |r| { | |
606 | let scope = cleanup::temporary_scope(bcx.tcx(), expr_id); | |
607 | r.to_lvalue_datum_in_scope(bcx, name, scope) | |
608 | }) | |
609 | } | |
610 | ||
611 | /// Ensures that we have an rvalue datum (that is, a datum with no cleanup scheduled). | |
612 | pub fn to_rvalue_datum<'blk>(self, | |
613 | bcx: Block<'blk, 'tcx>, | |
614 | name: &'static str) | |
615 | -> DatumBlock<'blk, 'tcx, Rvalue> { | |
616 | self.match_kind( | |
617 | |l| { | |
618 | let mut bcx = bcx; | |
619 | match l.appropriate_rvalue_mode(bcx.ccx()) { | |
620 | ByRef => { | |
621 | let scratch = rvalue_scratch_datum(bcx, l.ty, name); | |
622 | bcx = l.store_to(bcx, scratch.val); | |
623 | DatumBlock::new(bcx, scratch) | |
624 | } | |
625 | ByValue => { | |
626 | let v = load_ty(bcx, l.val, l.ty); | |
627 | bcx = l.kind.post_store(bcx, l.val, l.ty); | |
628 | DatumBlock::new(bcx, Datum::new(v, l.ty, Rvalue::new(ByValue))) | |
629 | } | |
630 | } | |
631 | }, | |
632 | |r| DatumBlock::new(bcx, r)) | |
633 | } | |
634 | ||
635 | } | |
636 | ||
637 | /// Methods suitable only for lvalues. These include the various | |
638 | /// operations to extract components out of compound data structures, | |
639 | /// such as extracting the field from a struct or a particular element | |
640 | /// from an array. | |
641 | impl<'tcx> Datum<'tcx, Lvalue> { | |
642 | /// Converts a datum into a by-ref value. The datum type must be one which is always passed by | |
643 | /// reference. | |
644 | pub fn to_llref(self) -> ValueRef { | |
645 | self.val | |
646 | } | |
647 | ||
648 | // Extracts a component of a compound data structure (e.g., a field from a | |
649 | // struct). Note that if self is an opened, unsized type then the returned | |
650 | // datum may also be unsized _without the size information_. It is the | |
651 | // callers responsibility to package the result in some way to make a valid | |
652 | // datum in that case (e.g., by making a fat pointer or opened pair). | |
653 | pub fn get_element<'blk, F>(&self, bcx: Block<'blk, 'tcx>, ty: Ty<'tcx>, | |
654 | gep: F) | |
655 | -> Datum<'tcx, Lvalue> where | |
656 | F: FnOnce(ValueRef) -> ValueRef, | |
657 | { | |
c34b1796 AL |
658 | let val = if type_is_sized(bcx.tcx(), self.ty) { |
659 | gep(self.val) | |
660 | } else { | |
661 | gep(Load(bcx, expr::get_dataptr(bcx, self.val))) | |
1a4d82fc JJ |
662 | }; |
663 | Datum { | |
664 | val: val, | |
c1a9b12d | 665 | kind: Lvalue::new("Datum::get_element"), |
1a4d82fc JJ |
666 | ty: ty, |
667 | } | |
668 | } | |
669 | ||
c34b1796 AL |
670 | pub fn get_vec_base_and_len<'blk>(&self, bcx: Block<'blk, 'tcx>) |
671 | -> (ValueRef, ValueRef) { | |
1a4d82fc JJ |
672 | //! Converts a vector into the slice pair. |
673 | ||
674 | tvec::get_base_and_len(bcx, self.val, self.ty) | |
675 | } | |
676 | } | |
677 | ||
678 | /// Generic methods applicable to any sort of datum. | |
85aaf69f | 679 | impl<'tcx, K: KindOps + fmt::Debug> Datum<'tcx, K> { |
1a4d82fc JJ |
680 | pub fn new(val: ValueRef, ty: Ty<'tcx>, kind: K) -> Datum<'tcx, K> { |
681 | Datum { val: val, ty: ty, kind: kind } | |
682 | } | |
683 | ||
684 | pub fn to_expr_datum(self) -> Datum<'tcx, Expr> { | |
685 | let Datum { val, ty, kind } = self; | |
686 | Datum { val: val, ty: ty, kind: kind.to_expr_kind() } | |
687 | } | |
688 | ||
689 | /// Moves or copies this value into a new home, as appropriate depending on the type of the | |
690 | /// datum. This method consumes the datum, since it would be incorrect to go on using the datum | |
691 | /// if the value represented is affine (and hence the value is moved). | |
692 | pub fn store_to<'blk>(self, | |
693 | bcx: Block<'blk, 'tcx>, | |
694 | dst: ValueRef) | |
695 | -> Block<'blk, 'tcx> { | |
696 | self.shallow_copy_raw(bcx, dst); | |
697 | ||
698 | self.kind.post_store(bcx, self.val, self.ty) | |
699 | } | |
700 | ||
701 | /// Helper function that performs a shallow copy of this value into `dst`, which should be a | |
702 | /// pointer to a memory location suitable for `self.ty`. `dst` should contain uninitialized | |
703 | /// memory (either newly allocated, zeroed, or dropped). | |
704 | /// | |
705 | /// This function is private to datums because it leaves memory in an unstable state, where the | |
706 | /// source value has been copied but not zeroed. Public methods are `store_to` (if you no | |
707 | /// longer need the source value) or `shallow_copy` (if you wish the source value to remain | |
708 | /// valid). | |
709 | fn shallow_copy_raw<'blk>(&self, | |
710 | bcx: Block<'blk, 'tcx>, | |
711 | dst: ValueRef) | |
712 | -> Block<'blk, 'tcx> { | |
713 | let _icx = push_ctxt("copy_to_no_check"); | |
714 | ||
715 | if type_is_zero_size(bcx.ccx(), self.ty) { | |
716 | return bcx; | |
717 | } | |
718 | ||
719 | if self.kind.is_by_ref() { | |
720 | memcpy_ty(bcx, dst, self.val, self.ty); | |
721 | } else { | |
722 | store_ty(bcx, self.val, dst, self.ty); | |
723 | } | |
724 | ||
725 | return bcx; | |
726 | } | |
727 | ||
728 | /// Copies the value into a new location. This function always preserves the existing datum as | |
729 | /// a valid value. Therefore, it does not consume `self` and, also, cannot be applied to affine | |
730 | /// values (since they must never be duplicated). | |
731 | pub fn shallow_copy<'blk>(&self, | |
732 | bcx: Block<'blk, 'tcx>, | |
733 | dst: ValueRef) | |
734 | -> Block<'blk, 'tcx> { | |
735 | /*! | |
736 | * Copies the value into a new location. This function always | |
737 | * preserves the existing datum as a valid value. Therefore, | |
738 | * it does not consume `self` and, also, cannot be applied to | |
739 | * affine values (since they must never be duplicated). | |
740 | */ | |
741 | ||
c1a9b12d SL |
742 | assert!(!self.ty |
743 | .moves_by_default(&bcx.tcx().empty_parameter_environment(), DUMMY_SP)); | |
1a4d82fc JJ |
744 | self.shallow_copy_raw(bcx, dst) |
745 | } | |
746 | ||
747 | #[allow(dead_code)] // useful for debugging | |
748 | pub fn to_string<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> String { | |
62682a34 | 749 | format!("Datum({}, {:?}, {:?})", |
1a4d82fc | 750 | ccx.tn().val_to_string(self.val), |
62682a34 | 751 | self.ty, |
1a4d82fc JJ |
752 | self.kind) |
753 | } | |
754 | ||
755 | /// See the `appropriate_rvalue_mode()` function | |
756 | pub fn appropriate_rvalue_mode<'a>(&self, ccx: &CrateContext<'a, 'tcx>) | |
757 | -> RvalueMode { | |
758 | appropriate_rvalue_mode(ccx, self.ty) | |
759 | } | |
760 | ||
761 | /// Converts `self` into a by-value `ValueRef`. Consumes this datum (i.e., absolves you of | |
762 | /// responsibility to cleanup the value). For this to work, the value must be something | |
763 | /// scalar-ish (like an int or a pointer) which (1) does not require drop glue and (2) is | |
764 | /// naturally passed around by value, and not by reference. | |
765 | pub fn to_llscalarish<'blk>(self, bcx: Block<'blk, 'tcx>) -> ValueRef { | |
c34b1796 | 766 | assert!(!bcx.fcx.type_needs_drop(self.ty)); |
1a4d82fc JJ |
767 | assert!(self.appropriate_rvalue_mode(bcx.ccx()) == ByValue); |
768 | if self.kind.is_by_ref() { | |
769 | load_ty(bcx, self.val, self.ty) | |
770 | } else { | |
771 | self.val | |
772 | } | |
773 | } | |
774 | ||
775 | pub fn to_llbool<'blk>(self, bcx: Block<'blk, 'tcx>) -> ValueRef { | |
c1a9b12d | 776 | assert!(self.ty.is_bool()); |
1a4d82fc JJ |
777 | self.to_llscalarish(bcx) |
778 | } | |
779 | } | |
780 | ||
781 | impl<'blk, 'tcx, K> DatumBlock<'blk, 'tcx, K> { | |
782 | pub fn new(bcx: Block<'blk, 'tcx>, datum: Datum<'tcx, K>) | |
783 | -> DatumBlock<'blk, 'tcx, K> { | |
784 | DatumBlock { bcx: bcx, datum: datum } | |
785 | } | |
786 | } | |
787 | ||
85aaf69f | 788 | impl<'blk, 'tcx, K: KindOps + fmt::Debug> DatumBlock<'blk, 'tcx, K> { |
1a4d82fc JJ |
789 | pub fn to_expr_datumblock(self) -> DatumBlock<'blk, 'tcx, Expr> { |
790 | DatumBlock::new(self.bcx, self.datum.to_expr_datum()) | |
791 | } | |
792 | } | |
793 | ||
794 | impl<'blk, 'tcx> DatumBlock<'blk, 'tcx, Expr> { | |
795 | pub fn store_to_dest(self, | |
796 | dest: expr::Dest, | |
797 | expr_id: ast::NodeId) -> Block<'blk, 'tcx> { | |
798 | let DatumBlock { bcx, datum } = self; | |
799 | datum.store_to_dest(bcx, dest, expr_id) | |
800 | } | |
801 | ||
802 | pub fn to_llbool(self) -> Result<'blk, 'tcx> { | |
803 | let DatumBlock { datum, bcx } = self; | |
804 | Result::new(bcx, datum.to_llbool(bcx)) | |
805 | } | |
806 | } |