Skip to content

Commit 823ed27

Browse files
authored
Merge pull request rust-lang#359 from RalfJung/stacked-borrows
update Stacked borrows documentation
2 parents e21202c + a34ac3c commit 823ed27

File tree

1 file changed

+14
-17
lines changed

1 file changed

+14
-17
lines changed

wip/stacked-borrows.md

+14-17
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Changes from to the latest post (2.1) to the paper:
2323
Changes since publication of the paper:
2424

2525
* Items with `SharedReadWrite` permission are not protected even with `FnEntry` retagging.
26+
* Removal of `Untagged`.
2627

2728
[Miri]: https://github.com/solson/miri/
2829
[all-hands]: https://paper.dropbox.com/doc/Topic-Stacked-borrows--AXAkoFfUGViWL_PaSryqKK~hAg-2q57v4UM7cIkxCq9PQc22
@@ -38,19 +39,12 @@ Moreover, there is a per-call-frame `CallId` as well as some global tracking sta
3839
// `nat` is the type of mathematical natural numbers, meaning we don't want to think about overflow.
3940
// NOTE: Miri just uses `NonZeroU64` which, realistically, will not overflow because we only ever increment by 1.
4041
type PtrId = nat;
42+
/// For historic reasons we often call pointer IDs "tags".
43+
type Tag = PtrId;
4144

4245
/// CallId: uniquely identifying a stack frame.
4346
type CallId = nat;
4447

45-
/// Extra per-pointer state (the "tag"), tracking how this pointer was computed
46-
/// ("pointer provenance").
47-
pub enum Tag {
48-
/// References are tagged: we know where they got created.
49-
Tag(PtrId),
50-
/// Raw pointers are not tracked.
51-
Untagged,
52-
}
53-
5448
/// Indicates which permission is granted (by this item to some pointers)
5549
pub enum Permission {
5650
/// Grants unique mutable access.
@@ -78,7 +72,7 @@ pub struct Stack {
7872
/// Used *mostly* as a stack; never empty.
7973
/// Invariants:
8074
/// * Above a `SharedReadOnly` there can only be more `SharedReadOnly`.
81-
/// * Except for `Untagged`, no tag occurs in the stack more than once.
75+
/// * No tag occurs in the stack more than once.
8276
borrows: Vec<Item>,
8377
}
8478

@@ -167,7 +161,7 @@ In other words, the per-stack-frame `CallId` is initialized by `Tracking::new_ca
167161
We say that a `CallId` is *active* if the call stack contains a stack frame with that ID.
168162
In the following, we pretend there exists a function `call_is_active(id)` that can check this.
169163

170-
**Note**: Miri uses a slightly more complex system with a `HashSet<CallId>` tracking the set of active `CallId`; that is just an optimization to avoid having to scan the call stack all the time.
164+
**Note**: Miri uses a slightly more complex system to track the set of active `CallId`; that is just an optimization to avoid having to scan the call stack all the time.
171165

172166
### Preliminaries for items
173167

@@ -217,13 +211,13 @@ Using any item within a block is equivalent to using any other item in that same
217211

218212
When allocating memory, we have to initialize the `Stack` associated with the new locations, and we have to choose a `Tag` for the initial pointer to this memory.
219213

220-
- For heap allocations, the stack of each freshly allocated memory location is `Stack { borrows: vec![(Untagged: SharedReadWrite)] }`, and the initial pointer to that memory has tag `Untagged`.
221-
- For global allocations (`static`, environment and program argument data, ...), we pick a fresh `id` associated with the global, and each time a pointer to the global is created, it gets tagged `Tag(id)`.
222-
The stacks in that memory are initialized with `Stack { borrows: vec![(Tag(id): SharedReadWrite)] }`.
223214
- Stack memory is handled by an environment (which is part of the information carried in a stack frame of the Rust abstract machine) that maps each local variable to a place.
224215
A place is a pointer together with some other data that is not relevant here -- the key point is that a place, just like every other pointer, carries a tag.
225-
When the local variable becomes live and its backing memory gets allocated, we generate a new pointer ID `id` by calling `Tracking::new_ptr` and use `Tag(id)` as tag for the place of this local variable.
226-
We also initialize the stack of all the memory locations in this new memory allocation with `Stack { borrows: vec![(Tag(id): Unique)] }`.
216+
When the local variable becomes live and its backing memory gets allocated, we generate a new pointer ID `id` by calling `Tracking::new_ptr` and use `id` as tag for the place of this local variable.
217+
We also initialize the stack of all the memory locations in this new memory allocation with `Stack { borrows: vec![(id: Unique)] }`.
218+
- For heap allocations, we pick a fresh `id` at allocation time. The stack of each freshly allocated memory location is `Stack { borrows: vec![(id: SharedReadWrite)] }`, and the initial pointer to that memory has tag `id`.
219+
- For global allocations (`static`, environment and program argument data, ...), we pick a fresh `id` associated with the global, and each time a pointer to the global is created, it gets tagged `id`.
220+
The stacks in that memory are initialized with `Stack { borrows: vec![(id: SharedReadWrite)] }`.
227221

228222
### Accessing memory
229223

@@ -333,13 +327,16 @@ location.stack.grant(
333327
When executing `Retag(kind, place)`, we check if `place` holds a reference (`&[mut] _`) or box (`Box<_>`), and if `kind == Raw` we also check each raw pointer (`*[const,mut] _`).
334328
For those we perform the following steps:
335329

336-
1. We compute a fresh tag: `Untagged` for raw pointers, `Tag(Tracking::new_ptr_id())` for everything else.
330+
1. We compute a fresh tag: `Tracking::new_ptr_id()`.
337331
2. We determine if we will want to protect the items we are going to generate:
338332
This is the case only if `kind == FnEntry` and the type of this pointer is a reference (not a box).
339333
3. We perform reborrowing of the memory this pointer points to with the new tag and indicating whether we want protection, treating boxes as `RefKind::Unique { two_phase: false }`.
340334

341335
We do not recurse into fields of structs or other compound types, only "bare" references/... get retagged.
342336

337+
**Note**: Miri offers a flag, `-Zmiri-retag-fields`, that changes this behavior to also recurse into compound types to search for references to retag.
338+
We never recurse through a pointer indirection.
339+
343340
### Deallocating memory
344341

345342
Memory deallocation first acts like a write access through the pointer used for deallocation.

0 commit comments

Comments
 (0)