-
Notifications
You must be signed in to change notification settings - Fork 1.1k
linking error to java's Consumer.accept when using Scala.js #16065
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I can reproduce on the JVM with the following minimization: trait MyConsumer[T] {
var x: Int = 1
def accept(x: T): Unit
}
object Test {
def main(args: Array[String]): Unit = {
val c: MyConsumer[_ >: String] = x => ()
c.accept("foo")
}
} Mandatory ingredients:
The workaround from the original post works because it uses an expected type of Showing the trees before and after erasure definitely paints erasure as the culprit: [[syntax trees at end of MegaPhase{pruneErasedDefs, uninitialized, inlinePatterns, vcInlineMethods, seqLiterals, intercepted, getters, specializeFunctions, specializeTuples, liftTry, collectNullableFields, elimOuterSelect, resolveSuper, functionXXLForwarders, paramForwarding, genericTuples, letOverApply, arrayConstructors}]] // tests\run\hello.scala
package <empty> {
@SourceFile("tests/run/hello.scala") trait MyConsumer[T]() extends Object {
private type T
def x: Int = 1
def x_=(x$1: Int): Unit = ()
def accept(x: T): Unit
}
final lazy module val Test: Test = new Test()
@SourceFile("tests/run/hello.scala") final module class Test() extends Object(
)
{
private def writeReplace(): AnyRef =
new scala.runtime.ModuleSerializationProxy(classOf[Test.type])
def main(args: Array[String]): Unit =
{
val c: MyConsumer[? >: String] =
{
def $anonfun(x: String): Unit = ()
{
final class $anon() extends Object(), MyConsumer[? >: String] {
final def accept(x: String): Unit = $anonfun(x)
}
new Object with MyConsumer[? >: String] {...}()
}
}
c.accept("foo")
}
}
}
[[syntax trees at end of erasure]] // tests\run\hello.scala
package <empty> {
@SourceFile("tests/run/hello.scala") trait MyConsumer() extends Object {
def x(): Int = 1
def x_=(x$1: Int): Unit = ()
def accept(x: Object): Unit
}
final lazy module val Test: Test = new Test()
@SourceFile("tests/run/hello.scala") final module class Test() extends Object(
)
{
private def writeReplace(): Object =
new scala.runtime.ModuleSerializationProxy(classOf[Test])
def main(args: String[]): Unit =
{
val c: MyConsumer =
{
def $anonfun(x: String): Unit = ()
{
final class $anon() extends Object(), MyConsumer {
final def accept(x: String): Unit = $anonfun(x)
}
new Object with MyConsumer {...}():MyConsumer
}
}
c.accept("foo")
}
}
} Removing the So this is not a Scala.js issue, but an erasure issue. |
Investigating further, it's not Erasure's fault either. It's either Typer or ExpandSAMs, depending on what are the invariants we want. After ExpandSAMs, the tree for the closure is: val c: MyConsumer[? >: String] =
{
def $anonfun(x: String): Unit = println(x)
{
final class $anon() extends Object(), MyConsumer[? >: String] {
final def accept(x: String): Unit = $anonfun(x)
}
new Object with MyConsumer[? >: String] {...}()
}
} which is wrong: it has
So why does val c: MyConsumer[? >: String <: Any] =
{
def $anonfun(x: String): Unit = println(x)
closure($anonfun:MyConsumer[? >: String])
} The I suspect this portion of the typer:
Allowing wildcards for We might think that we should reject non-LMF-capable things there instead of just So the solution is not to reject At this point, this issue exceeds my area of expertise, so I will throw it back to someone typer-savvy. To spare you the trouble, here are test cases: // i16065.scala
abstract class MyClassConsumer[T] {
def accept(x: T): Unit
}
trait MyTraitConsumer[T] {
var x: Int = 1
def accept(x: T): Unit
}
abstract class MyClassProducer[T] {
def produce(): T
}
trait MyTraitProducer[T] {
var x: Int = 1
def produce(): T
}
object Test {
def main(args: Array[String]): Unit = {
val c1: MyClassConsumer[_ >: String] = x => println(x)
c1.accept("MyClassConsumer")
val c2: MyTraitConsumer[_ >: String] = x => println(x)
c2.accept("MyTraitConsumer")
val p1: MyClassProducer[_ <: String] = () => "MyClassProducer"
println(p1.produce())
val p2: MyTraitProducer[_ <: String] = () => "MyTraitProducer"
println(p2.produce())
}
}
|
Thanks for the minimization and investigation, another piece of the puzzle here is the SAMType extractor which is where we strip the wildcards from the method type we use to represent the closure: |
Is it OK to assign it to you @smarter? |
Sure. |
This came back again as #16388. |
When typing a closure with an expected type containing a wildcard, the closure type itself should not contain wildcards, because it might be expanded to an anonymous class extending the closure type (this happens on non-JVM backends as well as on the JVM itself in situations where a SAM trait does not compile down to a SAM interface). We were already approximating wildcards in the method type returned by the SAMType extractor, but to fix this issue we had to change the extractor to perform the approximation on the expected type itself to generate a valid parent type. The SAMType extractor now returns both the approximated parent type and the type of the method itself. The wildcard approximation analysis relies on a new `VarianceMap` opaque type extracted from Inferencing#variances. Fixes scala#16065. Fixes scala#18096.
When typing a closure with an expected type containing a wildcard, the closure type itself should not contain wildcards, because it might be expanded to an anonymous class extending the closure type (this happens on non-JVM backends as well as on the JVM itself in situations where a SAM trait does not compile down to a SAM interface). We were already approximating wildcards in the method type returned by the SAMType extractor, but to fix this issue we had to change the extractor to perform the approximation on the expected type itself to generate a valid parent type. The SAMType extractor now returns both the approximated parent type and the type of the method itself. The wildcard approximation analysis relies on a new `VarianceMap` opaque type extracted from Inferencing#variances. Fixes scala#16065. Fixes scala#18096.
When typing a closure with an expected type containing a wildcard, the closure type itself should not contain wildcards, because it might be expanded to an anonymous class extending the closure type (this happens on non-JVM backends as well as on the JVM itself in situations where a SAM trait does not compile down to a SAM interface). We were already approximating wildcards in the method type returned by the SAMType extractor, but to fix this issue we had to change the extractor to perform the approximation on the expected type itself to generate a valid parent type. The SAMType extractor now returns both the approximated parent type and the type of the method itself. The wildcard approximation analysis relies on a new `VarianceMap` opaque type extracted from Inferencing#variances. Fixes scala#16065. Fixes scala#18096.
When typing a closure with an expected type containing a wildcard, the closure type itself should not contain wildcards, because it might be expanded to an anonymous class extending the closure type (this happens on non-JVM backends as well as on the JVM itself in situations where a SAM trait does not compile down to a SAM interface). We were already approximating wildcards in the method type returned by the SAMType extractor, but to fix this issue we had to change the extractor to perform the approximation on the expected type itself to generate a valid parent type. The SAMType extractor now returns both the approximated parent type and the type of the method itself. The wildcard approximation analysis relies on a new `VarianceMap` opaque type extracted from Inferencing#variances. Fixes scala#16065. Fixes scala#18096.
When typing a closure with an expected type containing a wildcard, the closure type itself should not contain wildcards, because it might be expanded to an anonymous class extending the closure type (this happens on non-JVM backends as well as on the JVM itself in situations where a SAM trait does not compile down to a SAM interface). We were already approximating wildcards in the method type returned by the SAMType extractor, but to fix this issue we had to change the extractor to perform the approximation on the expected type itself to generate a valid parent type. The SAMType extractor now returns both the approximated parent type and the type of the method itself. The wildcard approximation analysis relies on a new `VarianceMap` opaque type extracted from Inferencing#variances. Fixes #16065. Fixes #18096.
[Cherry-picked 89735d0][modified] When typing a closure with an expected type containing a wildcard, the closure type itself should not contain wildcards, because it might be expanded to an anonymous class extending the closure type (this happens on non-JVM backends as well as on the JVM itself in situations where a SAM trait does not compile down to a SAM interface). We were already approximating wildcards in the method type returned by the SAMType extractor, but to fix this issue we had to change the extractor to perform the approximation on the expected type itself to generate a valid parent type. The SAMType extractor now returns both the approximated parent type and the type of the method itself. The wildcard approximation analysis relies on a new `VarianceMap` opaque type extracted from Inferencing#variances. Fixes #16065. Fixes #18096.
In scala3/js (scala 3.2.0, scalajs 1.11.0), but not in scala2/js, there is linking error "Referring to non-existent method ....accept(java.lang.Object)void" in the following:
I can workaround it like this in foo:
There is "(modName / Compile / fastLinkJS) There were linking errors". Couldn't reproduce on the JVM.
Sorry if reporting to wrong repo, scalajs says if it is only on 3.x, we should report here.
The text was updated successfully, but these errors were encountered: