Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upPassing safe references to empty enums #2
Comments
This comment has been minimized.
This comment has been minimized.
Amanieu
commented
Aug 16, 2016
|
I think it should definitely be possible to include such an enum in a struct, especially considering the fact that
|
This comment has been minimized.
This comment has been minimized.
|
Personally, I'd argue that we should follow Weeping Angels rules here :P
In particular, a reference's lifetime contract is that it can only exist for a lifetime strictly included in the referent's lifetime. As one cannot construct an instance of an empty type, no value of such a type can have a non-empty lifetime. Constructing a reference with an invalid lifetime is (I think?) UB. QED. I personally feel that generic parameters are a red herring here: In the absence of Creating such a reference with
Other options include creating a reference from a pointer with invalid pointee (UB), or transmuting a reference of another type (which I think is, or at least should be, UB). What I think should be trivially valid are raw pointers to empty-typed values (which can be used to refer to opaque FFI types), which can be meaningfully newtyped (for external use) or retyped and then reference-ized/dereferenced (for internal use). |
This comment has been minimized.
This comment has been minimized.
|
This is equivalent creating references to invalid enum variants, e.g. enum Foo {
X, Y
}
fn main() {
let data: *const u32 = &42;
let illegal = unsafe { &*(data as *const Foo) };
}I am still not sure whether to make this UB. dereferencing such a pointer is obviously UB, through. |
This comment has been minimized.
This comment has been minimized.
Can you say more about why you feel this should be UB? I am not persuaded. =) I agree that dereferencing of trying to load from such a pointer (at least without transmuting it back...) seems problematic, but I'm not sure that creating a pointer is bad. That said, if the original intention of this pattern is to make something that can't be created, it seems like privacy could do that more easily and somewhat less problematically. =) |
This comment has been minimized.
This comment has been minimized.
mystor
commented
Aug 18, 2016
|
As the person who asked the question, I can clarify the original goal of this question. I want to have an object Foo which has a representation in C++, which is opaque to the Rust code. Consumers of my library would be defining The type I originally tried to implement this by creating a wrapper type around a private member: #[repr(C)]
pub struct Foo {
_prevent_construction: (),
}However, when consumers would put this type in extern "C" {
fn pass_a_foo(foo: *const Foo);
}the enum Impossible {}
#[repr(C)]
pub struct Foo {
_prevent_construction: Impossible,
}and simply make sure that within the module where this type was defined (which is littered with unsafe code anyways), references to |
This comment has been minimized.
This comment has been minimized.
|
That sounds like an issue with the |
This comment has been minimized.
This comment has been minimized.
mystor
commented
Aug 18, 2016
|
I agree that this would be best accomplished by changing the improper_ctypes lint, I was just trying to explain the motivation. I still am curious as to whether or not it's safe, whether or not it is the best solution to the problem at hand. |
This comment has been minimized.
This comment has been minimized.
|
@nikomatsakis: I was referring to transmuting the value, not the pointer to the value, and so it runs afoul of "actually constructing an instance of an empty type". Converting the pointer and then making a reference from that runs afoul of the lifetime issue at the top of my post. @mystor: Out of curiousity, why use EDIT: Ah, nevermind, misunderstood @mystor's goals. |
This comment has been minimized.
This comment has been minimized.
mystor
commented
Aug 25, 2016
|
Is it possible to invoke Undefined Behavior in this program without modifying the module |
This comment has been minimized.
This comment has been minimized.
Amanieu
commented
Aug 25, 2016
|
Yes, it allows you to trigger UB from safe code, but this is fine since you need unsafe code to create such a reference in the first place. It is your responsibility to ensure that you do not leak this reference to safe code outside your module. With that said, I think simply manipulating the reference without dereferencing it (inside your unsafe module) should not be UB. |
This comment has been minimized.
This comment has been minimized.
mystor
commented
Aug 25, 2016
|
But can you trigger Undefined Behavior from the outside module? The outside module cannot see the empty enum due to privacy, and thus cannot match on it. I'm handing the reference to safe code, but the safe code can't do anything with the reference, so I think it might be okay. |
This comment has been minimized.
This comment has been minimized.
Amanieu
commented
Aug 25, 2016
|
Hmm I think that would allow UB since you can do |
This comment has been minimized.
This comment has been minimized.
mystor
commented
Aug 25, 2016
|
I don't think that's possible. Bar is not Copy or Clone, so it can't be copied out. |
This comment has been minimized.
This comment has been minimized.
|
What that says to me (modulo @nikomatsakis' point about Tootsie-Pop models) is that type violations probably should not be permitted to exit the Anything else pushes us quite far towards Tootsie-Pop models, which have very nontrivial effects on the compiler's ability to optimize safe code ("the unchecked get use case"). |
nikomatsakis
added
T-lang
K-Code-Example
labels
Sep 2, 2016
This comment has been minimized.
This comment has been minimized.
glaebhoerl
commented
Sep 3, 2016
•
|
I agree with @eternaleye's original comment. References ( (Requiring (or hypothesizing) a value of such a type, that is |
This comment has been minimized.
This comment has been minimized.
|
Is this UB? (if so, we might want to fix it...) https://doc.rust-lang.org/src/core/up/src/libcore/fmt/mod.rs.html#185 |
This comment has been minimized.
This comment has been minimized.
mystor
commented
Sep 8, 2016
|
According to many of the arguments here, the answer is yes. It is currently not optimized as UB afaik, so it should be OK for now. |
This comment has been minimized.
This comment has been minimized.
|
@mystor I would agree. Just pointing out that it's used in real code. |
This comment has been minimized.
This comment has been minimized.
mystor
commented
Sep 8, 2016
|
That use seems like another use case for my opaque struct idea (https://internals.rust-lang.org/t/pre-rfc-opaque-structs/4005). It could also just use raw pointers and a PhantomData however. |
This comment has been minimized.
This comment has been minimized.
|
cc rust-lang/rust#36976 - the plain old "reference to undef" case is also interesting. |
This comment has been minimized.
This comment has been minimized.
glaebhoerl
commented
Oct 8, 2016
•
|
Revisiting my comment from earlier....
I'm struck anew by what treacherous ground this question of "should it be legal, or should it be undefined behavior" really is. When other people express the sentiment that some particular For what it's worth, I think my feelings on the subject attach more strongly to uninhabited types than to reference types. A live value of an uninhabited type is the very definition of undefined behavior (like GCC's __builtin_unreachable), what sound type systems everywhere exist to preclude, and the clarity of this really should not be muddied. If this isn't undefined behavior, then nothing is. On the other hand, if we say that in the presence of |
This comment has been minimized.
This comment has been minimized.
|
Merging rust-lang/rust#36449 has kind of answered the question raised here, didn't it? Everything |
This comment has been minimized.
This comment has been minimized.
|
No. We don't use |
nikomatsakis commentedAug 16, 2016
On IRC, @mystor asked me whether it would be illegal to pass around
&SafeTypeor&mut SafeTypegiven this definition:In particular, is it ok to have an empty enum "by-value" in a struct in this fashion?