ScalaJS fullLinkJS errors

197 views Asked by At

I upgraded my Play+ScalaJS project from ScalaJS 0.6.32 to 1.4.0 several weeks ago. All has been working fine in development. Today I tried to deploy it and thus executed fullLinkJS for the first time. It generated a number of errors:

sbt:browser> fullLinkJS
[info] Full optimizing /Users/bwbecker/oat/src/oat3/_browser/target/scala-2.12/browser-opt
[error] c91662c1ae832d6a8493/oat/browser/views/bulkmail/BMailCreateView.scala(125:43:Apply): scala.Dynamic expected but any found for tree of type org.scalajs.ir.Trees$JSSelect
[error] c91662c1ae832d6a8493/oat/browser/views/components/filteredTable/FilteredBMailTable.scala(81:53:Apply): scala.Dynamic expected but any found for tree of type org.scalajs.ir.Trees$JSSelect
[error] c91662c1ae832d6a8493/oat/browser/models/Autocomplete.scala(156:18:Apply): scala.Dynamic expected but any found for tree of type org.scalajs.ir.Trees$JSSelect
[error] c91662c1ae832d6a8493/oat/browser/views/virtualq/QueueAddEditView.scala(90:52:Apply): scala.Dynamic expected but any found for tree of type org.scalajs.ir.Trees$JSSelect
[error] c91662c1ae832d6a8493/oat/browser/views/bulkmail/edit/BMailSendView.scala(273:62:Apply): scala.Dynamic expected but any found for tree of type org.scalajs.ir.Trees$JSSelect
[error] c91662c1ae832d6a8493/oat/browser/views/bulkmail/TemplateListView.scala(42:64:Apply): scala.Dynamic expected but any found for tree of type org.scalajs.ir.Trees$JSSelect
...

I've looked at about a dozen of the referenced lines in the source code. Most of them refer to either a Play route or invoke the url method. The rest of them involve a call to jQuery.

The project has a cross-platform subproject. That one does the fullLinkJS just fine. There's also a client-specific ScalaJS project. That's where the errors are happening.

I'm using ScalaJS 1.4.0 and Play 2.6.25.

Suggestions on how to procede?

Later, after reading reply from @sjrd

A sample of the offending code:

private val wapp = routes.oat.wapp.controllers.BMailCtrl
...
window.location.href = wapp.edit(emailId).url // this is the line flagged with the error

window is defined in org.scalajs.dom as a js.Dynamic.

routes is defined in my code as val routes: js.Dynamic = global.jsRoutes. The js.Dynamic type annotation is what is inferred by IntelliJ.

Aha! global is defined in scala.scalajs.js as extending scala.Dynamic. But I believe the jsRoutes should be interpreted as a js.Dynamic. It's the routes produced by the Play server.

It looks like I need to change how I access those guys. Researching, particularly, Scala.js GlobalScope.

Still Later -- the fix

I'm recording what I did here so I can mark sjrd's answer as "the answer".

As noted, most of my IR validation errors were on lines of the form

window.location.href = wapp.edit(emailId).url

href has a type of String and url is js.Dynamic but produces a string. I think therein lies the problem.

The fix, however, was simple: add toString:

window.location.href = wapp.edit(emailId).url.toString

All the other IR validation errors were variants of this with a similar fix.

1

There are 1 answers

4
sjrd On BEST ANSWER

The errors you see are "IR checking errors". They mean that there are inconsistencies in the .sjsir files that are produced by the compiler. By default, they are only checked for fullOpt tasks, which is why you have not seen them before. However, that does not mean the IR is any more valid for fastOpt (it's the same IR!), so the error was always there (latent), but only surfaces now. Having IR checking errors means that the optimizer, or simply the JavaScript code generator, can produce nonsensical results.

(IR checking errors are also similar to VerifyErrors on the JVM.

Broadly speaking, there are 3 sources of IR checking errors:

  • Using binary incompatible versions of transitive libraries can cause IR checking errors in some rare cases, even when no linking error arises.
  • Bugs in the Scala.js core toolchain (compiler, linker)
  • A particular known (and somewhat intentional) unsoundness issue in the Scala.js type system: js.Dynamic <: scala.Dynamic causes unsoundness in the IR.

What you are experiencing is the latter issue. It is quite rare. Basically, it happens if you assign (through = or passing a function argument, etc.) a value of type js.Dynamic to a value of type scala.Dynamic. For example:

val jsDyn = js.Dynamic.literal(foo = "bar")
val scalaDyn: scala.Dynamic = jsDyn

The type checker accepts that snippet because, at compile-time, js.Dynamic extends scala.Dynamic. This is necessary for js.Dynamic to expose its dynamism in the first place, so we cannot change that. However, at link- and run-time, js.Dynamic is a JavaScript type (because it extends js.Any) and scala.Dynamic is a Scala type, and JavaScript types are never subtypes of Scala types.


So where does that leave you?

Look at the code pointed by the error messages. You will need to identify a place where a js.Dynamic (or any other JS type that extends scala.Dynamic, but js.Dynamic is typically the only one like that) is assigned to a scala.Dynamic.

It is extremely rare to write scala.Dynamic (or just Dynamic) as a type by hand (and doing so is probably always a mistake), but it could be the case that it is inferred by the compiler, for example if you have two branches of an if/else that return two different subtypes of scala.Dynamic.

This is all I can say without seeing the problematic code snippet.