Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 605
const-eval: explain the final-value-byte-provenance restriction#2138
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base:master
Are you sure you want to change the base?
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
2bb92c2c461766af42b06716afec8fd2d3e57f4708File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -232,6 +232,94 @@ r[const-eval.const-expr.loop] | ||||||||||||
| r[const-eval.const-expr.if-match] | ||||||||||||
| * [if] and [match] expressions. | ||||||||||||
| ## Const initializers | ||||||||||||
| r[const-eval.const-expr.final-value-provenance] | ||||||||||||
| The representation of the final value of a [constant][constant initializer] or [static initializer] must only contain bytes with provenance in whole-pointer groups. If a byte has provenance but is not part of an adjacent group of correctly-ordered bytes that form an entire pointer, compilation will fail. | ||||||||||||
| ||||||||||||
| The representation of the final value of a [constant][constant initializer] or [static initializer] must only contain bytes with provenance in whole-pointer groups. If a byte has provenance but is not part of an adjacent group of correctly-ordered bytes that form an entire pointer, compilation will fail. | |
| The representation of the final value of a [constant][constant initializer] or [static initializer] must only contain bytes with provenance in whole-pointer groups. If a byte has provenance but is not part of an adjacent group of correctly-ordered bytes that form an entire pointer, compilation will fail. | |
| > [!NOTE] | |
| > The _final_ here indicates that the value is computed by the initializer expression, to make it clear that it's not some arbitrary value that occurs in the middle of computing said expression. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think it would be useful to add some ASCII art to explain what happens?
+-------------------------------------------------+-------------------------+-------------------------------+
Field | x (offsets 0..16) | y (offsets 16..24) | <padding> (offsets 24..32) |
+-------------------------------------------------+-------------------------+-------------------------------+
Data | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | <byte 4..8 of &0> UU UU UU UU |
+-------------------------------------------------+-------------------------+-------------------------------+
Also, why is the x field even part of this example? It seems entirely unnecessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| > The bytes with provenance must form a complete pointer in the correct order. In the example above, the pointer is written at offset 20, but a pointer requires 8 bytes (assuming an 8-byte pointer). Four of those bytes fit in the `y` field; the rest extend into the padding at offset 24. When the fields are initialized, the `y` bytes get overwritten, leaving only a partial pointer (4 bytes) in the padding. These 4 bytes have provenance but don't form a complete pointer, causing compilation to fail. | |
| > The bytes with provenance must form a complete pointer in the correct order. In the example above, the pointer is written at offset 20, but a pointer requires 8 bytes (assuming a 64-bit target). Four of those bytes fit in the `y` field; the rest extend into the padding at offset 24. When the fields are initialized, the `y` bytes get overwritten, leaving only a partial pointer (4 bytes) in the padding. These 4 bytes have provenance but don't form a complete pointer, causing compilation to fail. |
"a pointer requires 8 bytes (assuming an 8-byte pointer)" sounds too much like an obvious tautology.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically speaking, the use of m here at the end incurs a typed copy which resets the padding back to "uninit".
With the hidden code, this example turns more into "how can I launder an arbitrary Pair value I got from somewhere to ensure it doesn't have nonsense it its padding", but that's not what the user sees in these docs.
Also, the more I think about it the more it makes me really uneasy that we'd recommend people write horrible code like this instead of the simple safe initializer above. There's no way I can imagine that initializer to actually cause a problem. You have to do "weird" things like reading/overwriting a part of a pointer value or doing a typed copy where the source memory has pointer bytes in places that the type doesn't store a pointer. It's non-trivial to fully precisely define this list, but even a vague and somewhat hand-wavy definition I think is better than people reading this and going "uh so should I use this MaybeUninit construction instead of normal safe Rust?".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does final value mean more than coercions? Should we use that term if so, or should we create a glossary entry maybe?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tshepang I don't understand the question. What does any of this have to do with coercions?
This is about the value computed by the const initializer expression, as opposed to some arbitrary value that occurs in the middle of computing said expression.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for lacking clarity, was thinking final value is a technical term, guessing it could mean a value one gets after coercions, me lacking proper vocabulary/understanding.
Would it be good to have final value be a glossary entry, or should we use different terminology... because framing things like that maybe goes too much into the implementation details. Maybe that's ok, like these are details that can't be omitted. Thing is it was not clear to me why call it final, and I don't know what happens before final.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this is about cases where a const item's path is used in an array repeat expression, and the const item needs to be coerced in some way before using it as the array, like in rust-lang/rust#143671 and rust-lang/rust#140123? I don't see how that could possibly matter though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If anything the term here is "(final) value of a (const or static) initializer". There's no general concept of "final value".