-
Notifications
You must be signed in to change notification settings - Fork 21
Reflective method lookup for structural type fails if the method has a wider parameter type in the classfile #10334
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
The function does not have to be a reflective call, because there is a generic version of |
In the particular case yes, the reflective call is unnecessary. The fact that it crashes shows another bug though, which can be exploited with the following snippet: object Test {
def main(args: Array[String]): Unit = {
val f: { def apply(s: String): Object } = (x: String) => x
println(f("hi"))
}
} Running this gives
In 2.11, there is a When looking up the method to be invoked reflectively, we are still using the I think this would be good to fix for 2.12.3. I'll take a look tomorrow, cc @retronym, let me know if you dive into it before. |
There is a similar bug on Scala 2.11 Welcome to Scala 2.11.11 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_131).
Type in expressions for evaluation. Or try :help.
scala> val f0: Product => Unit = { none: Product => }
f0: Product => Unit = <function1>
scala> val f1: None.type => Unit = f0
f1: None.type => Unit = <function1>
scala> val f2: { def apply(i: None.type): Unit } = f1
f2: AnyRef{def apply(i: None.type): Unit} = <function1>
scala> f2(None)
warning: there was one feature warning; re-run with -feature for details
java.lang.NoSuchMethodException: $anonfun$1.apply(scala.None$)
at java.lang.Class.getMethod(Class.java:1786)
at .reflMethod$Method1(<console>:13)
... 42 elided |
This is a pretty fundamental issue with structural types: a value of type class A
class B extends A
class C extends B
trait T[-A] {
def m(a: A): Object
}
object Test {
def main(args: Array[String]): Unit = {
val f1 = new T[A] {
def m(x: A) = "f1-a"
def m(x: B) = "f1-b"
// the m(Object)Object bridge method invokes (A)Object
}
val f2 = new T[B] {
def m(x: A) = "f2-a"
def m(x: B) = "f2-b"
// the (Object)Object bridge method invokes (B)Object
}
val g1: T[C] = f1
val g2: T[C] = f2
assert(g1.m(new C) == "f1-a")
assert(g2.m(new C) == "f2-b")
val s1: { def m(s: C): Object } = g1
val s2: { def m(s: C): Object } = g2
// these currently crash, the reflective lookup doesn't find `m(C)Object`
println(s1.m(new C)) // should invoke `m(A)Object` (?)
println(s2.m(new C)) // should invoke `m(B)Object` (?)
}
} |
Moved to 2.12.4 as it's an old issue |
@lrytz The bug in Scala 2.11 only occurs on types related to contravariance. However, in Scala 2.12, it occurs on SAM types, which is far more common than type contravariance. |
Yes, thanks for pointing that out. A partial fix that would cover many cases would be to have a fallback lookup by method name, and pick it if there's a single one. This should be easy to do for 2.12.3, but it would still fail in the presence of overloads, which can also occur in the case of LMF-generated objects. |
Also the original bug report does not trigger reflective call warning, which means the typer did not expect a reflective call at all. |
In a structural invocation, if the receiver object doesn't have a method with the expected paramter types, check if there's a single method matching the name. If so, invoke this method. This fixes parts of scala/bug#10334, namely the parts that regressed with the new lambda encoding. But the underlying issue still remains.
Submitted the workaround here: scala/scala#5973 |
Currently, a lambda object for `(s: String) => String` only gets the `apply(Object)Object` method (LMF generates the necessary casts). When using such a lambda through a structural type `{def apply(s: String): String}`, the reflective lookup for the apply mehthod fails. This patch asks LMF to generate a bridge method with the instantiated signature. Fixes the regressed parts of scala/bug#10334
Currently, a lambda object for `(s: String) => String` only gets the `apply(Object)Object` method (LMF generates the necessary casts). When using such a lambda through a structural type `{def apply(s: String): String}`, the reflective lookup for the apply mehthod fails. This patch asks LMF to generate a bridge method with the instantiated signature. Fixes the regressed parts of scala/bug#10334
I'm going to mark this as fixed and spin off #10414 for the non-regression part. |
(see comments below for more reduced examples, the bug is not related to by-name parameters)
https://scastie.scala-lang.org/Atry/iHBKTvX2TLOIZcDeKtbB7Q
This example works in 2.11.11, but the underlying bug also exists in 2.11 (see comments)
The text was updated successfully, but these errors were encountered: