-
Notifications
You must be signed in to change notification settings - Fork 13k
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
Use discriminant_value intrinsic for derive(PartialOrd) #24270
Conversation
Implements an intrinsic for extracting the value of the discriminant enum variant values. For non-enum types, this returns zero, otherwise it returns the value we use for discriminant comparisons. This means that enum types that do not have a discriminant will also work in this arrangement. This is (at least part of) the work on Issue rust-lang#24263
r? @brson (rust_highfive has picked a reviewer for you, use r? to override) |
r? @huonw |
3519318
to
afb7acf
Compare
rules: ast::UnsafeBlock(ast::CompilerGenerated), | ||
span: sp })); | ||
|
||
// FIXME: This unconditionally casts to `isize`. However: |
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.
oops this FIXME is now out-of-date. Will fix and push -f
.
…rrors. remove out of date fixme.
e9dbfae
to
47016f9
Compare
ast::TupleVariantKind(..) => ast::PatEnum(path, None), | ||
ast::StructVariantKind(..) => ast::PatStruct(path, Vec::new(), true), | ||
}) | ||
fn find_repr_type_name(diagnostic: &SpanHandler, |
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 like there's a chance that this could miss a repr attribute that is applied by a later pass of expansion.
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.
Very interesting, I'm not even sure how we can handle that ... without some somewhat serious revisions to the expansion infrastructure (e.g. some way to schedule an expansion "at the end" ...)
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.
The most conservative solution would be to change the intrinsic to return (sign, abs-value)
to avoid having to figure out the repr at all, but that does make the comparisons a bit less efficient depending on how smart LLVM can be.
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.
There is a simpler option. The intrinsic could be:
compare_variant_order<T:Reflect>(t: &T, u: &T)
which would yield a -1,0,1.
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.
(well, we may still want the discriminant_value
intrinsic itself independently, but I am inclined to agree that this is an elegant way to resolve all of the issues here.)
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.
Ah, yeah, that's a good call.
📌 Commit 847a897 has been approved by |
⌛ Testing commit 847a897 with merge c35ffe7... |
⛄ The build was interrupted to prioritize another pull request. |
Use `discriminant_value` intrinsic for `derive(PartialOrd)` [breaking-change] This is a [breaking-change] because it can change the result of comparison operators when enum discriminants have been explicitly assigned. Notably in a case like: ```rust #[derive(PartialOrd)] enum E { A = 2, B = 1} ``` Under the old deriving, `A < B` held, because `A` came before `B` in the order of declaration. But now we use the ordering according to the provided values, and thus `A > B`. (However, this change is very unlikely to break much, if any, code, since the orderings themselves should all remain well-defined, total, etc.) Fix #15523
💔 Test failed - auto-mac-32-opt |
🙀 |
Use `discriminant_value` intrinsic for `derive(PartialOrd)` [breaking-change] This is a [breaking-change] because it can change the result of comparison operators when enum discriminants have been explicitly assigned. Notably in a case like: ```rust #[derive(PartialOrd)] enum E { A = 2, B = 1} ``` Under the old deriving, `A < B` held, because `A` came before `B` in the order of declaration. But now we use the ordering according to the provided values, and thus `A > B`. (However, this change is very unlikely to break much, if any, code, since the orderings themselves should all remain well-defined, total, etc.) Fix #15523
r? @steveklabnik Should land this, #24270, and #24245 before rolling another beta.
No, at least the PartialEq issues are still a problem |
I am unable to reproduce issues with the quality of optimised LLVM IR code. The --pretty expanded output can still be very redundant, both for |
Consider this: #[derive(PartialEq)]
enum E {
A(i32),
B(i32),
C(i32),
D(i32),
} Ideally, eq would just do a single compare (on 64bit) or two compares (on 32bit). But instead we get this: _ZN23E...std..cmp..PartialEq2eq20hd7e6941040691683taaE:
.cfi_startproc
movl (%rdi), %eax
cmpl (%rsi), %eax
jne .LBB0_1
cmpl $1, %eax
je .LBB0_5
cmpl $2, %eax
je .LBB0_5
cmpl $3, %eax
.LBB0_5:
movl 4(%rdi), %eax
cmpl 4(%rsi), %eax
sete %al
retq
.LBB0_1:
xorl %eax, %eax
retq And if you add a few more variants, we end up with a jump table. |
AFAICT none of the issues I mentioned is about this kind of problem. It looks like you're suggesting that we should try to unify the code paths of all of the variants that share the same signature (or possibly even just the same signature prefix?). This is a (strong) generalisation of what I was suggesting, as nullary signatures are always matching. Several additional complications some to my mind, though, as this seems to interact with the memory layout of the #[derive(PartialEq)]
enum E {
A(i32),
B(isize),
} Should the Rust compiler be able to unify the comparison assuming that the on the target arch I believe that such optimisations might be more appropriate in LLVM than in the Rust compiler. As a matter of fact, although the LLVM IR code keeps them duplicated, upon translation to assembly LLVM is already able to unify the comparisons of the four cases. It is pretty weird that it cannot optimise the jump table correctly. Apparently it gets rid of the last Should a new issue be filed to track it or an existing one be updated to make this specific problem more prominent? |
#13623 was meant to be about all enums, not just C-like ones. I'll add the above example to it to clarify that. That LLVM should do most optimizations is right, but in some cases we should also generate code that is easier for LLVM to optimize. Also, while the final asm has the code unified, the IR is still large, which means more work for the optimizer and possibly missed opportunities for inlining. |
This is the same approach taken in rust-lang#24270, except that this should not be a breaking change because it only changes the output of hash functions, which nobody should be relying on.
This is the same approach taken in rust-lang#24270, except that this should not be a breaking change because it only changes the output of hash functions, which nobody should be relying on.
This is the same approach taken in rust-lang#24270, except that this should not be a breaking change because it only changes the output of hash functions, which nobody should be relying on.
This is the same approach taken in rust-lang#24270, except that this should not be a breaking change because it only changes the output of hash functions, which nobody should be relying on.
This is the same approach taken in rust-lang#24270, except that this should not be a breaking change because it only changes the output of hash functions, which nobody should be relying on.
Use
discriminant_value
intrinsic forderive(PartialOrd)
[breaking-change]
This is a [breaking-change] because it can change the result of comparison operators when enum discriminants have been explicitly assigned. Notably in a case like:
Under the old deriving,
A < B
held, becauseA
came beforeB
in the order of declaration. But now we use the ordering according to the provided values, and thusA > B
. (However, this change is very unlikely to break much, if any, code, since the orderings themselves should all remain well-defined, total, etc.)Fix #15523