Skip to content

SortedMap#empty Fails At Runtime With NoSuchMethodError #9175

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

Closed
adamgfraser opened this issue Jun 12, 2020 · 4 comments
Closed

SortedMap#empty Fails At Runtime With NoSuchMethodError #9175

adamgfraser opened this issue Jun 12, 2020 · 4 comments

Comments

@adamgfraser
Copy link
Contributor

Minimized code

import scala.collection.immutable.SortedMap

object Example extends App {
  
  val sortedMap = SortedMap("a" -> 1, "b" -> 2, "c" -> 3)
  val empty     = sortedMap.empty
  println(empty)
}

Output

java.lang.NoSuchMethodError: scala.collection.immutable.SortedMap.empty()Lscala/collection/Map;
	at Example$.<init>(main.scala:6)
	at Example$.<clinit>(main.scala)
	at Example.main(main.scala)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at sbt.Run.invokeMain(Run.scala:115)
	at sbt.Run.execute$1(Run.scala:79)
	at sbt.Run.$anonfun$runWithLoader$4(Run.scala:92)
	at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
	at sbt.util.InterfaceUtil$$anon$1.get(InterfaceUtil.scala:10)
	at sbt.TrapExit$App.run(TrapExit.scala:257)
	at java.lang.Thread.run(Thread.java:748)

Expectation

I would expect that since this method is in the API of the latest Scala collections it would work correctly. At the minimum if it did not work I would expect to get a compilation failure with a helpful error message.

@smarter
Copy link
Member

smarter commented Jun 12, 2020

java.lang.NoSuchMethodError: scala.collection.immutable.SortedMap.empty()Lscala/collection/Map;

The correct signature appears to be empty()Lscala/collection/SortedMapOps;

The empty method in question is defined in SortedMapFactoryDefaults:

override def empty: CC[K, V @uncheckedVariance] = sortedMapFactory.empty(ordering)

where CC is defined as:

CC[x, y] <:  Map[x, y] with SortedMapOps[x, y, CC, CC[x, y]] with UnsortedCC[x, y]

Dotty and Scala 2 do not erase intersection types in the same way (scala 2 does some really weird stuff), I'm working on a PR to take that into account when calling into scala 2 code which should hopefully fix that.

@adamgfraser
Copy link
Contributor Author

Thanks! For what it is worth I also tried to use parentheses like empty() but that resulted in a compilation error missing argument for parameter key of method apply: (key: String): Int @uncheckedVariance. So there doesn't seem to be a way to call this method from Dotty right now.

@smarter
Copy link
Member

smarter commented Jun 12, 2020

Indeed, but you should be able to call SortedMap.empty[String, Int]

@adamgfraser
Copy link
Contributor Author

Yes but if I am working with polymorphic types I may not have an implicit Ordering in scope and need to reuse whatever Ordering is underlying the original map which is why I want to call sortedMap.empty as opposed to SortedMap.empty.

smarter added a commit to dotty-staging/dotty that referenced this issue Mar 4, 2021
Because our algorithm for erasing intersection types does not exactly
match the one used by Scala 2, we could end up emitting calls to Scala 2
methods with the wrong bytecode signature, leading to NoSuchMethodError
at runtime. We could try to exactly match what Scala 2 does, but it
turns out that the Scala 2 logic heavily relies on implementation
details which makes it extremely complex to reliably replicate.
Therefore, this commit instead special-cases the erasure of Scala 2
intersections (just like we already special-case the erasure of Java
intersections) and limits which Scala 2 intersection types we support to
a subset that we can erase without too much complications (but even that
still requires ~200 lines of code!). This means that we're now free to
change the way we erase intersections in any way we want without
introducing more compatibility problems (until 3.0.0 that is), I'll
explore this in a follow-up PR.

Fixes scala#4619. Fixes scala#9175.
smarter added a commit to dotty-staging/dotty that referenced this issue Mar 4, 2021
Because our algorithm for erasing intersection types does not exactly
match the one used by Scala 2, we could end up emitting calls to Scala 2
methods with the wrong bytecode signature, leading to NoSuchMethodError
at runtime. We could try to exactly match what Scala 2 does, but it
turns out that the Scala 2 logic heavily relies on implementation
details which makes it extremely complex to reliably replicate.
Therefore, this commit instead special-cases the erasure of Scala 2
intersections (just like we already special-case the erasure of Java
intersections) and limits which Scala 2 intersection types we support to
a subset that we can erase without too much complications (but even that
still requires ~200 lines of code!). This means that we're now free to
change the way we erase intersections in any way we want without
introducing more compatibility problems (until 3.0.0 that is), I'll
explore this in a follow-up PR.

Fixes scala#4619. Fixes scala#9175.
@smarter smarter closed this as completed in e4af70b Mar 9, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants