-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Closed
Description
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.
Metadata
Metadata
Assignees
Type
Projects
Milestone
Relationships
Development
Select code repository
Activity
sjrd commentedon May 25, 2025
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 commentedon May 25, 2025
If it is a value (not a type), the comparison is a match - this appears to be a JVM specification.
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 between0.0
and-0.0
either. My expectation is that comparisons and operations on literal types work the same as on values.sjrd commentedon May 25, 2025
+0.0
and-0.0
are definitely not indistinguishable. You can tell them apart with1.0/x
, for example. The fact that they compare==
does not mean they are the same value or that are indistinguishable.Conversely,
NaN == NaN
isfalse
, butNaN
is the same value asNaN
.In general,
==
cannot be trusted to answer the question "are the operands the same value/indistinguishable?".kijuky commentedon 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 commentedon May 25, 2025
Yes, that's what I meant in my first comment.
fix scala#23261 Handle ConstantType comparison for floating-point 0.0…
fix: scala#23261 Distinguish 0.0 and -0.0 in ConstantType match types
Fix #23261: Distinguish 0.0 and -0.0 in ConstantType match types (#23265