Skip to content

Commit 4ed1944

Browse files
update documentation
1 parent c3f3205 commit 4ed1944

File tree

2 files changed

+58
-4
lines changed

2 files changed

+58
-4
lines changed

compiler/rustc_middle/src/mir/mod.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,14 @@ pub struct Body<'tcx> {
334334
#[type_visitable(ignore)]
335335
pub function_coverage_info: Option<Box<coverage::FunctionCoverageInfo>>,
336336

337-
/// Coroutine local-upvar map
337+
/// Coroutine local-upvar map, which maps the coroutine captures as fields
338+
/// in coroutine state to the internal MIR locals.
339+
/// This is to help borrow-checker assert lifetime invariance between types of
340+
/// the capture, as appear exterior to the coroutine, and that of locals,
341+
/// intto which the captures are relocated.
342+
/// It also assists diagnostic to re-construct the identity of the captures.
343+
/// MIR interpretation and instrumentation can disregard this information.
344+
/// It carries no operation-semantic significance.
338345
pub local_upvar_map: IndexVec<FieldIdx, Option<Local>>,
339346
}
340347

compiler/rustc_mir_transform/src/coroutine/relocate_upvars.rs

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,57 @@
2929
//!
3030
//! Each place that starts with access into the coroutine structure `_1` is replaced with the fresh local as
3131
//! the base.
32-
//! For instance, `(_1.4 as Some).0` is rewritten into `(_34 as Some).0` when `_34` is the fresh local
33-
//! corresponding to the captured upvar stored in `_1.4`.
32+
//! Suppose we are to lower this coroutine into the MIR.
33+
//! ```ignore (illustrative)
34+
//! let mut captured = None;
35+
//! let _ = #[coroutine] || {
36+
//! yield ();
37+
//! if let Some(inner) = &mut captured {
38+
//! *inner = 42i32; // (*)
39+
//! }
40+
//! };
41+
//! ```
42+
//! `captured` is the only capture, whose mutable borrow is formally alotted to the first field `_1.0: &mut i32`.
43+
//! The highlighted line `(*)` should be lowered, roughly, into MIR `(*_1.0) = const 42i32;`.
44+
//! Now, by application of this pass, we create a new local `_4: &mut i32` and we perform the following
45+
//! code transformation.
46+
//!
47+
//! A new block is constructed to just perform the relocation of this mutable borrow.
48+
//! This block is inserted to the very beginning of the coroutine body control flow,
49+
//! so that this is executed before any proper coroutine code as it transits from `UNRESUME` state to
50+
//! any other state.
51+
//! This "prologue" will look like the following.
52+
//! ```ignore (illustrative)
53+
//! StorageLive(_5);
54+
//! StorageLive(_4);
55+
//! _5 = move (_1.0);
56+
//! _4 = move (_5);
57+
//! StorageDead(_5);
58+
//! ```
59+
//! Note that we also create a trampoline local `_5` of the same type.
60+
//!
61+
//! ### Intricacy around the trampoline local
62+
//! The reason that we need the trampolines is because state transformation and coroutine
63+
//! layout calculation is not aware of potential storage conflict between captures as struct fields
64+
//! and other saved locals.
65+
//! The only guarantee that we can work with is one where any coroutine layout calculator respects
66+
//! the storage conflict constracts between *MIR locals*.
67+
//! It is known that calculators do not consider struct fields, where captures reside, as MIR locals.
68+
//! This is the source of potential memory overlaps.
69+
//! For instance, in a hypothetical situation,
70+
//! - `_1.0` is relocated to `_4`;
71+
//! - `_1.1` is relocated to `_6`;
72+
//! - `_4` and `_6` remains live at one of the first suspension state;
73+
//! - `_4` occupies the same offset of `_1.1` and `_6` occupies the same offset of `_1.0`
74+
//! as decided by some layout calculator;
75+
//! In this scenario, without trampolining, the relocations introduce undefined behaviour.
76+
//!
77+
//! As a proposal for a future design, it is best that coroutine captures receive their own
78+
//! MIR locals, possibly in a form of "function arguments" like `_1` itself.
79+
//! The trampolining transformation already attempts to restore the semantics of MIR locals to
80+
//! these captures and promoting them to "arguments" would make MIR safer to handle.
3481
//!
35-
//! This phase assumes that the initial built MIR respects the nature of captures.
82+
//! One should note that this phase assumes that the initial built MIR respects the nature of captures.
3683
//! For instance, if the upvar `_1.4` is instead a by-ref-mut capture of a value of type `T`,
3784
//! this phase assumes that all access correctly built as operating on the place `(*_1.4)`.
3885
//! Based on the assumption, this phase replaces `_1.4` with a fresh local `_34: &mut T` and

0 commit comments

Comments
 (0)