-
Notifications
You must be signed in to change notification settings - Fork 15
Legal values for a &T
or &mut T
where T
is zero-sized
#10
Comments
My feeling here is that any non-null value should be ok. |
My preference is that any non-null value is a valid reference to a 0-sized type. Also, for things to make sense, all reborrows would have to preserve that value, e.g.: struct Foo((), ());
fn addr_of<T>(t: &T) -> usize { t as *const T as usize }
fn main() {
let foo = unsafe { &*(0x1337 as *const Foo) };
assert_eq!(addr_of(foo), 0x1337);
assert_eq!(addr_of(&*foo), 0x1337);
assert_eq!(addr_of(&foo.0), 0x1337);
assert_eq!(addr_of(&foo.1), 0x1337);
} I don't see any good reasons not to adopt this, but some people have suggested that we may sometimes "squash" reborrows to 0x1 or something to reduce implementation complexity. |
@arielb1 I agree with you. A reborrow should, in my opinion, always be equivalent to the original... |
I think that any non-null value should be OK, and that the compiler should correctly propagate that value. It is useful and expected in #[repr(C)] structs for a ZST before a sized type to have the same address of that field. In addition, ZSTs have the advantage that they can be passed around as &T and &mut T while not allowing the consumer to move the actual data behind the reference (which may be a variable sized #[repr(C)] struct). The methods on the ZST T can then cast the reference to a reference to the actual backing data type, and avoid unsafety that would come by allowing moving, for example, the header of a FFI C type. I think patterns like that should be OK, as they solve a real problem, and shouldn't break anything. This means that pointers to opaque C structs can be passed around like native rust types behind &T and &mut T, rather than requiring special structs like:
which require a large number of impls on them and don't provide a nice API. |
Particularly since people often use |
The issue mentions Shared references are always the most complicated case... for the particular case of Related questions: Are zero-size types automatically |
We mean all of them.
You seem to be confused. Copying a ZST is guaranteed to not be UB by itself. However, copying a UB is not guaranteed to be safe - unsafe code should be able to assume that random ZSTs are not randomly being created from nothing. UB means that the compiler can do anything it wants. Safety violations mean that "certified-safe" code can do anything it wants. If you don't care about using the type-system to prove that your program is safe (either because you use some other way to prove it, or just don't give a ****), safety violations are not important. |
I see UB and safety as being closely related -- the guarantee safety (of a library) provides is that calling it from safe code will not trigger UB. The discussion above sounded (to me) like it was making the statement "if |
Sure. "certified-safe" code has the property that it can be composed with arbitrary "certified-safe" code while remaining "certified-safe" and never causing UB. If you have a dubious texture parser with buffer overflows on every edge case, declaring it as a Similarly, if you have a /// indicates that the GIL is held by the current thread.
pub struct GILToken(PhantomData<()>);
impl !Sync for GILToken {}
pub fn do_stuff_that_assumes_gil_is_held(gil: &GILToken) { /* .. */ } And you have some code from another module that goes fn make_token() -> &'static GILToken {
unsafe { &*(0x594f4c4f as *const GILToken) }
} Then doing |
Can you elaborate on what you mean by "its sharing"?
No, they are not https://is.gd/eCuyAk.
Agreed, this is not safe, and I do not believe you can do it with safe code. I'm curious if you think this is in dispute :) |
&T
where T
is zero-sized&T
or &mut T
where T
is zero-sized
Hmm, I'm not sure why saying that a fn make_token() -> &'static GILToken {
&*(0x594f4c4f as *const GILToken)
} would not actually compile, because the |
Yeah. I forgot the |
I mean the protocol that governs the type while it is shared -- the set of invariants that define what is and isn't legal to do with the memory occupied by
I guess I misunderstood some of your and @arielb1's earlier statements. I now see that's now what you meant. I am not sure I can completely (formally) make sense of what you mean instead -- probably something related to, for example, the compiler not being allowed to just add spurious dereferences of a |
Hmm. I feel like it takes more than possessing the same bits to have a value of a suitable type. That is, surely there is a distinction between a |
This is the memory model repo, not the soundness repo. |
The two are closely related. After all, the core purpose of the guarantees provided by types is to make sure that the program has no UB.
Well, yes, that's exactly what I mean. It's not enough for |
I think you may have it backwards, here.
I read this as saying that, given an IOW, I parsed it as a denial of a "canonical value" for references to ZSTs. This seems to bear that out:
|
From a UB standpoint, it does not matter what the value is. From a soundness standpoint, nobody's preventing you from using the address of an |
This is now discussed as part of the validity invariants: rust-lang/unsafe-code-guidelines#76. |
In #3, the question was posed what values a
Box<T>
whereT
is ZST can have -- one might ask a similar question about a&T
. The answer may well be the same as #3, but it could be different, depending on whether theBox
API is deemed to impose its own limitations.The text was updated successfully, but these errors were encountered: