Skip to content

Commit

Permalink
Support Scala.js unions extra methods via implicit conversion
Browse files Browse the repository at this point in the history
The companion of `js.|` contains the following widely used implicit
conversion to a value class:

    /** Provides an [[Option]]-like API to [[js.UndefOr]]. */
    implicit def undefOr2ops[A](value: js.UndefOr[A]): js.UndefOrOps[A] =
      new js.UndefOrOps(value)

(where `UndefOr[A]` dealiases to `A | Unit`).

Since we re-interpret Scala.js unions as real unions, this companion is
not in the implicit scope of `A | Unit`, we work around this by
injecting a new `UnitOps` with the implicit conversion we want in the
implicit scope of `Unit` (we could have directly injected the object
`js.|`, but that contains other implicits we do not need).

This finally lets us compile and run the sjsJUnitTests after the
previous two commits.
  • Loading branch information
smarter committed Mar 16, 2021
1 parent de1b04e commit f55d869
Show file tree
Hide file tree
Showing 4 changed files with 20 additions and 1 deletion.
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ final class JSDefinitions()(using Context) {
@threadUnsafe lazy val PseudoUnion_fromTypeConstructorR = PseudoUnionModule.requiredMethodRef("fromTypeConstructor")
def PseudoUnion_fromTypeConstructor(using Context) = PseudoUnion_fromTypeConstructorR.symbol

@threadUnsafe lazy val UnionOpsModuleRef = requiredModuleRef("scala.scalajs.js.internal.UnitOps")

@threadUnsafe lazy val JSArrayType: TypeRef = requiredClassRef("scala.scalajs.js.Array")
def JSArrayClass(using Context) = JSArrayType.symbol.asClass
@threadUnsafe lazy val JSDynamicType: TypeRef = requiredClassRef("scala.scalajs.js.Dynamic")
Expand Down
9 changes: 9 additions & 0 deletions compiler/src/dotty/tools/dotc/typer/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package dotty.tools
package dotc
package typer

import backend.sjs.JSDefinitions
import core._
import ast.{Trees, TreeTypeMap, untpd, tpd, DesugarEnums}
import util.Spans._
Expand Down Expand Up @@ -634,6 +635,14 @@ trait ImplicitRunInfo:
else pre.member(sym.name.toTermName)
.suchThat(companion => companion.is(Module) && companion.owner == sym.owner)
.symbol)

// The companion of `js.|` defines an implicit conversions from
// `A | Unit` to `js.UndefOrOps[A]`. To keep this conversion in scope
// in Scala 3, where we re-interpret `js.|` as a real union, we inject
// it in the scope of `Unit`.
if t.isRef(defn.UnitClass) && ctx.settings.scalajs.value then
companions += JSDefinitions.jsdefn.UnionOpsModuleRef

if sym.isClass then
for p <- t.parents do companions ++= iscopeRefs(p)
else
Expand Down
8 changes: 8 additions & 0 deletions library-js/src/scala/scalajs/js/internal/UnitOps.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package scala.scalajs.js.internal

import scala.scalajs.js

/** Under -scalajs, this object is part of the implicit scope of `scala.Unit` */
object UnitOps:
implicit def unitOrOps[A](x: A | Unit): js.UndefOrOps[A] =
new js.UndefOrOps(x)
2 changes: 1 addition & 1 deletion project/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -698,7 +698,7 @@ object Build {
settings(
libraryDependencies +=
("org.scala-js" %% "scalajs-library" % scalaJSVersion).withDottyCompat(scalaVersion.value),
Compile / unmanagedSourceDirectories :=
Compile / unmanagedSourceDirectories ++=
(`scala3-library-bootstrapped` / Compile / unmanagedSourceDirectories).value,

// Configure the source maps to point to GitHub for releases
Expand Down

0 comments on commit f55d869

Please sign in to comment.