Skip to content

Match types do not treat 0.0d and -0.0d as equivalent constants #23261

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

Open
kijuky opened this issue May 25, 2025 · 5 comments · May be fixed by #23265
Open

Match types do not treat 0.0d and -0.0d as equivalent constants #23261

kijuky opened this issue May 25, 2025 · 5 comments · May be fixed by #23265

Comments

@kijuky
Copy link

kijuky commented May 25, 2025

Compiler version

Scala 3.7.0
(Confirmed with both sbt and Scala CLI)

Minimized code

type DoubleToString[D <: Double] <: String = D match
  case 0.0 => "0.0"
  case _ => "_"

@main def main(): Unit =
  summon[DoubleToString[0.0] =:= "0.0"]
  summon[DoubleToString[-0.0] =:= "0.0"]
  summon[DoubleToString[-0.0] =:= "_"]

Compilation output

[error] -- [E172] Type Error: src/main/scala/Main.scala:7:40 
[error] 7 |  summon[DoubleToString[-0.0] =:= "0.0"]
[error]   |                                        ^
[error]   |  Cannot prove that DoubleToString[(-0.0d : Double)] =:= ("0.0" : String).
[error]   |
[error]   |  Note: a match type could not be fully reduced:
[error]   |    trying to reduce  DoubleToString[(-0.0d : Double)]
[error]   |    failed since selector (-0.0d : Double)
[error]   |    is uninhabited (there are no values of that type).

[error] -- [E172] Type Error: src/main/scala/Main.scala:8:38 
[error] 8 |  summon[DoubleToString[-0.0] =:= "_"]
[error]   |                                      ^
[error]   |  Cannot prove that DoubleToString[(-0.0d : Double)] =:= ("_" : String).
[error]   |  Note: a match type could not be fully reduced:
[error]   |    trying to reduce  DoubleToString[(-0.0d : Double)]
[error]   |    failed since selector (-0.0d : Double)
[error]   |    is uninhabited (there are no values of that type).

Problem description

In Scala 3.7.0, match types cannot distinguish or unify 0.0 and -0.0, even though:

  • 0.0d == -0.0d holds at runtime
  • Using =:= on the type level compiles successfully: summon[0.0 =:= -0.0] works

However, in match types, writing case 0.0 => causes -0.0 to be rejected as an uninhabited selector.
Switching to case -0.0 => then rejects 0.0.

This suggests that match types are not treating 0.0d and -0.0d as equivalent constant values, which is inconsistent with how =:= treats them.

Expected behavior

Match types on Double constants should not distinguish 0.0 and -0.0, just as =:= does not.

@kijuky kijuky added itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label labels May 25, 2025
@sjrd
Copy link
Member

sjrd commented May 25, 2025

  • Using =:= on the type level compiles successfully: summon[0.0 =:= -0.0] works

Huh. To me, that is the real bug. +0.0 and -0.0 definitely are not the same value. They should not have the same literal type. For example, literal types can cause constant-folding. You don't want to constant-fold -0.0 into +0.0, since it can change the result of computations.

@kijuky
Copy link
Author

kijuky commented May 25, 2025

+0.0 and -0.0 definitely are not the same value.

If it is a value (not a type), the comparison is a match - this appears to be a JVM specification.

println(0.0 == -0.0) // => true

https://docs.oracle.com/javase/specs/jvms/se24/html/jvms-2.html#:~:text=Positive%20zero%20and%20negative%20zero%20compare%20equal

Since 0.0 and -0.0 as values ​​are indistinguishable, I think it would be better if literal types did not distinguish between 0.0 and -0.0 either. My expectation is that comparisons and operations on literal types work the same as on values.

@sjrd
Copy link
Member

sjrd commented May 25, 2025

+0.0 and -0.0 are definitely not indistinguishable. You can tell them apart with 1.0/x, for example. The fact that they compare == does not mean they are the same value or that are indistinguishable.

Conversely, NaN == NaN is false, but NaN is the same value as NaN.

In general, == cannot be trusted to answer the question "are the operands the same value/indistinguishable?".

@kijuky
Copy link
Author

kijuky commented May 25, 2025

As for 0, since values ​​do not distinguish between positive and negative, I thought it would be better to not distinguish between positive and negative in literal types as well, so that operations could be related to values ​​and types. In fact, positive and negative are not distinguished outside of match types.

However, does this mean that it is better to distinguish between 0.0 and -0.0 for literal types?

(Personally, regardless of the conclusion, I would like the behavior of match types to be the same as others. In other words, if we go with the current situation, I would not like the distinction between positive and negative. If we go with your proposal, I would like the distinction to be made in behavior other than match types, and I would like to be able to put positive and negative zeros in case clauses.)

@sjrd
Copy link
Member

sjrd commented May 25, 2025

However, does this mean that it is better to distinguish between 0.0 and -0.0 for literal types?

Yes, that's what I meant in my first comment.

kijuky added a commit to kijuky/scala3 that referenced this issue May 25, 2025
… with sign sensitivity

- Ensure match types distinguish between 0.0 and -0.0 in pattern matching.
- This enables accurate reduction in match types involving Double constants.
kijuky added a commit to kijuky/scala3 that referenced this issue May 26, 2025
- Ensure match types distinguish between 0.0 and -0.0 in pattern matching.
- This enables accurate reduction in match types involving Double constants.

Close scala#23261
@Gedochao Gedochao removed the stat:needs triage Every issue needs to have an "area" and "itype" label label May 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants