You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This is quite a mouthful: [`Operand`] can represent either data stored somewhere in the [interpreter memory](#memory) (`Operand::Indirect`), or (as an optimization) immediate data stored in-line.
60
-
And [`Immediate`] can either be a single (potentially uninitialized) [scalar value][`Scalar`] (integer or thin pointer), or a pair of two of them.
61
-
In our case, the single scalar value is *not* (yet) initialized.
62
-
63
-
When the initialization of `_1` is invoked, the
64
-
value of the `FOO` constant is required, and triggers another call to
65
-
`tcx.const_eval`, which will not be shown here. If the evaluation of FOO is
66
-
successful, `42` will be subtracted from its value `4096` and the result stored in
67
-
`_1` as `Operand::Immediate(Immediate::ScalarPair(Scalar::Raw { data: 4054, .. }, Scalar::Raw { data: 0, .. })`. The first
68
-
part of the pair is the computed value, the second part is a bool that's true if
69
-
an overflow happened. A `Scalar::Raw` also stores the size (in bytes) of this scalar value; we are eliding that here.
58
+
`Operand::Immediate(Immediate::Scalar(ScalarMaybeUndef::Undef))`. This is quite
59
+
a mouthful: [`Operand`] can represent either data stored somewhere in the
60
+
[interpreter memory](#memory) (`Operand::Indirect`), or (as an optimization)
61
+
immediate data stored in-line. And [`Immediate`] can either be a single
62
+
(potentially uninitialized) [scalar value][`Scalar`] (integer or thin pointer),
63
+
or a pair of two of them. In our case, the single scalar value is *not* (yet)
64
+
initialized.
65
+
66
+
When the initialization of `_1` is invoked, the value of the `FOO` constant is
67
+
required, and triggers another call to `tcx.const_eval`, which will not be shown
68
+
here. If the evaluation of FOO is successful, `42` will be subtracted from its
This is mainly the error enum and the [`ConstValue`] and [`Scalar`] types. A `ConstValue` can
100
-
be either `Scalar` (a single `Scalar`, i.e., integer or thin pointer),
101
-
`Slice` (to represent byte slices and strings, as needed for pattern matching) or `ByRef`, which is used for anything else and
102
-
refers to a virtual allocation. These allocations can be accessed via the
103
-
methods on `tcx.interpret_interner`.
104
-
A `Scalar` is either some `Raw` integer or a pointer; see [the next section](#memory) for more on that.
106
+
This is mainly the error enum and the [`ConstValue`] and [`Scalar`] types. A
107
+
`ConstValue` can be either `Scalar` (a single `Scalar`, i.e., integer or thin
108
+
pointer), `Slice` (to represent byte slices and strings, as needed for pattern
109
+
matching) or `ByRef`, which is used for anything else and refers to a virtual
110
+
allocation. These allocations can be accessed via the methods on
111
+
`tcx.interpret_interner`. A `Scalar` is either some `Raw` integer or a pointer;
112
+
see [the next section](#memory) for more on that.
105
113
106
114
If you are expecting a numeric result, you can use `eval_usize` (panics on
107
115
anything that can't be representad as a `u64`) or `try_eval_usize` which results
108
116
in an `Option<u64>` yielding the `Scalar` if possible.
109
117
110
118
## Memory
111
119
112
-
To support any kind of pointers, Miri needs to have a "virtual memory" that the pointers can point to.
113
-
This is implemented in the [`Memory`] type.
114
-
In the simplest model, every global variable, stack variable and every dynamic allocation corresponds to an [`Allocation`] in that memory.
115
-
(Actually using an allocation for every MIR stack variable would be very inefficient; that's why we have `Operand::Immediate` for stack variables that are both small and never have their address taken.
116
-
But that is purely an optimization.)
117
-
118
-
Such an `Allocation` is basically just a sequence of `u8` storing the value of each byte in this allocation.
119
-
(Plus some extra data, see below.)
120
-
Every `Allocation` has a globally unique `AllocId` assigned in `Memory`.
121
-
With that, a [`Pointer`] consists of a pair of an `AllocId` (indicating the allocation) and an offset into the allocation (indicating which byte of the allocation the pointer points to).
122
-
It may seem odd that a `Pointer` is not just an integer address, but remember that during const evaluation, we cannot know at which actual integer address the allocation will end up -- so we use `AllocId` as symbolic base addresses, which means we need a separate offset.
123
-
(As an aside, it turns out that pointers at run-time are [more than just integers, too](https://rust-lang.github.io/unsafe-code-guidelines/glossary.html#pointer-provenance).)
120
+
To support any kind of pointers, Miri needs to have a "virtual memory" that the
121
+
pointers can point to. This is implemented in the [`Memory`] type. In the
122
+
simplest model, every global variable, stack variable and every dynamic
123
+
allocation corresponds to an [`Allocation`] in that memory. (Actually using an
124
+
allocation for every MIR stack variable would be very inefficient; that's why we
125
+
have `Operand::Immediate` for stack variables that are both small and never have
126
+
their address taken. But that is purely an optimization.)
127
+
128
+
Such an `Allocation` is basically just a sequence of `u8` storing the value of
129
+
each byte in this allocation. (Plus some extra data, see below.) Every
130
+
`Allocation` has a globally unique `AllocId` assigned in `Memory`. With that, a
131
+
[`Pointer`] consists of a pair of an `AllocId` (indicating the allocation) and
132
+
an offset into the allocation (indicating which byte of the allocation the
133
+
pointer points to). It may seem odd that a `Pointer` is not just an integer
134
+
address, but remember that during const evaluation, we cannot know at which
135
+
actual integer address the allocation will end up -- so we use `AllocId` as
136
+
symbolic base addresses, which means we need a separate offset. (As an aside,
137
+
it turns out that pointers at run-time are
138
+
[more than just integers, too](https://rust-lang.github.io/unsafe-code-guidelines/glossary.html#pointer-provenance).)
124
139
125
140
These allocations exist so that references and raw pointers have something to
126
141
point to. There is no global linear heap in which things are allocated, but each
@@ -131,23 +146,35 @@ matter how unsafe) operation that you can do that would ever change said pointer
131
146
to a pointer to a different local variable `b`.
132
147
Pointer arithmetic on `a` will only ever change its offset; the `AllocId` stays the same.
133
148
134
-
This, however, causes a problem when we want to store a `Pointer` into an `Allocation`: we cannot turn it into a sequence of `u8` of the right length!
135
-
`AllocId` and offset together are twice as big as a pointer "seems" to be.
136
-
This is what the `relocation` field of `Allocation` is for: the byte offset of the `Pointer` gets stored as a bunch of `u8`, while its `AllocId` gets stored out-of-band.
137
-
The two are reassembled when the `Pointer` is read from memory.
138
-
The other bit of extra data an `Allocation` needs is `undef_mask` for keeping track of which of its bytes are initialized.
149
+
This, however, causes a problem when we want to store a `Pointer` into an
150
+
`Allocation`: we cannot turn it into a sequence of `u8` of the right length!
151
+
`AllocId` and offset together are twice as big as a pointer "seems" to be. This
152
+
is what the `relocation` field of `Allocation` is for: the byte offset of the
153
+
`Pointer` gets stored as a bunch of `u8`, while its `AllocId` gets stored
154
+
out-of-band. The two are reassembled when the `Pointer` is read from memory.
155
+
The other bit of extra data an `Allocation` needs is `undef_mask` for keeping
156
+
track of which of its bytes are initialized.
139
157
140
158
### Global memory and exotic allocations
141
159
142
-
`Memory` exists only during the Miri evaluation; it gets destroyed when the final value of the constant is computed.
143
-
In case that constant contains any pointers, those get "interned" and moved to a global "const eval memory" that is part of `TyCtxt`.
144
-
These allocations stay around for the remaining computation and get serialized into the final output (so that dependent crates can use them).
145
-
146
-
Moreover, to also support function pointers, the global memory in `TyCtxt` can also contain "virtual allocations": instead of an `Allocation`, these contain an `Instance`.
147
-
That allows a `Pointer` to point to either normal data or a function, which is needed to be able to evaluate casts from function pointers to raw pointers.
148
-
149
-
Finally, the [`GlobalAlloc`] type used in the global memory also contains a variant `Static` that points to a particular `const` or `static` item.
150
-
This is needed to support circular statics, where we need to have a `Pointer` to a `static` for which we cannot yet have an `Allocation` as we do not know the bytes of its value.
160
+
`Memory` exists only during the Miri evaluation; it gets destroyed when the
161
+
final value of the constant is computed. In case that constant contains any
162
+
pointers, those get "interned" and moved to a global "const eval memory" that is
163
+
part of `TyCtxt`. These allocations stay around for the remaining computation
164
+
and get serialized into the final output (so that dependent crates can use
165
+
them).
166
+
167
+
Moreover, to also support function pointers, the global memory in `TyCtxt` can
168
+
also contain "virtual allocations": instead of an `Allocation`, these contain an
169
+
`Instance`. That allows a `Pointer` to point to either normal data or a
170
+
function, which is needed to be able to evaluate casts from function pointers to
171
+
raw pointers.
172
+
173
+
Finally, the [`GlobalAlloc`] type used in the global memory also contains a
174
+
variant `Static` that points to a particular `const` or `static` item. This is
175
+
needed to support circular statics, where we need to have a `Pointer` to a
176
+
`static` for which we cannot yet have an `Allocation` as we do not know the
@@ -156,14 +183,20 @@ This is needed to support circular statics, where we need to have a `Pointer` to
156
183
157
184
### Pointer values vs Pointer types
158
185
159
-
One common cause of confusion in Miri is that being a pointer *value* and having a pointer *type* are entirely independent properties.
160
-
By "pointer value", we refer to a `Scalar::Ptr` containing a `Pointer` and thus pointing somewhere into Miri's virtual memory.
161
-
This is in contrast to `Scalar::Raw`, which is just some concrete integer.
162
-
163
-
However, a variable of pointer or reference *type*, such as `*const T` or `&T`, does not have to have a pointer *value*:
164
-
it could be obtaining by casting or transmuting an integer to a pointer (currently that is hard to do in const eval, but eventually `transmute` will be stable as a `const fn`).
165
-
And similarly, when casting or transmuting a reference to some actual allocation to an integer, we end up with a pointer *value* (`Scalar::Ptr`) at integer *type* (`usize`).
166
-
This is a problem because we cannot meaningfully perform integer operations such as division on pointer values.
186
+
One common cause of confusion in Miri is that being a pointer *value* and having
187
+
a pointer *type* are entirely independent properties. By "pointer value", we
188
+
refer to a `Scalar::Ptr` containing a `Pointer` and thus pointing somewhere into
189
+
Miri's virtual memory. This is in contrast to `Scalar::Raw`, which is just some
190
+
concrete integer.
191
+
192
+
However, a variable of pointer or reference *type*, such as `*const T` or `&T`,
193
+
does not have to have a pointer *value*: it could be obtaining by casting or
194
+
transmuting an integer to a pointer (currently that is hard to do in const eval,
195
+
but eventually `transmute` will be stable as a `const fn`). And similarly, when
196
+
casting or transmuting a reference to some actual allocation to an integer, we
197
+
end up with a pointer *value* (`Scalar::Ptr`) at integer *type* (`usize`). This
198
+
is a problem because we cannot meaningfully perform integer operations such as
0 commit comments