linking error to java's Consumer.accept when using Scala.js
See original GitHub issueIn 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:
def foo(): Unit = {
val set = new util.LinkedHashSet[String]
set.forEach(bar)
}
def bar(v: String): Unit = ()
I can workaround it like this in foo:
val barc: util.function.Consumer[String] = bar
set.forEach(barc)
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.
Issue Analytics
- State:
- Created a year ago
- Comments:5 (5 by maintainers)
Top Results From Across the Web
Linking Errors - Scala.js
A common cause for linking errors is to use %% instead of %%% when depending on another Scala.js library. Having the JVM version...
Read more >Linker error in ScalaJS: "Referring to non-existent class"
The problem was that I was requiring the JVM version of FastParse two lines up in my build.sbt . This broke my build...
Read more >Linking error with Scala.js 1.0 ...
You can see in project/Build.scala that the gh-pages modules has ... Linking error with Scala.js 1.0 & scalaJSUseMainModuleInitializer #4030.
Read more >Scala Native Documentation
This documentation is divided into different parts. It's recommended to go through the User's Guide to get familiar with Scala Native.
Read more >The importance of Scala.js
Why would a frontend developer take on Scala.js when they probably ... package my app with Java and then upload binaries for various...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
I can reproduce on the JVM with the following minimization:
Mandatory ingredients:
var x: Int = 1
definition makes the test pass.MyConsumer[_ >: String]
-> usingMyConsumer[String]
instead makes the test passThe workaround from the original post works because it uses an expected type of
Consumer[String]
, notConsumer[_ >: String]
.Showing the trees before and after erasure definitely paints erasure as the culprit:
Removing the
_ >:
causes erasure to correctly add a bridge foraccept(x: Object): Unit
.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:
which is wrong: it has
MyConsumer[? >: String]
in theextends
clause, butaccept
takes aString
. In fact, if we write this by hand,RefChecks
complains:So why does
ExpandSAMs
generate that? Well because already after Typer, the tree is dubious:The
closure
node already mentionsMyConsumer[? >: String]
, but points to$anonfun(String)
.I suspect this portion of the typer: https://github.com/lampepfl/dotty/blob/c3ba2f4bf137e6834f081aaf9927e1ffe9a48e13/compiler/src/dotty/tools/dotc/typer/Typer.scala#L1517-L1531 In particular, note that with
isWildcardClassSAM
it rejects a?
ifMyConsumer
is aclass
, but explicitly allows atrait
. Sure enough, if we defineMyConsumer
as a class, we get:Allowing wildcards for
trait
s was most likely done to support Java-style closures, which use use-site variance everywhere. And rejectingclass
es was most likely done as a proxy to detecting things that would not beLambdaMetaFactory
-capable. For LMF stuff, it “works out”, because bridges are taken care of by the backend, who doesn’t care about wildcards. But clearly, that proxy is not good enough, since it allows theMyConsumer
in my repro.We might think that we should reject non-LMF-capable things there instead of just
class
es, but that would mean that what SAMs are actually language-level SAMs becomes platform-dependent, and that’s very, very bad.So the solution is not to reject
class
es, and instead correctly get rid of such wildcards by choosing the appropriate bound. Upper bound if it is used in covariant position in the SAM type, or Lower bound in contravariant. If used in both, the code must be rejected.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: