diff --git a/compiler/src/dotty/tools/dotc/transform/ElimOpaque.scala b/compiler/src/dotty/tools/dotc/transform/ElimOpaque.scala index c64486ddf50b..ea5b53483e0f 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimOpaque.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimOpaque.scala @@ -1,4 +1,5 @@ -package dotty.tools.dotc +package dotty.tools +package dotc package transform import core._ @@ -12,6 +13,8 @@ import Denotations.{SingleDenotation, NonSymSingleDenotation} import SymDenotations.SymDenotation import DenotTransformers._ import TypeUtils._ +import Names._ +import ast.Trees._ object ElimOpaque { val name: String = "elimOpaque" @@ -19,6 +22,8 @@ object ElimOpaque { /** Rewrites opaque type aliases to normal alias types */ class ElimOpaque extends MiniPhase with DenotTransformer { + thisPhase => + import ast.tpd._ override def phaseName: String = ElimOpaque.name @@ -52,4 +57,19 @@ class ElimOpaque extends MiniPhase with DenotTransformer { ref } } + + /** Resolve overloading of `==` and `!=` methods with the representation + * types in order to avoid boxing. + */ + override def transformApply(tree: Apply)(using Context): Tree = + val sym = tree.symbol + if sym == defn.Any_== || sym == defn.Any_!= then + tree match + case Apply(Select(receiver, name: TermName), args) + if atPhase(thisPhase)(receiver.tpe.widen.typeSymbol.isOpaqueAlias) => + applyOverloaded(receiver, name, args, Nil, defn.BooleanType) + case _ => + tree + else + tree } \ No newline at end of file diff --git a/docs/docs/reference/other-new-features/opaques-details.md b/docs/docs/reference/other-new-features/opaques-details.md index 6d65f4aa7eee..e6021a4a3b9a 100644 --- a/docs/docs/reference/other-new-features/opaques-details.md +++ b/docs/docs/reference/other-new-features/opaques-details.md @@ -46,6 +46,21 @@ object o { def id(x: o.T): o.T = x ``` +### Translation of Equality + +Comparing two values of opaque type with `==` or `!=` normally uses universal equality, +unless another overloaded `==` or `!=` operator is defined for the type. To avoid +boxing, the operation is mapped after type checking to the (in-)equality operator +defined on the underlying type. For instance, +```scala + opaque type T = Int + + ... + val x: T + val y: T + x == y // uses Int equality for the comparison. +``` + ### Toplevel Opaque Types 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 diff --git a/tests/pos/opaque.scala b/tests/pos/opaque.scala index 7208d0b74889..7eda1f527585 100644 --- a/tests/pos/opaque.scala +++ b/tests/pos/opaque.scala @@ -35,6 +35,8 @@ object usesites { // as a contextual implicit this takes precedence over the // implicit scope implicit LogarithmOps. // TODO: Remove any2stringadd + assert(l == Logarithm(1.0)) + assert(l != l2) val d = l3.toDouble val l5: Logarithm = (1.0).asInstanceOf[Logarithm] }