-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Matching on floating-point literal values is totally allowed and shouldn't be #41255
Comments
There seem to be three options:
idk maybe a crater run should be done to find out what is best. |
@est31 indeed, a crater run seems warranted. |
Nominating for compiler team discussion on best way to proceed and prioritization. Seems to me we ought to settle this pretty quickly one way or another. |
triage: P-high This is time-sensitive, so we gotta act if we are ever going to. Assigning to @pnkfelix. |
I've filed two PRs #41292 for removing the use of a floating point match from the compiler and #41293 for a crater run to test the most strict solution -- to add it to the existing lint without changing the deny-by-default status. Independent from whether that strategy will be chosen in the end, it gives us an initial number of crates that would be affected by the change. #41293 includes #41292 already to make a crater run possible without #41292 being merged. |
Avoid to use floating point match Its going to be forbidden, see issue rust-lang#41255.
This is arguable. Floats are built-in types known to the compiler, you don't have to run any user-defined code to match on them. Another analogy is that float literals are the same thing as variants for non- match xxx {
NON_STRUCTURAL_MATCH_CONST => {} // Not OK, constant
NonStructuralMatch { field } => {} // OK, variant
FLOAT_CONST => {} // Not OK, constant
1.0 => {} // OK, variant
} |
The number of regressions in #41293 is also relatively large. |
As I wrote on the comment, I too am second-guessing this rule. I think at this point I'm inclined to say that matching on floats uses |
While this is true, most (all?) of the time there is some visual hint that the compiler may insert extra logic. E.g. if I see a |
@aidanhs we'd also have to settle what NaN ought to do (would it ever be a match?). My inclination would be to make floating point matches align with what |
If we had to choose, personally I'd prefer NaN to be matched! My current intuition is that a pattern match is quite distinct from Of course, there are some annoyances since
But...is there actually any need to make a decision for NaN? There's no NaN literal, so there's no way to write it in a pattern in a way that rustc accepts (I think? It's not even rejected under the lint, it's rejected as an irrefutable pattern). And if you can't write it on the left, it doesn't matter if it's ever on the right. Continuing a compile-time error that already exists is definitely my favourite approach for NaN. |
If you allow the lint, this compiles: https://is.gd/U8BM1Y |
I'd prefer to accept both named constants and constant patterns in a consistent way, myself. I also agree that NaN should match NaN (in general, I think that if you can do both UPDATE: (But I know that nullary enum variants do not necessarily behave this way -- I rationalize this by the idea that matching a variant is distinct from matching a constant, but elsewhere we don't draw that distinction.) |
So @aidanhs tactfully pointed out to me on IRC that I wrote two contradictory things:
and
To be honest, I'm not sure which of these values I hold most dearly anymore! I do have the notion that pattern matching (for enums, as you say) is doing "variant testing", which is a distinct operation from And the bigger question here is when (if ever) we would allow matching constants of generic type (which will start to arise when we get to functions that can be generic over constants). I'd like to allow for that future, which seems to imply that one of three things is true:
So, unless I'm missing something, we basically have to choose between:
|
@eddyb @withoutboats -- I'd appreciate your thoughts on this comment, and in particular about the implications for unification. For context, we're talking about the meaning of floating point constants in match, but I'm bringing in more general questions of what it means to have a constant as a pattern anyhow. In any case, I suppose "no constants of generic type in match" is not the end of the world. After all, one can write the relevant code using pattern guards. |
We could make that trait about pattern-matching instead of "equality" (i.e. if we ever add custom patterns, it could be through that trait), and while it might be customizable, it couldn't escape our restrictions on pattern-matching. E.g. maybe terrible, but: trait Inject<T...; const IN: T> {
const VALUE: Self;
}
struct Point { x: i32, y: i32 }
impl<const x: i32, const y: i32> Inject<i32, i32; {(x, y)}> for Point {
const VALUE = Point { x, y };
} This looks worse than refinement typing, but it probably has some usecases. |
I suppose that is another way of looking at it -- that is, instead of "another kind of equality", what we have instead is a trait that defines what pattern matching a value means. This could be more than marker trait -- that is, it could open the door to overloaded pattern matching, which would be potentially quite cool. (This all works better, of course, if pattern variants have their own types.) I could get behind that story, though we'd have to have some sort of "fallback" behavior -- or default impl -- since we currently permit pattern matching for things that derive |
I expect the automatic implementation to hook into |
I think I'd prefer to just disallow float literals in patterns. |
Nominating for lang team discussion today. |
We discussed this in the @rust-lang/lang team meeting. There was some disagreement about the best path forward, but we agreed that as a compromise we could start issuing warnings (not deny by default) for floating point literals, and see where that takes us. That was the RFC that we merged, after all. |
Sorry, I didn't give many details from our discussion (which actually took quite some time). I don't think we really covered any new ground that's not already in this discussion thread, though. |
…omatsakis Implement the illegal_floating_point_literal_pattern compat lint Adds a future-compatibility lint for the [breaking-change] introduced by issue rust-lang#41620 . cc issue rust-lang#41255 .
Was a crater run made, in the end? This breaks Servo, which unfortunately is not part of Crater. |
Do you mean "this would break servo if it became a hard error"? (But we should take it to #41620 in any case.) |
Yes, I think this particular crate has |
Refiling as T-compiler; what's left here is to move to a hard error. The warning went out in 1.18. |
triage: P-medium |
Heads up: #46882 removes the hard error for matching on float constants and produces the same future compat warning. |
I'm going to close this in favor of the tracking issue for the lint, since it seems like that's where discussion should move. |
Per this comment on the tracking issue for disallowing the use of constants set to floating point numbers (which errors as expected today), @nikomatsakis says the intention was to disallow floating point literal values in matches, but uh... this is totally accepted by Rust 1.16.0:
This prints "thirteen point four". I expected to get a compiler error like the one I get if I change a floating point value in the match to a constant:
results in:
The text was updated successfully, but these errors were encountered: