]>
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 | ||
291 | ||
292 | /// Allocates temporary space on the stack using alloca() and returns a by-ref Datum pointing to | |
293 | /// it. The memory will be dropped upon exit from `scope`. The callback `populate` should | |
c34b1796 | 294 | /// initialize the memory. |
1a4d82fc JJ |
295 | pub fn lvalue_scratch_datum<'blk, 'tcx, A, F>(bcx: Block<'blk, 'tcx>, |
296 | ty: Ty<'tcx>, | |
297 | name: &str, | |
1a4d82fc JJ |
298 | scope: cleanup::ScopeId, |
299 | arg: A, | |
300 | populate: F) | |
301 | -> DatumBlock<'blk, 'tcx, Lvalue> where | |
302 | F: FnOnce(A, Block<'blk, 'tcx>, ValueRef) -> Block<'blk, 'tcx>, | |
303 | { | |
e9174d1e | 304 | let scratch = alloc_ty(bcx, ty, name); |
1a4d82fc JJ |
305 | |
306 | // Subtle. Populate the scratch memory *before* scheduling cleanup. | |
307 | let bcx = populate(arg, bcx, scratch); | |
c1a9b12d | 308 | bcx.fcx.schedule_drop_mem(scope, scratch, ty, None); |
1a4d82fc | 309 | |
c1a9b12d | 310 | DatumBlock::new(bcx, Datum::new(scratch, ty, Lvalue::new("datum::lvalue_scratch_datum"))) |
1a4d82fc JJ |
311 | } |
312 | ||
313 | /// Allocates temporary space on the stack using alloca() and returns a by-ref Datum pointing to | |
314 | /// it. If `zero` is true, the space will be zeroed when it is allocated; this is normally not | |
315 | /// necessary, but in the case of automatic rooting in match statements it is possible to have | |
316 | /// temporaries that may not get initialized if a certain arm is not taken, so we must zero them. | |
317 | /// You must arrange any cleanups etc yourself! | |
318 | pub fn rvalue_scratch_datum<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, | |
319 | ty: Ty<'tcx>, | |
320 | name: &str) | |
321 | -> Datum<'tcx, Rvalue> { | |
e9174d1e SL |
322 | let scratch = alloc_ty(bcx, ty, name); |
323 | call_lifetime_start(bcx, scratch); | |
1a4d82fc JJ |
324 | Datum::new(scratch, ty, Rvalue::new(ByRef)) |
325 | } | |
326 | ||
327 | /// Indicates the "appropriate" mode for this value, which is either by ref or by value, depending | |
328 | /// on whether type is immediate or not. | |
329 | pub fn appropriate_rvalue_mode<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, | |
330 | ty: Ty<'tcx>) -> RvalueMode { | |
331 | if type_is_immediate(ccx, ty) { | |
332 | ByValue | |
333 | } else { | |
334 | ByRef | |
335 | } | |
336 | } | |
337 | ||
338 | fn add_rvalue_clean<'a, 'tcx>(mode: RvalueMode, | |
339 | fcx: &FunctionContext<'a, 'tcx>, | |
340 | scope: cleanup::ScopeId, | |
341 | val: ValueRef, | |
342 | ty: Ty<'tcx>) { | |
343 | match mode { | |
344 | ByValue => { fcx.schedule_drop_immediate(scope, val, ty); } | |
345 | ByRef => { | |
346 | fcx.schedule_lifetime_end(scope, val); | |
c1a9b12d | 347 | fcx.schedule_drop_mem(scope, val, ty, None); |
1a4d82fc JJ |
348 | } |
349 | } | |
350 | } | |
351 | ||
352 | pub trait KindOps { | |
353 | ||
354 | /// Take appropriate action after the value in `datum` has been | |
355 | /// stored to a new location. | |
356 | fn post_store<'blk, 'tcx>(&self, | |
357 | bcx: Block<'blk, 'tcx>, | |
358 | val: ValueRef, | |
359 | ty: Ty<'tcx>) | |
360 | -> Block<'blk, 'tcx>; | |
361 | ||
362 | /// True if this mode is a reference mode, meaning that the datum's | |
363 | /// val field is a pointer to the actual value | |
364 | fn is_by_ref(&self) -> bool; | |
365 | ||
366 | /// Converts to an Expr kind | |
367 | fn to_expr_kind(self) -> Expr; | |
368 | ||
369 | } | |
370 | ||
371 | impl KindOps for Rvalue { | |
372 | fn post_store<'blk, 'tcx>(&self, | |
373 | bcx: Block<'blk, 'tcx>, | |
374 | _val: ValueRef, | |
375 | _ty: Ty<'tcx>) | |
376 | -> Block<'blk, 'tcx> { | |
377 | // No cleanup is scheduled for an rvalue, so we don't have | |
378 | // to do anything after a move to cancel or duplicate it. | |
85aaf69f SL |
379 | if self.is_by_ref() { |
380 | call_lifetime_end(bcx, _val); | |
381 | } | |
1a4d82fc JJ |
382 | bcx |
383 | } | |
384 | ||
385 | fn is_by_ref(&self) -> bool { | |
386 | self.mode == ByRef | |
387 | } | |
388 | ||
389 | fn to_expr_kind(self) -> Expr { | |
390 | RvalueExpr(self) | |
391 | } | |
392 | } | |
393 | ||
394 | impl KindOps for Lvalue { | |
395 | /// If an lvalue is moved, we must zero out the memory in which it resides so as to cancel | |
396 | /// cleanup. If an @T lvalue is copied, we must increment the reference count. | |
397 | fn post_store<'blk, 'tcx>(&self, | |
398 | bcx: Block<'blk, 'tcx>, | |
399 | val: ValueRef, | |
400 | ty: Ty<'tcx>) | |
401 | -> Block<'blk, 'tcx> { | |
c34b1796 AL |
402 | let _icx = push_ctxt("<Lvalue as KindOps>::post_store"); |
403 | if bcx.fcx.type_needs_drop(ty) { | |
c1a9b12d SL |
404 | // cancel cleanup of affine values: |
405 | // 1. if it has drop-hint, mark as moved; then code | |
406 | // aware of drop-hint won't bother calling the | |
407 | // drop-glue itself. | |
408 | if let Some(hint_datum) = self.drop_flag_info.hint_datum(bcx) { | |
e9174d1e | 409 | let moved_hint_byte = adt::DTOR_MOVED_HINT; |
c1a9b12d SL |
410 | let hint_llval = hint_datum.to_value().value(); |
411 | Store(bcx, C_u8(bcx.fcx.ccx, moved_hint_byte), hint_llval); | |
412 | } | |
413 | // 2. if the drop info says its necessary, drop-fill the memory. | |
414 | if self.drop_flag_info.must_zero() { | |
415 | let () = drop_done_fill_mem(bcx, val, ty); | |
416 | } | |
1a4d82fc JJ |
417 | bcx |
418 | } else { | |
c1a9b12d SL |
419 | // FIXME (#5016) would be nice to assert this, but we have |
420 | // to allow for e.g. DontZeroJustUse flags, for now. | |
421 | // | |
422 | // (The dropflag hint construction should be taking | |
423 | // !type_needs_drop into account; earlier analysis phases | |
424 | // may not have all the info they need to include such | |
425 | // information properly, I think; in particular the | |
426 | // fragments analysis works on a non-monomorphized view of | |
427 | // the code.) | |
428 | // | |
429 | // assert_eq!(self.drop_flag_info, DropFlagInfo::None); | |
1a4d82fc JJ |
430 | bcx |
431 | } | |
432 | } | |
433 | ||
434 | fn is_by_ref(&self) -> bool { | |
435 | true | |
436 | } | |
437 | ||
438 | fn to_expr_kind(self) -> Expr { | |
c1a9b12d | 439 | LvalueExpr(self) |
1a4d82fc JJ |
440 | } |
441 | } | |
442 | ||
443 | impl KindOps for Expr { | |
444 | fn post_store<'blk, 'tcx>(&self, | |
445 | bcx: Block<'blk, 'tcx>, | |
446 | val: ValueRef, | |
447 | ty: Ty<'tcx>) | |
448 | -> Block<'blk, 'tcx> { | |
449 | match *self { | |
c1a9b12d | 450 | LvalueExpr(ref l) => l.post_store(bcx, val, ty), |
1a4d82fc JJ |
451 | RvalueExpr(ref r) => r.post_store(bcx, val, ty), |
452 | } | |
453 | } | |
454 | ||
455 | fn is_by_ref(&self) -> bool { | |
456 | match *self { | |
c1a9b12d | 457 | LvalueExpr(ref l) => l.is_by_ref(), |
1a4d82fc JJ |
458 | RvalueExpr(ref r) => r.is_by_ref() |
459 | } | |
460 | } | |
461 | ||
462 | fn to_expr_kind(self) -> Expr { | |
463 | self | |
464 | } | |
465 | } | |
466 | ||
467 | impl<'tcx> Datum<'tcx, Rvalue> { | |
468 | /// Schedules a cleanup for this datum in the given scope. That means that this datum is no | |
469 | /// longer an rvalue datum; hence, this function consumes the datum and returns the contained | |
470 | /// ValueRef. | |
471 | pub fn add_clean<'a>(self, | |
472 | fcx: &FunctionContext<'a, 'tcx>, | |
473 | scope: cleanup::ScopeId) | |
474 | -> ValueRef { | |
475 | add_rvalue_clean(self.kind.mode, fcx, scope, self.val, self.ty); | |
476 | self.val | |
477 | } | |
478 | ||
479 | /// Returns an lvalue datum (that is, a by ref datum with cleanup scheduled). If `self` is not | |
480 | /// already an lvalue, cleanup will be scheduled in the temporary scope for `expr_id`. | |
481 | pub fn to_lvalue_datum_in_scope<'blk>(self, | |
482 | bcx: Block<'blk, 'tcx>, | |
483 | name: &str, | |
484 | scope: cleanup::ScopeId) | |
485 | -> DatumBlock<'blk, 'tcx, Lvalue> { | |
486 | let fcx = bcx.fcx; | |
487 | ||
488 | match self.kind.mode { | |
489 | ByRef => { | |
490 | add_rvalue_clean(ByRef, fcx, scope, self.val, self.ty); | |
c1a9b12d SL |
491 | DatumBlock::new(bcx, Datum::new( |
492 | self.val, | |
493 | self.ty, | |
494 | Lvalue::new("datum::to_lvalue_datum_in_scope"))) | |
1a4d82fc JJ |
495 | } |
496 | ||
497 | ByValue => { | |
498 | lvalue_scratch_datum( | |
c34b1796 | 499 | bcx, self.ty, name, scope, self, |
e9174d1e SL |
500 | |this, bcx, llval| { |
501 | call_lifetime_start(bcx, llval); | |
502 | let bcx = this.store_to(bcx, llval); | |
503 | bcx.fcx.schedule_lifetime_end(scope, llval); | |
504 | bcx | |
505 | }) | |
1a4d82fc JJ |
506 | } |
507 | } | |
508 | } | |
509 | ||
510 | pub fn to_ref_datum<'blk>(self, bcx: Block<'blk, 'tcx>) | |
511 | -> DatumBlock<'blk, 'tcx, Rvalue> { | |
512 | let mut bcx = bcx; | |
513 | match self.kind.mode { | |
514 | ByRef => DatumBlock::new(bcx, self), | |
515 | ByValue => { | |
516 | let scratch = rvalue_scratch_datum(bcx, self.ty, "to_ref"); | |
517 | bcx = self.store_to(bcx, scratch.val); | |
518 | DatumBlock::new(bcx, scratch) | |
519 | } | |
520 | } | |
521 | } | |
522 | ||
523 | pub fn to_appropriate_datum<'blk>(self, bcx: Block<'blk, 'tcx>) | |
524 | -> DatumBlock<'blk, 'tcx, Rvalue> { | |
525 | match self.appropriate_rvalue_mode(bcx.ccx()) { | |
526 | ByRef => { | |
527 | self.to_ref_datum(bcx) | |
528 | } | |
529 | ByValue => { | |
530 | match self.kind.mode { | |
531 | ByValue => DatumBlock::new(bcx, self), | |
532 | ByRef => { | |
533 | let llval = load_ty(bcx, self.val, self.ty); | |
85aaf69f | 534 | call_lifetime_end(bcx, self.val); |
1a4d82fc JJ |
535 | DatumBlock::new(bcx, Datum::new(llval, self.ty, Rvalue::new(ByValue))) |
536 | } | |
537 | } | |
538 | } | |
539 | } | |
540 | } | |
541 | } | |
542 | ||
543 | /// Methods suitable for "expr" datums that could be either lvalues or | |
544 | /// rvalues. These include coercions into lvalues/rvalues but also a number | |
545 | /// of more general operations. (Some of those operations could be moved to | |
546 | /// the more general `impl<K> Datum<K>`, but it's convenient to have them | |
547 | /// here since we can `match self.kind` rather than having to implement | |
548 | /// generic methods in `KindOps`.) | |
549 | impl<'tcx> Datum<'tcx, Expr> { | |
550 | fn match_kind<R, F, G>(self, if_lvalue: F, if_rvalue: G) -> R where | |
551 | F: FnOnce(Datum<'tcx, Lvalue>) -> R, | |
552 | G: FnOnce(Datum<'tcx, Rvalue>) -> R, | |
553 | { | |
554 | let Datum { val, ty, kind } = self; | |
555 | match kind { | |
c1a9b12d | 556 | LvalueExpr(l) => if_lvalue(Datum::new(val, ty, l)), |
1a4d82fc JJ |
557 | RvalueExpr(r) => if_rvalue(Datum::new(val, ty, r)), |
558 | } | |
559 | } | |
560 | ||
561 | /// Asserts that this datum *is* an lvalue and returns it. | |
562 | #[allow(dead_code)] // potentially useful | |
563 | pub fn assert_lvalue(self, bcx: Block) -> Datum<'tcx, Lvalue> { | |
564 | self.match_kind( | |
565 | |d| d, | |
566 | |_| bcx.sess().bug("assert_lvalue given rvalue")) | |
567 | } | |
568 | ||
569 | pub fn store_to_dest<'blk>(self, | |
570 | bcx: Block<'blk, 'tcx>, | |
571 | dest: expr::Dest, | |
572 | expr_id: ast::NodeId) | |
573 | -> Block<'blk, 'tcx> { | |
574 | match dest { | |
575 | expr::Ignore => { | |
576 | self.add_clean_if_rvalue(bcx, expr_id); | |
577 | bcx | |
578 | } | |
579 | expr::SaveIn(addr) => { | |
580 | self.store_to(bcx, addr) | |
581 | } | |
582 | } | |
583 | } | |
584 | ||
585 | /// Arranges cleanup for `self` if it is an rvalue. Use when you are done working with a value | |
586 | /// that may need drop. | |
587 | pub fn add_clean_if_rvalue<'blk>(self, | |
588 | bcx: Block<'blk, 'tcx>, | |
589 | expr_id: ast::NodeId) { | |
590 | self.match_kind( | |
591 | |_| { /* Nothing to do, cleanup already arranged */ }, | |
592 | |r| { | |
593 | let scope = cleanup::temporary_scope(bcx.tcx(), expr_id); | |
594 | r.add_clean(bcx.fcx, scope); | |
595 | }) | |
596 | } | |
597 | ||
1a4d82fc JJ |
598 | pub fn to_lvalue_datum<'blk>(self, |
599 | bcx: Block<'blk, 'tcx>, | |
600 | name: &str, | |
601 | expr_id: ast::NodeId) | |
602 | -> DatumBlock<'blk, 'tcx, Lvalue> { | |
603 | debug!("to_lvalue_datum self: {}", self.to_string(bcx.ccx())); | |
604 | ||
1a4d82fc JJ |
605 | self.match_kind( |
606 | |l| DatumBlock::new(bcx, l), | |
607 | |r| { | |
608 | let scope = cleanup::temporary_scope(bcx.tcx(), expr_id); | |
609 | r.to_lvalue_datum_in_scope(bcx, name, scope) | |
610 | }) | |
611 | } | |
612 | ||
613 | /// Ensures that we have an rvalue datum (that is, a datum with no cleanup scheduled). | |
614 | pub fn to_rvalue_datum<'blk>(self, | |
615 | bcx: Block<'blk, 'tcx>, | |
616 | name: &'static str) | |
617 | -> DatumBlock<'blk, 'tcx, Rvalue> { | |
618 | self.match_kind( | |
619 | |l| { | |
620 | let mut bcx = bcx; | |
621 | match l.appropriate_rvalue_mode(bcx.ccx()) { | |
622 | ByRef => { | |
623 | let scratch = rvalue_scratch_datum(bcx, l.ty, name); | |
624 | bcx = l.store_to(bcx, scratch.val); | |
625 | DatumBlock::new(bcx, scratch) | |
626 | } | |
627 | ByValue => { | |
628 | let v = load_ty(bcx, l.val, l.ty); | |
629 | bcx = l.kind.post_store(bcx, l.val, l.ty); | |
630 | DatumBlock::new(bcx, Datum::new(v, l.ty, Rvalue::new(ByValue))) | |
631 | } | |
632 | } | |
633 | }, | |
634 | |r| DatumBlock::new(bcx, r)) | |
635 | } | |
636 | ||
637 | } | |
638 | ||
639 | /// Methods suitable only for lvalues. These include the various | |
640 | /// operations to extract components out of compound data structures, | |
641 | /// such as extracting the field from a struct or a particular element | |
642 | /// from an array. | |
643 | impl<'tcx> Datum<'tcx, Lvalue> { | |
644 | /// Converts a datum into a by-ref value. The datum type must be one which is always passed by | |
645 | /// reference. | |
646 | pub fn to_llref(self) -> ValueRef { | |
647 | self.val | |
648 | } | |
649 | ||
650 | // Extracts a component of a compound data structure (e.g., a field from a | |
651 | // struct). Note that if self is an opened, unsized type then the returned | |
652 | // datum may also be unsized _without the size information_. It is the | |
653 | // callers responsibility to package the result in some way to make a valid | |
654 | // datum in that case (e.g., by making a fat pointer or opened pair). | |
655 | pub fn get_element<'blk, F>(&self, bcx: Block<'blk, 'tcx>, ty: Ty<'tcx>, | |
656 | gep: F) | |
657 | -> Datum<'tcx, Lvalue> where | |
658 | F: FnOnce(ValueRef) -> ValueRef, | |
659 | { | |
c34b1796 AL |
660 | let val = if type_is_sized(bcx.tcx(), self.ty) { |
661 | gep(self.val) | |
662 | } else { | |
663 | gep(Load(bcx, expr::get_dataptr(bcx, self.val))) | |
1a4d82fc JJ |
664 | }; |
665 | Datum { | |
666 | val: val, | |
c1a9b12d | 667 | kind: Lvalue::new("Datum::get_element"), |
1a4d82fc JJ |
668 | ty: ty, |
669 | } | |
670 | } | |
671 | ||
c34b1796 AL |
672 | pub fn get_vec_base_and_len<'blk>(&self, bcx: Block<'blk, 'tcx>) |
673 | -> (ValueRef, ValueRef) { | |
1a4d82fc JJ |
674 | //! Converts a vector into the slice pair. |
675 | ||
676 | tvec::get_base_and_len(bcx, self.val, self.ty) | |
677 | } | |
678 | } | |
679 | ||
680 | /// Generic methods applicable to any sort of datum. | |
85aaf69f | 681 | impl<'tcx, K: KindOps + fmt::Debug> Datum<'tcx, K> { |
1a4d82fc JJ |
682 | pub fn new(val: ValueRef, ty: Ty<'tcx>, kind: K) -> Datum<'tcx, K> { |
683 | Datum { val: val, ty: ty, kind: kind } | |
684 | } | |
685 | ||
686 | pub fn to_expr_datum(self) -> Datum<'tcx, Expr> { | |
687 | let Datum { val, ty, kind } = self; | |
688 | Datum { val: val, ty: ty, kind: kind.to_expr_kind() } | |
689 | } | |
690 | ||
691 | /// Moves or copies this value into a new home, as appropriate depending on the type of the | |
692 | /// datum. This method consumes the datum, since it would be incorrect to go on using the datum | |
693 | /// if the value represented is affine (and hence the value is moved). | |
694 | pub fn store_to<'blk>(self, | |
695 | bcx: Block<'blk, 'tcx>, | |
696 | dst: ValueRef) | |
697 | -> Block<'blk, 'tcx> { | |
698 | self.shallow_copy_raw(bcx, dst); | |
699 | ||
700 | self.kind.post_store(bcx, self.val, self.ty) | |
701 | } | |
702 | ||
703 | /// Helper function that performs a shallow copy of this value into `dst`, which should be a | |
704 | /// pointer to a memory location suitable for `self.ty`. `dst` should contain uninitialized | |
705 | /// memory (either newly allocated, zeroed, or dropped). | |
706 | /// | |
707 | /// This function is private to datums because it leaves memory in an unstable state, where the | |
708 | /// source value has been copied but not zeroed. Public methods are `store_to` (if you no | |
709 | /// longer need the source value) or `shallow_copy` (if you wish the source value to remain | |
710 | /// valid). | |
711 | fn shallow_copy_raw<'blk>(&self, | |
712 | bcx: Block<'blk, 'tcx>, | |
713 | dst: ValueRef) | |
714 | -> Block<'blk, 'tcx> { | |
715 | let _icx = push_ctxt("copy_to_no_check"); | |
716 | ||
717 | if type_is_zero_size(bcx.ccx(), self.ty) { | |
718 | return bcx; | |
719 | } | |
720 | ||
721 | if self.kind.is_by_ref() { | |
722 | memcpy_ty(bcx, dst, self.val, self.ty); | |
723 | } else { | |
724 | store_ty(bcx, self.val, dst, self.ty); | |
725 | } | |
726 | ||
727 | return bcx; | |
728 | } | |
729 | ||
730 | /// Copies the value into a new location. This function always preserves the existing datum as | |
731 | /// a valid value. Therefore, it does not consume `self` and, also, cannot be applied to affine | |
732 | /// values (since they must never be duplicated). | |
733 | pub fn shallow_copy<'blk>(&self, | |
734 | bcx: Block<'blk, 'tcx>, | |
735 | dst: ValueRef) | |
736 | -> Block<'blk, 'tcx> { | |
737 | /*! | |
738 | * Copies the value into a new location. This function always | |
739 | * preserves the existing datum as a valid value. Therefore, | |
740 | * it does not consume `self` and, also, cannot be applied to | |
741 | * affine values (since they must never be duplicated). | |
742 | */ | |
743 | ||
c1a9b12d SL |
744 | assert!(!self.ty |
745 | .moves_by_default(&bcx.tcx().empty_parameter_environment(), DUMMY_SP)); | |
1a4d82fc JJ |
746 | self.shallow_copy_raw(bcx, dst) |
747 | } | |
748 | ||
749 | #[allow(dead_code)] // useful for debugging | |
750 | pub fn to_string<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> String { | |
62682a34 | 751 | format!("Datum({}, {:?}, {:?})", |
1a4d82fc | 752 | ccx.tn().val_to_string(self.val), |
62682a34 | 753 | self.ty, |
1a4d82fc JJ |
754 | self.kind) |
755 | } | |
756 | ||
757 | /// See the `appropriate_rvalue_mode()` function | |
758 | pub fn appropriate_rvalue_mode<'a>(&self, ccx: &CrateContext<'a, 'tcx>) | |
759 | -> RvalueMode { | |
760 | appropriate_rvalue_mode(ccx, self.ty) | |
761 | } | |
762 | ||
763 | /// Converts `self` into a by-value `ValueRef`. Consumes this datum (i.e., absolves you of | |
764 | /// responsibility to cleanup the value). For this to work, the value must be something | |
765 | /// scalar-ish (like an int or a pointer) which (1) does not require drop glue and (2) is | |
766 | /// naturally passed around by value, and not by reference. | |
767 | pub fn to_llscalarish<'blk>(self, bcx: Block<'blk, 'tcx>) -> ValueRef { | |
c34b1796 | 768 | assert!(!bcx.fcx.type_needs_drop(self.ty)); |
1a4d82fc JJ |
769 | assert!(self.appropriate_rvalue_mode(bcx.ccx()) == ByValue); |
770 | if self.kind.is_by_ref() { | |
771 | load_ty(bcx, self.val, self.ty) | |
772 | } else { | |
773 | self.val | |
774 | } | |
775 | } | |
776 | ||
777 | pub fn to_llbool<'blk>(self, bcx: Block<'blk, 'tcx>) -> ValueRef { | |
c1a9b12d | 778 | assert!(self.ty.is_bool()); |
1a4d82fc JJ |
779 | self.to_llscalarish(bcx) |
780 | } | |
781 | } | |
782 | ||
783 | impl<'blk, 'tcx, K> DatumBlock<'blk, 'tcx, K> { | |
784 | pub fn new(bcx: Block<'blk, 'tcx>, datum: Datum<'tcx, K>) | |
785 | -> DatumBlock<'blk, 'tcx, K> { | |
786 | DatumBlock { bcx: bcx, datum: datum } | |
787 | } | |
788 | } | |
789 | ||
85aaf69f | 790 | impl<'blk, 'tcx, K: KindOps + fmt::Debug> DatumBlock<'blk, 'tcx, K> { |
1a4d82fc JJ |
791 | pub fn to_expr_datumblock(self) -> DatumBlock<'blk, 'tcx, Expr> { |
792 | DatumBlock::new(self.bcx, self.datum.to_expr_datum()) | |
793 | } | |
794 | } | |
795 | ||
796 | impl<'blk, 'tcx> DatumBlock<'blk, 'tcx, Expr> { | |
797 | pub fn store_to_dest(self, | |
798 | dest: expr::Dest, | |
799 | expr_id: ast::NodeId) -> Block<'blk, 'tcx> { | |
800 | let DatumBlock { bcx, datum } = self; | |
801 | datum.store_to_dest(bcx, dest, expr_id) | |
802 | } | |
803 | ||
804 | pub fn to_llbool(self) -> Result<'blk, 'tcx> { | |
805 | let DatumBlock { datum, bcx } = self; | |
806 | Result::new(bcx, datum.to_llbool(bcx)) | |
807 | } | |
808 | } |