Skip to content

Commit 423bcd2

Browse files
authored
Merge pull request #9583 from dotty-staging/fix-opaque-eq
Re-resolve ==, != after expanding opaque types
2 parents 52923b0 + 55c70d6 commit 423bcd2

File tree

3 files changed

+38
-1
lines changed

3 files changed

+38
-1
lines changed

compiler/src/dotty/tools/dotc/transform/ElimOpaque.scala

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
package dotty.tools.dotc
1+
package dotty.tools
2+
package dotc
23
package transform
34

45
import core._
@@ -12,13 +13,17 @@ import Denotations.{SingleDenotation, NonSymSingleDenotation}
1213
import SymDenotations.SymDenotation
1314
import DenotTransformers._
1415
import TypeUtils._
16+
import Names._
17+
import ast.Trees._
1518

1619
object ElimOpaque {
1720
val name: String = "elimOpaque"
1821
}
1922

2023
/** Rewrites opaque type aliases to normal alias types */
2124
class ElimOpaque extends MiniPhase with DenotTransformer {
25+
thisPhase =>
26+
import ast.tpd._
2227

2328
override def phaseName: String = ElimOpaque.name
2429

@@ -52,4 +57,19 @@ class ElimOpaque extends MiniPhase with DenotTransformer {
5257
ref
5358
}
5459
}
60+
61+
/** Resolve overloading of `==` and `!=` methods with the representation
62+
* types in order to avoid boxing.
63+
*/
64+
override def transformApply(tree: Apply)(using Context): Tree =
65+
val sym = tree.symbol
66+
if sym == defn.Any_== || sym == defn.Any_!= then
67+
tree match
68+
case Apply(Select(receiver, name: TermName), args)
69+
if atPhase(thisPhase)(receiver.tpe.widen.typeSymbol.isOpaqueAlias) =>
70+
applyOverloaded(receiver, name, args, Nil, defn.BooleanType)
71+
case _ =>
72+
tree
73+
else
74+
tree
5575
}

docs/docs/reference/other-new-features/opaques-details.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,21 @@ object o {
4646
def id(x: o.T): o.T = x
4747
```
4848

49+
### Translation of Equality
50+
51+
Comparing two values of opaque type with `==` or `!=` normally uses universal equality,
52+
unless another overloaded `==` or `!=` operator is defined for the type. To avoid
53+
boxing, the operation is mapped after type checking to the (in-)equality operator
54+
defined on the underlying type. For instance,
55+
```scala
56+
opaque type T = Int
57+
58+
...
59+
val x: T
60+
val y: T
61+
x == y // uses Int equality for the comparison.
62+
```
63+
4964
### Toplevel Opaque Types
5065

5166
An opaque type alias on the toplevel is transparent in all other toplevel definitions in the sourcefile where it appears, but is opaque in nested

tests/pos/opaque.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ object usesites {
3535
// as a contextual implicit this takes precedence over the
3636
// implicit scope implicit LogarithmOps.
3737
// TODO: Remove any2stringadd
38+
assert(l == Logarithm(1.0))
39+
assert(l != l2)
3840
val d = l3.toDouble
3941
val l5: Logarithm = (1.0).asInstanceOf[Logarithm]
4042
}

0 commit comments

Comments
 (0)