Skip to content

Commit 53b899e

Browse files
committed
address many review comments
1 parent 168249a commit 53b899e

File tree

2 files changed

+37
-24
lines changed

2 files changed

+37
-24
lines changed

wip/memory-interface.md

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,25 @@ This interface is a key part of the Rust Abstract Machine: it lets us separate c
99

1010
The interface shown below is also opinionated in several ways.
1111
It is not intended to be able to support *any imaginable* memory model, but rather start the process of reducing the design space of what we consider a "reasonable" memory model for Rust.
12-
For example, it explicitly acknowledges that pointers are not just integers and that uninitialized memory is special (both are true for C and C++ as well but you have to read the standard very careful, and consult [non-normative defect report responses](http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_260.htm), to see this).
12+
For example, it explicitly acknowledges that pointers are not just integers and that uninitialized memory is special (both are true for C and C++ as well but you have to read the standard very careful, and consult [defect report responses](http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_260.htm), to see this).
1313
Another key property of the interface presented below is that it is *untyped*.
14-
This implies that in Rust, *operations are typed, but memory is not*---a key difference to C and C++ with their type-based strict aliasing rules.
14+
This implies that in Rust, *operations are typed, but memory is not* - a key difference to C and C++ with their type-based strict aliasing rules.
1515
At the same time, the memory model provides a *side-effect free* way to turn pointers into "raw bytes", which is *not* [the direction C++ is moving towards](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2364.pdf), and we might have to revisit this choice later if it turns out to not be workable.
1616

1717
## Pointers
1818

1919
One key question a memory model has to answer is *what is a pointer*.
2020
It might seem like the answer is just "an integer of appropriate size", but [that is not the case][pointers-complicated].
2121
This becomes even more prominent with aliasing models such as [Stacked Borrows].
22-
So the interface will leave it up to the concrete instance to answer this question, and carry `Pointer` as an associated type.
22+
So we leave it up to the memory model to answer this question, and make `Pointer` an associated type.
2323
Practically speaking, `Pointer` will be some representation of an "address", plus [provenance] information.
2424

2525
[provenance]: https://github.com/rust-lang/unsafe-code-guidelines/blob/master/reference/src/glossary.md#pointer-provenance
2626

2727
## Bytes
2828

2929
The unit of communication between the memory model and the rest of the program is a *byte*.
30-
Again the question of "what is a byte" is not as trivial as it might seem; beyond `u8` values we have to represent `Pointer`s and [uninitialized memory][uninit].
30+
Again, the question of "what is a byte" is not as trivial as it might seem; beyond `u8` values we have to represent `Pointer`s and [uninitialized memory][uninit].
3131
We define the `Byte` type (in terms of an arbitrary `Pointer` type) as follows:
3232

3333
```rust
@@ -39,6 +39,7 @@ enum Byte<Pointer> {
3939
/// One byte of a pointer.
4040
PtrFragment {
4141
/// The pointer of which this is a byte.
42+
/// That is, the byte is a fragment of this pointer.
4243
ptr: Pointer,
4344
/// Which byte of the pointer this is.
4445
/// `idx` will always be in `0..PTR_SIZE`.
@@ -47,10 +48,15 @@ enum Byte<Pointer> {
4748
}
4849
```
4950

50-
The purpose of `PtrFragment` is to be able to have a byte-wise representation of a `Pointer`.
51+
The purpose of `PtrFragment` is to enable a byte-wise representation of a `Pointer`.
5152
On a 32-bit system, the sequence of 4 bytes representing `ptr: Pointer` is:
5253
```
53-
[PtrFragment { ptr, idx: 0 }, PtrFragment { ptr, idx: 1 }, PtrFragment { ptr, idx: 2 }, PtrFragment { ptr, idx: 3 }]
54+
[
55+
PtrFragment { ptr, idx: 0 },
56+
PtrFragment { ptr, idx: 1 },
57+
PtrFragment { ptr, idx: 2 },
58+
PtrFragment { ptr, idx: 3 },
59+
]
5460
```
5561

