-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
"discriminant_value" intrinsic returns u64, cannot fit all discriminants #70509
Comments
cc @rust-lang/libs I wonder if we're safe to change |
A crater run would be fine, but we should not block a change on people doing things like that IMO. |
I wouldn't have any sympathy for people transmuting to u64. If you want to be slick, you could make std::mem::Discriminant<T> have the same size as T's discriminant, rather than always 8 bytes. This supports enums with a 128 bit discriminant without increasing the size of Discriminant in the common case. |
Would the following work? Add a (potentially hidden) permanently unstable trait trait DiscriminantKind {
// the traits are required by `mem::Discriminant`
type Discriminant: Clone + Copy + Debug + Eq + PartialEq + Hash + Send + Sync + Unpin;
} which is automatically implemented for every type and change
This allows for |
The @lcnr There’s no point in making the intrinsic hide its implementation details, as the intrinsics are inherently unstable. |
The reason why I considered hiding it is to not pollute rustdoc by adding this (for the user irrelevant) impl to every type. |
@rustbot claim |
Rather than perma-unstable, I think the trait could be private to libcore. (The An associated type in a trait is required because that’s how the compiler supports having a field of different types in |
@SimonSapin While it is not required as a bound for |
We can make the intrinsic private, they're not really meant to be used directly anyway. |
Couldn’t the return type of |
This might not even require a change to the compiler side of the intrinsic, if we add |
I’m not sure what’s exactly the point of changing the intrinsic. Intrinsics are inherently unstable, have changed in the past multiple times and people are expected to not rely on them being stable. It is an implementation detail. What for are we trying to make our own lives harder by thinking about it at all?
Not sure there’s any difference as people can just (unstably) |
Yeah, I would change the intrinsic in whatever way makes the implementation easier. |
While implementing #70705 I realized that this breaks PartialEq for #![feature(core_intrinsics, repr128)]
use std::intrinsics::discriminant_value;
#[derive(PartialEq, Debug)]
#[repr(i128)]
enum Test {
A = 0,
B = u64::max_value() as i128 + 1,
}
fn main() {
println!("{}, {}", discriminant_value(&Test::A), discriminant_value(&Test::B));
assert_eq!(Test::A, Test::B);
} |
memory unsafety ❤️ #![feature(repr128, arbitrary_enum_discriminant)]
#[derive(PartialEq, Debug)]
#[repr(i128)]
enum Test {
A(Box<u64>) = 0,
B(usize) = u64::max_value() as i128 + 1,
}
fn main() {
assert_eq!(Test::A(Box::new(2)), Test::B(0));
//~^ Illegal instruction (core dumped)
} |
At least |
@lcnr to clarify, when you say
you meant that the failure to represent 128-bit discriminants breaks PartialEq, right? That is, it's not that the approach of using an associated type of a magic trait breaks PartialEq, but rather the underlying bug. |
Yes, in the current state |
#![derive(PartialEq)]
enum Foo {
A(u8),
B,
} can be thought of as impl PartialEq for Foo {
fn eq(&self, rhs: &Self) -> bool {
use core::intrinsics::discriminant_value;
if discriminant_value(self) != discriminant_value(rhs) {
return false;
}
match (self, rhs) {
(Foo::A(l), Foo::A(r)) => l == r,
(Foo::B, Foo::B) => true,
_ => unsafe { core::hint::unreachable_unchecked() },
}
}
} |
The output of impl ::core::cmp::PartialEq for Test {
#[inline]
fn eq(&self, other: &Test) -> bool {
{
let __self_vi =
unsafe { ::core::intrinsics::discriminant_value(&*self) } as
i128;
let __arg_1_vi =
unsafe { ::core::intrinsics::discriminant_value(&*other) } as
i128;
if true && __self_vi == __arg_1_vi {
match (&*self, &*other) {
(&Test::A(ref __self_0), &Test::A(ref __arg_1_0)) =>
(*__self_0) == (*__arg_1_0),
(&Test::B(ref __self_0), &Test::B(ref __arg_1_0)) =>
(*__self_0) == (*__arg_1_0),
_ => unsafe { ::core::intrinsics::unreachable() }
}
} else { false }
}
} My understanding is that in current Nightly, I believe #70705 would fix this. |
One note would be that, eventually, it seems like we could stabilize the |
Although I guess that's an orthogonal thing -- i.e., in user code, |
So what is needed to unblock this? This PR is blocking some Miri cleanup of |
It seems like we're mostly good to go forward, there aren't (afaict) many public-facing implications of this beyond correctly handling larger discriminants. |
Sorry, I meant to post that in the PR #70705, yes. |
Stabilize `arbitrary_enum_discriminant` Closes rust-lang#60553. ---- ## Stabilization Report _copied from rust-lang#60553 (comment) ### Summary Enables a user to specify *explicit* discriminants on arbitrary enums. Previously, this was hard to achieve: ```rust #[repr(u8)] enum Foo { A(u8) = 0, B(i8) = 1, C(bool) = 42, } ``` Someone would need to add 41 hidden variants in between as a workaround with implicit discriminants. In conjunction with [RFC 2195](https://github.com/rust-lang/rfcs/blob/master/text/2195-really-tagged-unions.md), this feature would provide more flexibility for FFI and unsafe code involving enums. ### Test cases Most tests are in [`src/test/ui/enum-discriminant`](https://github.com/rust-lang/rust/tree/master/src/test/ui/enum-discriminant), there are two [historical](https://github.com/rust-lang/rust/blob/master/src/test/ui/parser/tag-variant-disr-non-nullary.rs) [tests](https://github.com/rust-lang/rust/blob/master/src/test/ui/parser/issue-17383.rs) that are now covered by the feature (removed by this pr due to them being obsolete). ### Edge cases The feature is well defined and does not have many edge cases. One [edge case](rust-lang#70509) was related to another unstable feature named `repr128` and is resolved. ### Previous PRs The [implementation PR](rust-lang#60732) added documentation to the Unstable Book, rust-lang/reference#1055 was opened as a continuation of rust-lang/reference#639. ### Resolution of unresolved questions The questions are resolved in rust-lang#60553 (comment). ---- (someone please add `needs-fcp`)
Ever since we had enums with
repr(u128)
/repr(i128)
, the type of thediscriminant_value
intrinsic was no longer adequate: it returns au64
, which simply cannot fit all discriminant values.@eddyb says the type of the intrinsic should be adjusted. This will affect both its codegen and Miri implementation.
This issue has been assigned to @lcnr via this comment.
The text was updated successfully, but these errors were encountered: