-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
Compiler version
3.3.2-RC1-bin-20230615-916d4e7-NIGHTLY and before
Minimized code
class Sel1 extends Selectable {
def applyDynamic(name: String)(args: Int*): Int = args.sum
}
class Sel2 extends Selectable {
def applyDynamic(name: String)(args: Int*)(arg: Int): Int = args.sum + arg
}
object Main {
def main(args: Array[String]) = {
val sel1 = (new Sel1).asInstanceOf[Sel1 {
def foo(x: Int)(y: Int): Int
def bar(xs: Int*)(y: Int): Int
}]
// println(sel1.foo(1)(2))
// println(sel1.bar(1)(2))
val sel2 = (new Sel2).asInstanceOf[Sel2 {
def foo(x: Int)(y: Int): Int
def bar(xs: Int*)(y: Int): Int
}]
// println(sel2.foo(1)(2))
// println(sel2.bar(1)(2))
}
}
Output
When one tries to compile and run the snippet from above after uncommenting only one commented line at a time, here's what happens in each case:
sel1.foo:
Compilation succeeds, prints3.
Relevant-Xprint:typeroutput:
sel1.applyDynamic("foo")([1,2 : Int]*).$asInstanceOf[Int]sel1.bar:
Compilation error
[error] Sel.scala:15:22
[error] Sequence argument type annotation `*` cannot be used here:
[error] it is not the only argument to be passed to the corresponding repeated parameter Int*
[error] println(sel1.bar(1)(2))
[error] ^Relevant -Xprint:typer output:
sel1.applyDynamic("bar")([[1 : Int]*,2 : Int]*).$asInstanceOf[Int]sel2.foo:
Runtime exception
Exception in thread "main" java.lang.ClassCastException: Main$$$Lambda$3/1929600551 cannot be cast to java.lang.Integer
at scala.runtime.BoxesRunTime.unboxToInt(BoxesRunTime.java:99)
at Main$.main(Sel.scala:21)
at Main.main(Sel.scala)Relevant -Xprint:typer output:
{
val args$1: Int* = ([1,2 : Int]*)
{
def $anonfun(arg: Int): Int =
sel2.applyDynamic("foo")(args$1)(arg)
closure($anonfun)
}
}.$asInstanceOf[Int]sel2.bar:
Compilation error
[error] Sel.scala:22:22
[error] Sequence argument type annotation `*` cannot be used here:
[error] it is not the only argument to be passed to the corresponding repeated parameter Int*
[error] println(sel2.bar(1)(2))
[error] ^Relevant -Xprint:typer output:
sel2.applyDynamic("bar")([[1 : Int]*,2 : Int]*).$asInstanceOf[Int]Expectation
The specification says:
Given a value v of type C { Rs }, where C is a class reference and Rs are structural refinement declarations, and given v.a of type U [...]
- If
Uis a method type(T11, ..., T1n)...(TN1, ..., TNn): Rand it is not a dependent method type, we mapv.a(a11, ..., a1n)...(aN1, ..., aNn)to:
v.applyDynamic("a")(a11, ..., a1n, ..., aN1, ..., aNn)
.asInstanceOf[R]Thus, according to the spec, the 4 cases should get desugared as follows:
sel1.foo(1)(2)->sel1.applyDynamic("foo")(1, 2).asInstanceOf[Int]sel1.bar(1)(2)->sel1.applyDynamic("bar")(1, 2).asInstanceOf[Int]sel2.foo(1)(2)->sel2.applyDynamic("foo")(1, 2).asInstanceOf[Int]sel2.bar(1)(2)->sel2.applyDynamic("bar")(1, 2).asInstanceOf[Int]
The first two cases should compile successfully and return3then, while the latter two should either perform eta-expansion and fail at runtime with an exception on casting a function toInt(we should try to avoid this situation) or complain about a missing parameter.
On the other hand, as a user I would expect code using Selectable to behave analogously to code using Dynamic instead.
Thus, given the snippet below
import scala.language.dynamics
class Dyn1 extends Dynamic {
def applyDynamic(name: String)(args: Int*): Int = args.sum
}
class Dyn2 extends Dynamic {
def applyDynamic(name: String)(args: Int*)(arg: Int): Int = args.sum + arg
}
object Main {
def main(args: Array[String]) = {
val dyn1 = new Dyn1
// println(dyn1.foo(1)(2))
// println(dyn1.bar(1)(2))
val dyn2 = new Dyn2
// println(dyn2.foo(1)(2))
// println(dyn2.bar(1)(2))
}
}and uncommenting a single line at a time, I get:
dyn1.foo:
[error] Dyn.scala:13:13
[error] method applyDynamic in class Dyn1 does not take more parameters
[error] println(dyn1.foo(1)(2))
[error] ^^^^^^^^^^dyn1.bar:
[error] Dyn.scala:14:13
[error] method applyDynamic in class Dyn1 does not take more parameters
[error] println(dyn1.bar(1)(2))
[error] ^^^^^^^^^^-
dyn2.foo:
Compiles, prints3 -
dyn2.bar:
Compiles, prints3
For Dynamics the behaviour in scala 2 is basically the same, though the compilation errors for dyn1.foo and dyn1.bar are slightly different:
[error] Dyn.scala:13:13
[error] Int does not take parameters
[error] println(dyn1.foo(1)(2))
[error] ^^^^^^^^^^^^^^
[error] Dyn.scala:14:13
[error] Int does not take parameters
[error] println(dyn1.bar(1)(2))
[error] ^^^^^^^^^^^^^^Summing this up, the behaviour for Dynamics in this case is the opposite to the specified behaviour for Selectable, which in turn is different to the current actual (definitely buggy in some way) behaviour for Selectable.