From dc89ac4d802ec67af0f7baba89b37086763c78c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 31 May 2021 17:31:19 +0200 Subject: [PATCH] Fix #12572: Ignore default accessor bridges in non-native JS classes. They are not emitted, and they are excluded when looking up the default getter to call. --- .../dotty/tools/backend/sjs/JSCodeGen.scala | 9 ++++++++ .../tools/backend/sjs/JSExportsGen.scala | 2 +- .../compiler/RegressionTestScala3.scala | 23 +++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala index f11abc515e12..183b25213b4d 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala @@ -18,6 +18,7 @@ import Decorators._ import Flags._ import dotty.tools.dotc.ast.Trees._ import Names._ +import NameKinds.DefaultGetterName import Types._ import Symbols._ import Denotations._ @@ -1069,6 +1070,14 @@ class JSCodeGen()(using genCtx: Context) { } else if (sym.isJSNativeCtorDefaultParam) { // #11592 None + } else if (sym.is(Bridge) && sym.name.is(DefaultGetterName) && currentClassSym.isNonNativeJSClass) { + /* #12572 Bridges for default accessors in non-native JS classes must not be emitted, + * because they call another default accessor, making their entire body an + * that cannot be eliminated. + * Such methods are never called anyway, because they are filtered out in + * JSExportsGen.defaultGetterDenot(). + */ + None } else /*if (sym.isClassConstructor && isHijackedBoxedClass(sym.owner)) { None } else*/ { diff --git a/compiler/src/dotty/tools/backend/sjs/JSExportsGen.scala b/compiler/src/dotty/tools/backend/sjs/JSExportsGen.scala index eba914f1aa98..f52cf1d29d53 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSExportsGen.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSExportsGen.scala @@ -774,7 +774,7 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) { else sym.owner private def defaultGetterDenot(targetSym: Symbol, sym: Symbol, paramIndex: Int): Denotation = - targetSym.info.member(DefaultGetterName(sym.name.asTermName, paramIndex)) + targetSym.info.memberBasedOnFlags(DefaultGetterName(sym.name.asTermName, paramIndex), excluded = Bridge) private def defaultGetterDenot(sym: Symbol, paramIndex: Int): Denotation = defaultGetterDenot(targetSymForDefaultGetter(sym), sym, paramIndex) diff --git a/tests/sjs-junit/test/org/scalajs/testsuite/compiler/RegressionTestScala3.scala b/tests/sjs-junit/test/org/scalajs/testsuite/compiler/RegressionTestScala3.scala index e2ac600ae742..77a986470012 100644 --- a/tests/sjs-junit/test/org/scalajs/testsuite/compiler/RegressionTestScala3.scala +++ b/tests/sjs-junit/test/org/scalajs/testsuite/compiler/RegressionTestScala3.scala @@ -35,6 +35,10 @@ class RegressionTestScala3 { assertEquals(5, foo(5)(4)) } + + @Test def defaultAccessorBridgesIssue12572(): Unit = { + new MyPromiseIssue12572[Int](5) + } } object RegressionTestScala3 { @@ -53,6 +57,25 @@ object RegressionTestScala3 { class RangeErrorIssue11592(msg: String = js.native) extends js.Object { val message: String = js.native } + + class MyPromiseIssue12572[T](t: T) extends js.Promise[T]((resolve, reject) => resolve(t)) { + override def `then`[S]( + onFulfilled: js.Function1[T, S | js.Thenable[S]], + onRejected: js.UndefOr[js.Function1[scala.Any, S | js.Thenable[S]]] = js.undefined): js.Promise[S] = { + ??? + } + + override def `then`[S >: T]( + onFulfilled: Unit, + onRejected: js.UndefOr[js.Function1[scala.Any, S | js.Thenable[S]]]): js.Promise[S] = { + ??? + } + + override def `catch`[S >: T]( + onRejected: js.UndefOr[js.Function1[scala.Any, S | js.Thenable[S]]] = js.undefined): js.Promise[S] = { + ??? + } + } } // This class needs to be at the top-level, not in an object, to reproduce the issue