From 03a47b4b4eb8bcfc86960eecba6f11f0654ef128 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 24 Aug 2020 18:02:05 +0200 Subject: [PATCH 1/3] Scala.js: Fix a codegen bug for switch matches in statement position. The codegen chooses `jstpe.NoType` (`void`) over the declared type of a `Match` node. That is normally fine, except if the declared type was `Nothing`, in which case we must retain it, because the enclosing statement might be demanding an expression of type `Nothing`. --- compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala index cf010de61a8c..2067c62851c5 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala @@ -2277,9 +2277,11 @@ class JSCodeGen()(using genCtx: Context) { abortMatch(s"Invalid selector type ${genSelector.tpe}") } - val resultType = - if (isStat) jstpe.NoType - else toIRType(tree.tpe) + val resultType = toIRType(tree.tpe) match { + case jstpe.NothingType => jstpe.NothingType // must take priority over NoType below + case _ if isStat => jstpe.NoType + case resType => resType + } var clauses: List[(List[js.Tree], js.Tree)] = Nil var optDefaultClause: Option[js.Tree] = None From 29a956d94b6bb74ca6283d6aa3301d8e5478c8aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 24 Aug 2020 18:13:15 +0200 Subject: [PATCH 2/3] Scala.js: Throw an NPE if the receiver of a `synchronized` is null. --- compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala index 2067c62851c5..546366482e58 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala @@ -1976,10 +1976,11 @@ class JSCodeGen()(using genCtx: Context) { genArg case _ => implicit val pos = tree.span - /* TODO Check for a null receiver? - * In theory, it's UB, but that decision should be left for link time. - */ - js.Block(genReceiver, genArg) + js.Block( + js.If(js.BinaryOp(js.BinaryOp.===, genReceiver, js.Null()), + js.Throw(js.New(NullPointerExceptionClass, js.MethodIdent(jsNames.NoArgConstructorName), Nil)), + js.Skip())(jstpe.NoType), + genArg) } } @@ -3472,6 +3473,7 @@ class JSCodeGen()(using genCtx: Context) { object JSCodeGen { + private val NullPointerExceptionClass = ClassName("java.lang.NullPointerException") private val JSObjectClassName = ClassName("scala.scalajs.js.Object") private val newSimpleMethodName = SimpleMethodName("new") From 8016ed172dbda5da0a1cf2cce80c9908c2f51b09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 24 Aug 2020 18:23:55 +0200 Subject: [PATCH 3/3] Scala.js: Fix `Any.==` for `j.l.{Float,Double}`. This is a port of https://github.com/scala-js/scala-js/commit/b7f3e23a832accbc78c98dbebf97b36b588bf23c in Scala.js upstream, which was itself a port to the JS back-end of https://github.com/scala/scala/commit/6abb6ba47ad7a111385d1b4f5d2a90d81ee4472a in the Scala 2 JVM back-end. Together with the two parent commits, this fixes the `RegressionTest.scala` test suite, which we now enable. --- compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala | 10 +++++++--- project/Build.scala | 1 - 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala index 546366482e58..59e97f92cf0f 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala @@ -1838,13 +1838,17 @@ class JSCodeGen()(using genCtx: Context) { * **which includes when either is a JS type**. * When it is statically known that both sides are equal and subtypes of * Number or Character, not using the rich equality is possible (their - * own equals method will do ok.) + * own equals method will do ok), except for java.lang.Float and + * java.lang.Double: their `equals` have different behavior around `NaN` + * and `-0.0`, see Javadoc (scala-dev#329, scala-js#2799). */ val mustUseAnyComparator: Boolean = { isJSType(lsym) || isJSType(rsym) || { val p = ctx.platform - val areSameFinals = lsym.is(Final) && rsym.is(Final) && (ltpe =:= rtpe) - !areSameFinals && p.isMaybeBoxed(lsym) && p.isMaybeBoxed(rsym) + p.isMaybeBoxed(lsym) && p.isMaybeBoxed(rsym) && { + val areSameFinals = lsym.is(Final) && rsym.is(Final) && (ltpe =:= rtpe) + !areSameFinals || lsym == defn.BoxedFloatClass || lsym == defn.BoxedDoubleClass + } } } diff --git a/project/Build.scala b/project/Build.scala index feb14165f090..2e6e7a242389 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1021,7 +1021,6 @@ object Build { val dir = fetchScalaJSSource.value / "test-suite" ( (dir / "shared/src/test/scala" ** (("*.scala": FileFilter) - -- "RegressionTest.scala" // IR checking errors -- "ReflectiveCallTest.scala" // uses many forms of structural calls that are not allowed in Scala 3 anymore -- "EnumerationTest.scala" // scala.Enumeration support for Scala.js is not implemented in dotc (yet) -- "SymbolTest.scala" // uses the old literal symbol syntax, pending update upstream