5662
Based on the `PtrToInt` trait (see below), we can turn every initialized `Byte` into an integer in `0..256`:
@@ -73,13 +79,18 @@ impl<Pointer: PtrToInt> Byte<Pointer> {
7379
The Rust memory interface is described by the following (not-yet-complete) trait definition:
7480

7581
```rust
82+
/// All operations are fallible, so they return `Result`. If they fail, that
83+
/// means the program caused UB. What exactly the `UndefinedBehavior` type is
84+
/// does not matter here.
85+
type Result<T=()> = std::result::Result<T, UndefinedBehavior>;
86+
7687
/// *Note*: All memory operations can be non-deterministic, which means that
7788
/// executing the same operation on the same memory can have different results.
7889
/// We also let all operations potentially mutate memory. For example, reads
7990
/// actually do change the current state when considering concurrency or
8091
/// Stacked Borrows.
81-
/// And finally, all operations are fallible (they return `Result`); if they
82-
/// fail, that means the program caused UB.
92+
/// This is pseudo-Rust, so we just use fully owned types everywhere for
93+
/// symmetry and simplicity.
8394
trait Memory {
8495
/// The type of pointer values.
8596
type Pointer: Copy + PtrToInt;
@@ -88,23 +99,23 @@ trait Memory {
8899
const PTR_SIZE: u64;
89100

90101
/// Create a new allocation.
91-
fn allocate(&mut self, size: u64, align: u64) -> Result<Self::Pointer, Error>;
102+
fn allocate(&mut self, size: u64, align: u64) -> Result<Self::Pointer>;
92103

93104
/// Remove an allocation.
94-
fn deallocate(&mut self, ptr: Self::Pointer, size: u64, align: u64) -> Result<(), Error>;
105+
fn deallocate(&mut self, ptr: Self::Pointer, size: u64, align: u64) -> Result;
95106

96107
/// Write some bytes to memory.
97-
fn write(&mut self, ptr: Self::Pointer, bytes: Vec<Byte<Self::Pointer>>) -> Result<(), Error>;
108+
fn write(&mut self, ptr: Self::Pointer, bytes: Vec<Byte<Self::Pointer>>) -> Result;
98109

99110
/// Read some bytes from memory.
100-
fn read(&mut self, ptr: Self::Pointer, len: u64) -> Result<Vec<Byte<Self::Pointer>>, Error>;
111+
fn read(&mut self, ptr: Self::Pointer, len: u64) -> Result<Vec<Byte<Self::Pointer>>>;
101112

102113
/// Offset the given pointer.
103114
fn offset(&mut self, ptr: Self::Pointer, offset: u64, mode: OffsetMode)
104-
-> Result<Self::Pointer, Error>;
115+
-> Result<Self::Pointer>;
105116

106117
/// Cast the given integer to a pointer. (The other direction is handled by `PtrToInt` below.)
107-
fn int_to_ptr(&mut self, int: u64) -> Result<Self::Pointer, Error>;
118+
fn int_to_ptr(&mut self, int: u64) -> Result<Self::Pointer>;
108119
}
109120

110121
/// The `Pointer` type must know how to extract its bytes, *without any access to the `Memory`*.

wip/value-domain.md

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,30 @@ The Rust value domain is described by the following (incomplete) type definition
1717

1818
```rust
1919
enum Value<Pointer> {
20-
/// A mathematical integer.
20+
/// A mathematical integer, used for `i*`/`u*` types.
2121
Int(BigInt),
22-
/// A Boolean value.
22+
/// A Boolean value, used for `bool`.
2323
Bool(bool),
24-
/// A pointer.
24+
/// A pointer value, used for (thin) references and raw pointers.
2525
Ptr(Pointer),
2626
/// An uninitialized value.
2727
Uninit,
28-
/// An n-tuple.
28+
/// An n-tuple, used for arrays, structs, tuples, SIMD vectors.
2929
Tuple(Vec<Self>),
30-
/// A variant of a sum type.
30+
/// A variant of a sum type, used for enums.
3131
Variant {
32-
idx: u64,
32+
idx: BigInt,
3333
data: Box<Self>,
3434
},
35-
/// A "bag of raw bytes".
35+
/// A "bag of raw bytes", used for unions.
3636
RawBag(Vec<Byte<Pointer>>),
3737
/* ... */
3838
}
3939
```
4040

41-
As Rust grows, we might expand this definition. That is okay; all previously defined representation relations are still well-defined when the domain grows, the newly added values will just not be valid for old types as one would expect.
41+
The point of this type is to capture the mathematical concepts that are represented by the data we store in memory.
42+
The definition is likely incomplete, and even if it was complete now, we might expand it as Rust grows.
43+
That is okay; all previously defined representation relations are still well-defined when the domain grows, the newly added values will just not be valid for old types as one would expect.
4244

4345
## Example value relations
4446

@@ -97,10 +99,10 @@ One key use of the value representation is to define a "typed" interface to memo
9799
```rust
98100
trait TypedMemory: Memory {
99101
/// Write a value of the given type to memory.
100-
fn typed_write(&mut self, ptr: Self::Pointer, val: Value, ty: Type) -> Result<(), Error>;
102+
fn typed_write(&mut self, ptr: Self::Pointer, val: Value, ty: Type) -> Result;
101103

102104
/// Read a value of the given type.
103-
fn typed_read(&mut self, ptr: Self::Pointer, ty: Type) -> Result<Value, Error>;
105+
fn typed_read(&mut self, ptr: Self::Pointer, ty: Type) -> Result<Value>;
104106
}
105107
```
106108

0 commit comments

Comments
 (0)