-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Clarify safety of layout_for_ptr #117991
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
Closed
Closed
Clarify safety of layout_for_ptr #117991
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
This does not seem desirable. Shouldn't we guarantee that if the argument would be sound to cast to a reference, then it may also be passed to
size_of_val_raw
and behavior is equivalent tosize_of_val
?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.
I do agree it's unfortunate, but the reasoning for this is hedging with respect to future unsized tail kinds. It's certainly sufficient for any
MetaSized
type, and Rust can't yet manipulate properly!MetaSized
types, but exotic size kinds can cause issues.My go-to example is
UnsafeCell<ThinCStr>
. Getting the size of a value requires reading the value, which is a potential data race depending on whatever the external synchronization is.This is, to be honest, "I don't want to deal with it" flavored UB. Even if
size_of_val
panics or is a post-monomorphization error, there does need to be an option which actually gets the layout (if it's knowable).The lower floor is that if you can make a
Box
holding it,size_of_val_raw
on a uniquely owned allocation with a valid (but potentially dropped) value is allowed (e.g. when dropping the lastWeak<T>
and deallocating). Beyond that is as far as I'm aware still undecided.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.
I think size_of_val_raw should keep exposing the same underlying intrinsic as size_of_val. For these hypothetical future custom DST, whatever they do to keep size_of_val sound should also apply to size_of_val_raw. They will have to introduce new mechanisms anyway, so those should be used to deal with the extra constraints / provide new even-less-safe operations.
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.
It seems prudent to me not to make any guarantees about those hypothetical types. If we call it UB now, we can still change that to some defined behavior later, and even now the implementation could use the same intrinsic and possibly work, or explode. Am I missing something?
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.
We already made the guarantee that the size of every
&T
can be computed in safe code -- by providingsize_of_val
. I don't see what we'd lose by saying that the same can be done for every*const T
that satisfies all the requirements for an&T
. Not making that guarantee, OTOH, sounds like it would create a lot of potential for confusion and uncertainty.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.
I think I've slowly come around to agreeing that guaranteeing that {if
size_of_val(&*p)
is valid,size_of_val_raw(p)
is also valid and produces the same result} is reasonable. It is the "raw" version of the same operation, after all. The "future unsized tail kind" clause would then defer to this general case and call out that this requires the pointer to be valid for reads that may occur, unlike the other unsize kinds.What may have convinced me the most was a realization that the "more raw" version should probably live in the
ptr
module and not themem
module anyway, or be a method of raw pointers like it is of theDynMetadata
type. Having three versions of the same functionality isn't great (especially withLayout
versions existing as well as the split size/align), but I don't think that's bad enough to justifyWhat's mostly being said in guaranteeing that imo is a soft & loose commitment to one of:
size_of_val
(i.e. they satisfySelf: ?edition2015#Sized
), causing erroneous behavior (complains to sanitizers) if they don't provide a way to get size from&self
, thensize_of_val
(i.e. they do not satisfySelf: ?edition2015#Sized
) unless they provide a way to get size from&self
(e.g.Self: DynSized
).Unfortunately, the desire to avoid even implying any decision there is a primary part of why
feature(layout_for_ptr)
has been stuck in unstable limbo and why this PR proposes to weaken it to the minimally useful baseline (i.e. explicitly not saying anything about unsized tail kinds that do not yet exist)."It's just
size_of_val
with these relaxations for 'MetaSized
' types" does also feel conservative, but I've yet to find a way to word that without implying that the functionality works for unsize kinds that haven't been mentioned. And due to all that,size_of_val::<&UnsafeCell<ThinCStr>>
haunts me.Footnotes
Returning wrong results is very scary. Consider a version of
mem::swap
or eventake_mut
that works forT: ?Sized
by using a dynamically allocated heap buffer instead of a statically sized stack slot, checking the runtime queried size/align/metadata to ensure that it all lines up. Or even justfeature(unsized_locals)
. These turnextern type
claiming a layout of(0, 1)
into a nicely hidden soundness footgun. ↩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 we make
size_of_val
panic / mono-time-error for such hypothetical future types, we can do the same forsize_of_val_raw
. If we find a way to statically prevent them from being passed tosize_of_val
, we can likewise do the same forsize_of_val_raw
. I think that's what you are saying?