-
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
test more variants of enum-int-casting #61702
Conversation
(rust_highfive has picked a reviewer for you, use r? to override) |
I looked at the MIR for something like fn main() {
let _val = std::cmp::Ordering::Less as i32;
} has the following MIR:
What is even going on? Why is there an addition? Of 0? Checked for overflow?!? I find it unlikely that adding 0 will make anything overflow. For comparison, the MIR with an intermediate let-binding: fn main() {
let val = std::cmp::Ordering::Less;
let _val = val as i32;
}
That's what I expected to begin with. Cc @eddyb (This is unrelated to the PR by the way, I am just not sure what would be a better place to discuss this.) |
@RalfJung Heh, @oli-obk knows why because IIRC he did all of this. Let's take it one by one: Now, why is So, that weird MIR you see, doesn't depend on |
So
I was more wondering where it comes from, but now I guess this is from the "counting up" that happens where the next variant gets N+1 if it does not have an explicit discriminator expression?
I don't understand. Who can get which discriminants of which other expressions but not at run-time...? Why is it better to refer to the discriminant expression here instead of doing something like what happens with an intermediate let-binding? The code generated with the let-binding is more readable and it needs fewer basic blocks. Having a special case for "immediate cast to int" makes testing harder. This almost caused an incorrect assertion to sneak into the miri engine that neither the rustc CTFE test suite nor the Miri test suite found, because nobody writes these tests in let-expanded way and nobody expects that to hit a totally different code path. Is there any argument for this funny special case translation? I could understand if some sort of const propagation would kick in later and replace statically-known casts with constant stuff, but that is not what is happening here. |
enum Foo {
A = 0,
B = Foo::A as isize + 2,
} This will cycle error if you do the runtime thing, because the runtime thing requires knowing the layout, and knowing the layout requires knowing the discriminants. |
But we still cycle error for this: enum Foo {
A = 0,
B = {let v = Foo::A; v as isize + 2},
} or this: const fn do_something(x: Foo) -> isize { ... }
enum Foo {
A = 0,
B = do_something(Foo::A),
} Seems like an awful hack for a partial solution... and even supporting this very special case seems way not worth the cost in incurs. This affects all MIR generated anywhere! |
@RalfJung Backwards compatibility is certainly worth it, and there was no |
Well I'd be in favor of deprecating cyclic enums... But in the interrim, can we do something like only apply this weird special case when translating an enum discriminant expression? |
Can you point me to the code implementing this? I did a grep for "enum" and "cast" in |
rust/src/librustc_mir/hair/cx/expr.rs Lines 636 to 710 in 1cbd8a4
|
Okay, let's see in #61723 what crater finds, and let's move the discussion there. Until then, this PR helps to at least get us some basic test coverage for "both kinds" of enum-int-casts. |
r? @oli-obk |
@bors r+ rollup |
📌 Commit 2e9931f has been approved by |
test more variants of enum-int-casting As I learned in rust-lang#61673 (comment), there is a code path we are not testing yet. Looks like enum-int-casting with and without an intermediate let-binding is totally different. EDIT: The reason for this is to get rid of the cycle in definitions such as: ```rust enum Foo { A = 0, B = Foo::A as isize + 2, } ``` This has historically been supported, so a hack adding special treatment to `Enum::Variant as _` was added to keep supporting it.
Rollup of 5 pull requests Successful merges: - #61702 (test more variants of enum-int-casting) - #61836 (Replace some uses of NodeId with HirId) - #61885 (Help LLVM better optimize slice::Iter(Mut)::len) - #61893 (make `Weak::ptr_eq`s into methods) - #61908 (don't ICE on large files) Failed merges: r? @ghost
As I learned in #61673 (comment), there is a code path we are not testing yet. Looks like enum-int-casting with and without an intermediate let-binding is totally different.
EDIT: The reason for this is to get rid of the cycle in definitions such as:
This has historically been supported, so a hack adding special treatment to
Enum::Variant as _
was added to keep supporting it.