-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Refine parameter adaptation logic for arrays #23591
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
Conversation
|
This seems misguided to me. Why Changes to erasure are extremely risky, because they basically break tasty and binary compatibility every time. They need a very strong justification. Typically, only fixing unsoundness is a good enough reason to do this. |
|
Edit: Hmm it does not, at least judging by |
|
If we do not want to mess with erasure, could we just uniformly use the erased SAM methodtype for the implementation method type as well in the emitted bytecode? Would this always be safe for a type-correct source program? |
|
Probably, but we would need to do that at the backend level. Basically consider it a bridge method, or an |
Array[? >: AnyRef] erases to Object[]
sjrd
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new approach seems reasonable.
It might need similar changes in ExpandSAMs for Scala SAM types that don't translate to target SAM types.
| !sameClass(implType, samType) && !autoAdaptedParam(implType) | ||
| || (samType, implType).match { | ||
| case (defn.ArrayOf(_), defn.ArrayOf(_)) => false | ||
| case (defn.ArrayOf(_), _) => true // see #23179 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't there be the reverse order as well ((_, defn.ArrayOf(_)))? Like if the SAM takes an Array[T] where T <: AnyRef but the lambda is specialized to T = String, for example?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm unable to construct such a reversed example that will be accepted by the compiler.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need something similar for the result type (resultAdaptationNeeded)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure, I also fail here to construct an example that would cause a run time crash (which is of course weak evidence).
I tried constructing each of the five cases in the comment at the top of |
|
It might be worth including the other combinations in the test case as regression tests. |
|
This solution doesn't appear to work for ScalaJS, I'll need to dig deeper. |
1eade2c to
89116bb
Compare
c6be3bc to
2c7b12f
Compare
Fixes scala#23179 Since JDK 9, argument checks for LambdaMetaFactory have become stricter. Which is to say that its JDK 8 version was too lax. We apply adaptation now in case the samType is an array, but the implType is not.
- Guard the Erasure.scala array adaptation check with !scalajs.value since Scala.js custom SAMs are handled differently (via ExpandSAMs) - Add array type adaptation in tpd.AnonClass for Scala.js, which handles custom SAM closures that get expanded to anonymous classes before erasure - Add neg test documenting that reverse cases (impl has Array, SAM erases to Object) are rejected by the type checker due to array invariance
The fix for scala#23179 is now localized to ExpandSAMs where the SAM expansion happens, rather than in the general-purpose tpd.AnonClass function.
d10f173 to
05c812f
Compare
| val samInfo = samDenot.info | ||
| val implInfo = implSym.info | ||
| val needsArrayAdaptation = (samInfo, implInfo) match | ||
| case (samMt: MethodType, implMt: MethodType) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if it's hidden behind a PolyType? Or in a second term param list?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have a concrete example in mind? Those cases do not qualify as SAMs afaik.
I've added more run tests that involve multiple parameters and a generic return type on the trait.
For generics on the method, the compiler rejects both of these:
trait PolyA { def f[T](a: Array[AnyRef]): T }
object Test {
def gPoly(a: PolyA) = a.f[Int](Array.empty[AnyRef])
def main(args: Array[String]): Unit = {
gPoly([T] => (x: Array[? >: AnyRef]) => x.length.asInstanceOf[T]) // error
gPoly(new PolyA { // error
def f[T](x: Array[? >: AnyRef]): T = x.length.asInstanceOf[T]
})
}
}Same goes for curried argument lists:
trait CurriedA { def f(a: Array[AnyRef])(b: Int): Any }
object Test {
def gCurried(a: CurriedA) = a.f(Array.empty[AnyRef])(42)
def main(args: Array[String]): Unit = {
gCurried((x: Array[AnyRef]) => (n: Int) => x.headOption) // error
}
}Scala 2 rejects these as well (modulo the polymorphic lambda syntax of course).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those cases do not qualify as SAMs afaik.
Right. I missed that. Seems all good.
|
Should this change be backported to 3.8.0? It seems to be that it might be useful, since it's now compiled with JDK 17 so potentially might be affected. |
|
Yes, any JDK newer than 8 would result in a runtime failure for the kind of example this PR addresses. |
Fixes #23179
Since JDK 9, argument checks for LambdaMetaFactory have become stricter. Which is to say that its JDK 8 version was too lax.
We apply adaptation now in case the samType is an array, but the implType is not.