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