From 6beb69ac61b148b17be9cd60fc2b27eae8f9d85c Mon Sep 17 00:00:00 2001 From: G1ng3r Date: Tue, 16 May 2023 23:01:47 +0300 Subject: [PATCH 01/11] erase value classes from applyDynamic arguments --- .../src/dotty/tools/dotc/typer/Dynamic.scala | 16 +++++++++++-- tests/run/i16995.check | 3 +++ tests/run/i16995.scala | 23 +++++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 tests/run/i16995.check create mode 100644 tests/run/i16995.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala index 14cc7bf963a6..b0c8922bb54b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala +++ b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala @@ -193,7 +193,14 @@ trait Dynamic { val scall = if (vargss.isEmpty) base - else untpd.Apply(base, vargss.flatten.map(untpd.TypedSplice(_))) + else untpd.Apply(base, vargss.flatten.map{ t => + val clzSym = t.tpe.resultType.classSymbol.asClass + if ValueClasses.isDerivedValueClass(clzSym) then + val x = ValueClasses.valueClassUnbox(clzSym).asTerm + tpd.Select(t, x.name) + else + t + }.map(untpd.TypedSplice(_))) // If function is an `applyDynamic` that takes a Class* parameter, // add `classOfs`. @@ -268,7 +275,12 @@ trait Dynamic { if tpe.paramInfoss.nestedExists(!TypeErasure.hasStableErasure(_)) then fail(i"has a parameter type with an unstable erasure") :: Nil else - TypeErasure.erasure(tpe).asInstanceOf[MethodType].paramInfos.map(clsOf(_)) + TypeErasure.erasure(tpe).asInstanceOf[MethodType].paramInfos.map { tpe => + if ValueClasses.isDerivedValueClass(tpe.widen.classSymbol) then + clsOf(ValueClasses.valueClassUnbox(tpe.classSymbol.asClass).info) + else + clsOf(tpe) + } structuralCall(nme.applyDynamic, classOfs).maybeBoxingCast(tpe.finalResultType) } diff --git a/tests/run/i16995.check b/tests/run/i16995.check new file mode 100644 index 000000000000..218f60dad959 --- /dev/null +++ b/tests/run/i16995.check @@ -0,0 +1,3 @@ +1 +check +6 \ No newline at end of file diff --git a/tests/run/i16995.scala b/tests/run/i16995.scala new file mode 100644 index 000000000000..b97ea54592d5 --- /dev/null +++ b/tests/run/i16995.scala @@ -0,0 +1,23 @@ +class Foo(val i: Int) extends AnyVal +class Argument(val x: String) extends AnyVal +class Reflective extends reflect.Selectable + +@main def Test: Unit = + val reflective = new Reflective { + def bar(foo: Foo) = foo.i + def fun(argument: Argument) = argument + def manyArgs(argument: Argument, foo: Foo, someInt: Int) = foo.i + someInt + argument.x.length + } + + val i = reflective.bar(Foo(1)) + println(i) + + val arg = Argument("check") + val k = reflective.fun(arg).x + println(k) + + val length4 = Argument("four") + val foo = Foo(1) + val x = 1 + val j = reflective.manyArgs(length4, foo, x) + println(j) \ No newline at end of file From 2ca90a512cdd2d1e6d2e04d9ed017c85728536ba Mon Sep 17 00:00:00 2001 From: G1ng3r Date: Tue, 23 May 2023 23:14:18 +0300 Subject: [PATCH 02/11] handle varargs --- .../src/dotty/tools/dotc/typer/Dynamic.scala | 21 ++++++++++++------- tests/run/i16995.check | 5 ++++- tests/run/i16995.scala | 19 ++++++++++++++++- 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala index b0c8922bb54b..f2d75a9c2858 100644 --- a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala +++ b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala @@ -181,6 +181,18 @@ trait Dynamic { def handleStructural(tree: Tree)(using Context): Tree = { val fun @ Select(qual, name) = funPart(tree): @unchecked val vargss = termArgss(tree) + val isRepeated = vargss.flatten.exists(_.tpe.widen.isRepeatedParam) + + def handleRepeated(args: List[List[Tree]]): List[Tree] = + if isRepeated then List(untpd.TypedSplice(tpd.repeated(args.flatten, TypeTree(defn.AnyType)))) + else args.flatten.map { t => + val clzSym = t.tpe.resultType.classSymbol.asClass + if ValueClasses.isDerivedValueClass(clzSym) then + val underlying = ValueClasses.valueClassUnbox(clzSym).asTerm + tpd.Select(t, underlying.name) + else + t + }.map(untpd.TypedSplice(_)) def structuralCall(selectorName: TermName, classOfs: => List[Tree]) = { val selectable = adapt(qual, defn.SelectableClass.typeRef | defn.DynamicClass.typeRef) @@ -193,14 +205,7 @@ trait Dynamic { val scall = if (vargss.isEmpty) base - else untpd.Apply(base, vargss.flatten.map{ t => - val clzSym = t.tpe.resultType.classSymbol.asClass - if ValueClasses.isDerivedValueClass(clzSym) then - val x = ValueClasses.valueClassUnbox(clzSym).asTerm - tpd.Select(t, x.name) - else - t - }.map(untpd.TypedSplice(_))) + else untpd.Apply(base, handleRepeated(vargss)) // If function is an `applyDynamic` that takes a Class* parameter, // add `classOfs`. diff --git a/tests/run/i16995.check b/tests/run/i16995.check index 218f60dad959..e4b8cae71ac0 100644 --- a/tests/run/i16995.check +++ b/tests/run/i16995.check @@ -1,3 +1,6 @@ 1 check -6 \ No newline at end of file +6 +7 +5 +3 \ No newline at end of file diff --git a/tests/run/i16995.scala b/tests/run/i16995.scala index b97ea54592d5..97bf28d8f149 100644 --- a/tests/run/i16995.scala +++ b/tests/run/i16995.scala @@ -7,6 +7,8 @@ class Reflective extends reflect.Selectable def bar(foo: Foo) = foo.i def fun(argument: Argument) = argument def manyArgs(argument: Argument, foo: Foo, someInt: Int) = foo.i + someInt + argument.x.length + def varargs(x: Int, foo: Foo*) = foo.map(_.i).sum + x + def letsHaveSeq(args: Seq[Argument]) = args.map(_.x.length).sum } val i = reflective.bar(Foo(1)) @@ -20,4 +22,19 @@ class Reflective extends reflect.Selectable val foo = Foo(1) val x = 1 val j = reflective.manyArgs(length4, foo, x) - println(j) \ No newline at end of file + println(j) + + val varargs = List(Foo(1), Foo(2), Foo(3)) + val m = reflective.varargs(1, varargs:_*) + println(m) + + val foo1 = Foo(1) + val foo2 = Foo(2) + val foo3 = Foo(3) + val n = reflective.varargs(2, foo1, foo2) + println(n) + + val arg1 = Argument("1") + val seq = Seq(arg1, arg1, arg1) + val p = reflective.letsHaveSeq(seq) + println(p) \ No newline at end of file From d1f98a09e75889b001a2ebfdb1a64e76ae224bd9 Mon Sep 17 00:00:00 2001 From: "s.bazarsadaev" Date: Wed, 24 May 2023 16:37:51 +0300 Subject: [PATCH 03/11] remove type annotation --- compiler/src/dotty/tools/dotc/typer/Dynamic.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala index f2d75a9c2858..c42fd59bd6f1 100644 --- a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala +++ b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala @@ -183,7 +183,7 @@ trait Dynamic { val vargss = termArgss(tree) val isRepeated = vargss.flatten.exists(_.tpe.widen.isRepeatedParam) - def handleRepeated(args: List[List[Tree]]): List[Tree] = + def handleRepeated(args: List[List[Tree]]) = if isRepeated then List(untpd.TypedSplice(tpd.repeated(args.flatten, TypeTree(defn.AnyType)))) else args.flatten.map { t => val clzSym = t.tpe.resultType.classSymbol.asClass From b66fb49ac5b60a32212ddc014d656086bbe1575d Mon Sep 17 00:00:00 2001 From: "s.bazarsadaev" Date: Fri, 26 May 2023 01:21:40 +0300 Subject: [PATCH 04/11] add tree conversion into vararg-compatible tree represented as SeqLiteral --- compiler/src/dotty/tools/dotc/ast/tpd.scala | 5 +++++ compiler/src/dotty/tools/dotc/typer/Dynamic.scala | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 71b85d97a187..ab74a4cd099b 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -1592,6 +1592,11 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def repeated(trees: List[Tree], tpt: Tree)(using Context): Tree = ctx.typeAssigner.arrayToRepeated(JavaSeqLiteral(trees, tpt)) + /** Convert a list of trees to a vararg-compatible tree represented as SeqLiteral. + */ + def repeatedSeq(trees: List[Tree], tpt: Tree)(using Context): Tree = + ctx.typeAssigner.seqToRepeated(SeqLiteral(trees, tpt)) + /** Create a tree representing a list containing all * the elements of the argument list. A "list of tree to * tree of list" conversion. diff --git a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala index c42fd59bd6f1..62c3cba8e2a9 100644 --- a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala +++ b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala @@ -181,10 +181,10 @@ trait Dynamic { def handleStructural(tree: Tree)(using Context): Tree = { val fun @ Select(qual, name) = funPart(tree): @unchecked val vargss = termArgss(tree) - val isRepeated = vargss.flatten.exists(_.tpe.widen.isRepeatedParam) def handleRepeated(args: List[List[Tree]]) = - if isRepeated then List(untpd.TypedSplice(tpd.repeated(args.flatten, TypeTree(defn.AnyType)))) + val isRepeated = args.flatten.exists(_.tpe.widen.isRepeatedParam) + if isRepeated then List(untpd.TypedSplice(tpd.repeatedSeq(args.flatten, TypeTree(defn.AnyType)))) else args.flatten.map { t => val clzSym = t.tpe.resultType.classSymbol.asClass if ValueClasses.isDerivedValueClass(clzSym) then From 759cba04867d3f9c124eaf96cd7543d923a07c6b Mon Sep 17 00:00:00 2001 From: "s.bazarsadaev" Date: Fri, 26 May 2023 18:47:10 +0300 Subject: [PATCH 05/11] transform varargs only for subclasses of reflect.Selectable --- .../src/dotty/tools/dotc/typer/Dynamic.scala | 3 +- tests/run/i16995.check | 1 + tests/run/i16995.scala | 20 +++++++++++- .../CustomReflectSelectableTestScala3.scala | 32 +++++++++++++++++++ 4 files changed, 54 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala index 62c3cba8e2a9..9d49b90f5067 100644 --- a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala +++ b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala @@ -184,7 +184,8 @@ trait Dynamic { def handleRepeated(args: List[List[Tree]]) = val isRepeated = args.flatten.exists(_.tpe.widen.isRepeatedParam) - if isRepeated then List(untpd.TypedSplice(tpd.repeatedSeq(args.flatten, TypeTree(defn.AnyType)))) + if isRepeated && qual.tpe <:< defn.ReflectSelectableTypeRef then + List(untpd.TypedSplice(tpd.repeatedSeq(args.flatten, TypeTree(defn.AnyType)))) else args.flatten.map { t => val clzSym = t.tpe.resultType.classSymbol.asClass if ValueClasses.isDerivedValueClass(clzSym) then diff --git a/tests/run/i16995.check b/tests/run/i16995.check index e4b8cae71ac0..2329c0150b56 100644 --- a/tests/run/i16995.check +++ b/tests/run/i16995.check @@ -3,4 +3,5 @@ check 6 7 5 +3 3 \ No newline at end of file diff --git a/tests/run/i16995.scala b/tests/run/i16995.scala index 97bf28d8f149..90cd7cedb6cf 100644 --- a/tests/run/i16995.scala +++ b/tests/run/i16995.scala @@ -2,6 +2,12 @@ class Foo(val i: Int) extends AnyVal class Argument(val x: String) extends AnyVal class Reflective extends reflect.Selectable +class ScalaSelectable(values: Map[String, Any], methods: Map[String, (Int, Seq[Foo]) => Int]) extends Selectable { + def selectDynamic(name: String): Any = values(name) + + def applyDynamic(name: String)(i: Int, foos: Foo*): Int = methods(name)(i, foos) +} + @main def Test: Unit = val reflective = new Reflective { def bar(foo: Foo) = foo.i @@ -37,4 +43,16 @@ class Reflective extends reflect.Selectable val arg1 = Argument("1") val seq = Seq(arg1, arg1, arg1) val p = reflective.letsHaveSeq(seq) - println(p) \ No newline at end of file + println(p) + + val cont2values = Map.empty[String, Any] + + val cont2methods = Map[String, (Int, Seq[Foo]) => Int]( + "varargs" -> { (i: Int, foos: Seq[Foo]) => foos.map(_.i).sum + i } + ) + + val cont2 = ScalaSelectable(cont2values, cont2methods).asInstanceOf[ScalaSelectable { + def varargs(i: Int, foos: Foo*): Int + }] + + println(cont2.varargs(1, Foo(1), Foo(1))) \ No newline at end of file diff --git a/tests/sjs-junit/test/org/scalajs/testsuite/compiler/CustomReflectSelectableTestScala3.scala b/tests/sjs-junit/test/org/scalajs/testsuite/compiler/CustomReflectSelectableTestScala3.scala index 744a59bebc9d..e2b74ef16eed 100644 --- a/tests/sjs-junit/test/org/scalajs/testsuite/compiler/CustomReflectSelectableTestScala3.scala +++ b/tests/sjs-junit/test/org/scalajs/testsuite/compiler/CustomReflectSelectableTestScala3.scala @@ -15,12 +15,44 @@ class CustomReflectSelectableTestScala3 { val obj: reflect.Selectable { def foo(x: Int, y: String): String } = new CustomReflectSelectable(42) assertEquals("3 bar 42", obj.foo(3, "bar")) } + + @Test def callMethodWithValueClass(): Unit = { + val obj: reflect.Selectable { def bar(bar: Bar): Int } = new CustomReflectSelectable(42) + assertEquals(1, obj.bar(Bar(1))) + } + + @Test def callMethodWithVarargs(): Unit = { + val obj: reflect.Selectable { def varargs(x: Int, args: Bar*): Int } = new CustomReflectSelectable(42) + assertEquals(4, obj.varargs(2, Bar(1), Bar(1))) + } + + @Test def callSelectableWithVarargs(): Unit = { + val cont2values = Map.empty[String, Any] + val cont2methods = Map[String, (Int, Seq[Bar]) => Int]( + "varargs" -> { (i: Int, bars: Seq[Bar]) => bars.map(_.x).sum + i } + ) + val cont = ScalaSelectable(cont2values, cont2methods).asInstanceOf[ScalaSelectable { + def varargs(i: Int, foos: Bar*): Int + }] + assertEquals(3, cont.varargs(1, Bar(1), Bar(1))) + } } object CustomReflectSelectableTestScala3 { + class Bar(val x: Int) extends AnyVal class CustomReflectSelectable(param: Int) extends reflect.Selectable { val x: Int = 5 + param def foo(x: Int, y: String): String = s"$x $y $param" + + def bar(bar: Bar) = bar.x + + def varargs(x: Int, args: Bar*) = args.map(_.x).sum + x + } + + class ScalaSelectable(values: Map[String, Any], methods: Map[String, (Int, Seq[Bar]) => Int]) extends Selectable { + def selectDynamic(name: String): Any = values(name) + + def applyDynamic(name: String)(i: Int, bars: Bar*): Int = methods(name)(i, bars) } } From 2742caa668589418f4e41e055e68c0dd4b8b3936 Mon Sep 17 00:00:00 2001 From: "s.bazarsadaev" Date: Thu, 1 Jun 2023 15:37:30 +0300 Subject: [PATCH 06/11] add scalajs test for varargs seq expansion --- .../compiler/CustomReflectSelectableTestScala3.scala | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/sjs-junit/test/org/scalajs/testsuite/compiler/CustomReflectSelectableTestScala3.scala b/tests/sjs-junit/test/org/scalajs/testsuite/compiler/CustomReflectSelectableTestScala3.scala index e2b74ef16eed..fae8f7edfa0b 100644 --- a/tests/sjs-junit/test/org/scalajs/testsuite/compiler/CustomReflectSelectableTestScala3.scala +++ b/tests/sjs-junit/test/org/scalajs/testsuite/compiler/CustomReflectSelectableTestScala3.scala @@ -36,6 +36,18 @@ class CustomReflectSelectableTestScala3 { }] assertEquals(3, cont.varargs(1, Bar(1), Bar(1))) } + + @Test def callSelectableWithVarargsExpansion(): Unit = { + val cont2values = Map.empty[String, Any] + val cont2methods = Map[String, (Int, Seq[Bar]) => Int]( + "varargs" -> { (i: Int, bars: Seq[Bar]) => bars.map(_.x).sum + i } + ) + val cont = ScalaSelectable(cont2values, cont2methods).asInstanceOf[ScalaSelectable { + def varargs(i: Int, foos: Bar*): Int + }] + val args = Seq(Bar(1), Bar(1)) + assertEquals(3, cont.varargs(1, args:_*)) + } } object CustomReflectSelectableTestScala3 { From 24c85ce61743328fcceef5e5f3d6059b111fe055 Mon Sep 17 00:00:00 2001 From: "s.bazarsadaev" Date: Thu, 1 Jun 2023 18:43:01 +0300 Subject: [PATCH 07/11] add test to check seq expansion with reflection subclass --- .../compiler/CustomReflectSelectableTestScala3.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/sjs-junit/test/org/scalajs/testsuite/compiler/CustomReflectSelectableTestScala3.scala b/tests/sjs-junit/test/org/scalajs/testsuite/compiler/CustomReflectSelectableTestScala3.scala index fae8f7edfa0b..19740a31f48a 100644 --- a/tests/sjs-junit/test/org/scalajs/testsuite/compiler/CustomReflectSelectableTestScala3.scala +++ b/tests/sjs-junit/test/org/scalajs/testsuite/compiler/CustomReflectSelectableTestScala3.scala @@ -26,6 +26,12 @@ class CustomReflectSelectableTestScala3 { assertEquals(4, obj.varargs(2, Bar(1), Bar(1))) } + @Test def callMethodWithVarargsExpansion(): Unit = { + val args = Seq(Bar(1), Bar(1)) + val obj: reflect.Selectable {def varargs(x: Int, args: Bar*): Int} = new CustomReflectSelectable(42) + assertEquals(4, obj.varargs(2, args:_*)) + } + @Test def callSelectableWithVarargs(): Unit = { val cont2values = Map.empty[String, Any] val cont2methods = Map[String, (Int, Seq[Bar]) => Int]( From 89dd6f531a0af9b4320114e0fa3c0b1975606419 Mon Sep 17 00:00:00 2001 From: G1ng3r Date: Sun, 10 Sep 2023 20:49:01 +0300 Subject: [PATCH 08/11] add support for curried applyDynamic methods --- .../src/dotty/tools/dotc/typer/Dynamic.scala | 31 ++++++++++--------- .../changed-features/structural-types-spec.md | 12 +++++++ tests/run/i16995.check | 3 +- tests/run/i16995.scala | 8 ++++- 4 files changed, 37 insertions(+), 17 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala index 9d49b90f5067..ca0a08da3ae0 100644 --- a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala +++ b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala @@ -3,8 +3,7 @@ package dotc package typer import dotty.tools.dotc.ast.Trees.* -import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.ast.untpd +import dotty.tools.dotc.ast.{tpd, untpd} import dotty.tools.dotc.core.Constants.Constant import dotty.tools.dotc.core.Contexts.* import dotty.tools.dotc.core.Flags.* @@ -182,18 +181,20 @@ trait Dynamic { val fun @ Select(qual, name) = funPart(tree): @unchecked val vargss = termArgss(tree) - def handleRepeated(args: List[List[Tree]]) = - val isRepeated = args.flatten.exists(_.tpe.widen.isRepeatedParam) - if isRepeated && qual.tpe <:< defn.ReflectSelectableTypeRef then - List(untpd.TypedSplice(tpd.repeatedSeq(args.flatten, TypeTree(defn.AnyType)))) - else args.flatten.map { t => - val clzSym = t.tpe.resultType.classSymbol.asClass - if ValueClasses.isDerivedValueClass(clzSym) then - val underlying = ValueClasses.valueClassUnbox(clzSym).asTerm - tpd.Select(t, underlying.name) - else - t - }.map(untpd.TypedSplice(_)) + def handleRepeated(base: Tree, possiblyCurried: List[List[Tree]]) = + possiblyCurried.map { args => + val isRepeated = args.exists(_.tpe.widen.isRepeatedParam) + if isRepeated && qual.tpe <:< defn.ReflectSelectableTypeRef then + List(untpd.TypedSplice(tpd.repeatedSeq(args, TypeTree(defn.AnyType)))) + else args.map { t => + val clzSym = t.tpe.resultType.classSymbol.asClass + if ValueClasses.isDerivedValueClass(clzSym) && qual.tpe <:< defn.ReflectSelectableTypeRef then + val underlying = ValueClasses.valueClassUnbox(clzSym).asTerm + tpd.Select(t, underlying.name) + else + t + }.map(untpd.TypedSplice(_)) + }.foldLeft(base)((base, args) => untpd.Apply(base, args)) def structuralCall(selectorName: TermName, classOfs: => List[Tree]) = { val selectable = adapt(qual, defn.SelectableClass.typeRef | defn.DynamicClass.typeRef) @@ -206,7 +207,7 @@ trait Dynamic { val scall = if (vargss.isEmpty) base - else untpd.Apply(base, handleRepeated(vargss)) + else handleRepeated(base, vargss) // If function is an `applyDynamic` that takes a Class* parameter, // add `classOfs`. diff --git a/docs/_docs/reference/changed-features/structural-types-spec.md b/docs/_docs/reference/changed-features/structural-types-spec.md index 216b738ae61c..be33201d53b2 100644 --- a/docs/_docs/reference/changed-features/structural-types-spec.md +++ b/docs/_docs/reference/changed-features/structural-types-spec.md @@ -55,6 +55,18 @@ Both versions are passed the actual arguments in the `args` parameter. The secon if `applyDynamic` is implemented using Java reflection, but it could be useful in other cases as well. `selectDynamic` and `applyDynamic` can also take additional context parameters in using clauses. These are resolved in the normal way at the callsite. +Typically, vararg arguments of a function at callsite are represented as an `Array(elems)`. But in case of `scala.Selectable` implementation relied on Java reflection `scala.reflect.Selectable` when vararg arguments passed to `applyDynamic` method, they get interpreted as single parameter instead of multiple. +To overcome this they should be wrapped in `Seq(elems)` to be considered as multiple parameters at callsite of `scala.reflect.Selectable.applyDynamic`. +```scala +class Reflective extends reflect.Selectable +class Foo(val i: Int) extends AnyVal +val reflective = new Reflective { + def varargs(x: Int, foo: Foo*) = foo.map(_.i).sum + x +} +val varargs = List(Foo(1), Foo(2), Foo(3)) +reflective.varargs(1, varargs:_*) +``` + 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`, we consider three distinct cases: diff --git a/tests/run/i16995.check b/tests/run/i16995.check index 2329c0150b56..700f1b325fac 100644 --- a/tests/run/i16995.check +++ b/tests/run/i16995.check @@ -4,4 +4,5 @@ check 7 5 3 -3 \ No newline at end of file +3 +7 \ No newline at end of file diff --git a/tests/run/i16995.scala b/tests/run/i16995.scala index 90cd7cedb6cf..6994a308ddf4 100644 --- a/tests/run/i16995.scala +++ b/tests/run/i16995.scala @@ -6,6 +6,8 @@ class ScalaSelectable(values: Map[String, Any], methods: Map[String, (Int, Seq[F def selectDynamic(name: String): Any = values(name) def applyDynamic(name: String)(i: Int, foos: Foo*): Int = methods(name)(i, foos) + + def applyDynamic(name: String)(foo: Foo)(argument: Argument)(someInt: Int): Int = foo.i + argument.x.length + someInt } @main def Test: Unit = @@ -15,6 +17,7 @@ class ScalaSelectable(values: Map[String, Any], methods: Map[String, (Int, Seq[F def manyArgs(argument: Argument, foo: Foo, someInt: Int) = foo.i + someInt + argument.x.length def varargs(x: Int, foo: Foo*) = foo.map(_.i).sum + x def letsHaveSeq(args: Seq[Argument]) = args.map(_.x.length).sum + def curried(foo: Foo)(arg1: Argument)(someInt: Int): Int = foo.i + arg1.x.length + someInt } val i = reflective.bar(Foo(1)) @@ -53,6 +56,9 @@ class ScalaSelectable(values: Map[String, Any], methods: Map[String, (Int, Seq[F val cont2 = ScalaSelectable(cont2values, cont2methods).asInstanceOf[ScalaSelectable { def varargs(i: Int, foos: Foo*): Int + def curried(foo: Foo)(argument: Argument)(someInt: Int): Int }] - println(cont2.varargs(1, Foo(1), Foo(1))) \ No newline at end of file + println(cont2.varargs(1, Foo(1), Foo(1))) + + println(cont2.curried(Foo(1))(Argument("123"))(3)) \ No newline at end of file From f76d1b5ee60a42aa20fd27a898d7dcb5db6a755c Mon Sep 17 00:00:00 2001 From: G1ng3r Date: Mon, 11 Sep 2023 21:54:07 +0300 Subject: [PATCH 09/11] pass untpd.Tree to handleRepeated --- compiler/src/dotty/tools/dotc/typer/Dynamic.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala index ca0a08da3ae0..5f6e19ed5aac 100644 --- a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala +++ b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala @@ -177,11 +177,11 @@ trait Dynamic { * It's an error if U is neither a value nor a method type, or a dependent method * type */ - def handleStructural(tree: Tree)(using Context): Tree = { + def handleStructural(tree: Tree)(using Context) = { val fun @ Select(qual, name) = funPart(tree): @unchecked val vargss = termArgss(tree) - def handleRepeated(base: Tree, possiblyCurried: List[List[Tree]]) = + def handleRepeated(base: untpd.Tree, possiblyCurried: List[List[Tree]]) = possiblyCurried.map { args => val isRepeated = args.exists(_.tpe.widen.isRepeatedParam) if isRepeated && qual.tpe <:< defn.ReflectSelectableTypeRef then From c680f7d498f0b5356c6057cb5312986fb2d2cd0f Mon Sep 17 00:00:00 2001 From: G1ng3r Date: Sun, 5 Nov 2023 22:07:48 +0300 Subject: [PATCH 10/11] fix varargs when scala.reflect with implicit converison --- .../src/dotty/tools/dotc/typer/Dynamic.scala | 24 ++++++++++++++----- tests/run/i16995.check | 4 +++- tests/run/i16995.scala | 21 +++++++++++++++- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala index 5f6e19ed5aac..6f5c3c7ac5e2 100644 --- a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala +++ b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala @@ -148,6 +148,9 @@ trait Dynamic { untpd.Apply(selectWithTypes, Literal(Constant(name.toString))) } + extension (tpe: Type) + def isReflectSelectableTypeRef(using Context): Boolean = tpe <:< defn.ReflectSelectableTypeRef + /** Handle reflection-based dispatch for members of structural types. * * Given `x.a`, where `x` is of (widened) type `T` (a value type or a nullary method type), @@ -181,20 +184,25 @@ trait Dynamic { val fun @ Select(qual, name) = funPart(tree): @unchecked val vargss = termArgss(tree) - def handleRepeated(base: untpd.Tree, possiblyCurried: List[List[Tree]]) = - possiblyCurried.map { args => + def handleRepeated(base: untpd.Tree, possiblyCurried: List[List[Tree]], isReflectSelectable: Boolean) = { + val handled = possiblyCurried.map { args => val isRepeated = args.exists(_.tpe.widen.isRepeatedParam) - if isRepeated && qual.tpe <:< defn.ReflectSelectableTypeRef then + if isRepeated && isReflectSelectable then List(untpd.TypedSplice(tpd.repeatedSeq(args, TypeTree(defn.AnyType)))) else args.map { t => val clzSym = t.tpe.resultType.classSymbol.asClass - if ValueClasses.isDerivedValueClass(clzSym) && qual.tpe <:< defn.ReflectSelectableTypeRef then + if ValueClasses.isDerivedValueClass(clzSym) && isReflectSelectable then val underlying = ValueClasses.valueClassUnbox(clzSym).asTerm tpd.Select(t, underlying.name) else t }.map(untpd.TypedSplice(_)) - }.foldLeft(base)((base, args) => untpd.Apply(base, args)) + } + + if isReflectSelectable + then untpd.Apply(base, handled.flatten) + else handled.foldLeft(base)((base, args) => untpd.Apply(base, args)) + } def structuralCall(selectorName: TermName, classOfs: => List[Tree]) = { val selectable = adapt(qual, defn.SelectableClass.typeRef | defn.DynamicClass.typeRef) @@ -207,7 +215,11 @@ trait Dynamic { val scall = if (vargss.isEmpty) base - else handleRepeated(base, vargss) + else handleRepeated( + base, + vargss, + qual.tpe.isReflectSelectableTypeRef || selectable.tpe.isReflectSelectableTypeRef + ) // If function is an `applyDynamic` that takes a Class* parameter, // add `classOfs`. diff --git a/tests/run/i16995.check b/tests/run/i16995.check index 700f1b325fac..ade44fc1ed36 100644 --- a/tests/run/i16995.check +++ b/tests/run/i16995.check @@ -5,4 +5,6 @@ check 5 3 3 -7 \ No newline at end of file +3 +7 +3 \ No newline at end of file diff --git a/tests/run/i16995.scala b/tests/run/i16995.scala index 6994a308ddf4..163a5b070de3 100644 --- a/tests/run/i16995.scala +++ b/tests/run/i16995.scala @@ -2,6 +2,14 @@ class Foo(val i: Int) extends AnyVal class Argument(val x: String) extends AnyVal class Reflective extends reflect.Selectable +type ReflectiveType = { + def reflectiveCall(arg1: Int)(arg2: Int): Int +} + +class ClassWithReflectiveCall { + def reflectiveCall(x: Int)(y: Int): Int = x + y +} + class ScalaSelectable(values: Map[String, Any], methods: Map[String, (Int, Seq[Foo]) => Int]) extends Selectable { def selectDynamic(name: String): Any = values(name) @@ -48,6 +56,8 @@ class ScalaSelectable(values: Map[String, Any], methods: Map[String, (Int, Seq[F val p = reflective.letsHaveSeq(seq) println(p) + println(reflective.curried(foo1)(arg1)(1)) + val cont2values = Map.empty[String, Any] val cont2methods = Map[String, (Int, Seq[Foo]) => Int]( @@ -61,4 +71,13 @@ class ScalaSelectable(values: Map[String, Any], methods: Map[String, (Int, Seq[F println(cont2.varargs(1, Foo(1), Foo(1))) - println(cont2.curried(Foo(1))(Argument("123"))(3)) \ No newline at end of file + println(cont2.curried(Foo(1))(Argument("123"))(3)) + + { + import scala.reflect.Selectable.reflectiveSelectable + val obj = new ClassWithReflectiveCall() + def instantiate(): ReflectiveType = obj + + val rtype = instantiate() + println(rtype.reflectiveCall(1)(2)) + } \ No newline at end of file From 1f3f7e71d994ec0b42eb5c1fe8b0b8d53ebb186e Mon Sep 17 00:00:00 2001 From: G1ng3r Date: Mon, 6 Nov 2023 17:21:53 +0300 Subject: [PATCH 11/11] add more vararg tests --- .../src/dotty/tools/dotc/typer/Dynamic.scala | 31 ++- tests/pos/i17100.scala | 2 +- tests/run/i14340.scala | 10 +- tests/run/i16995.check | 72 +++++- tests/run/i16995.scala | 229 ++++++++++++------ .../CustomReflectSelectableTestScala3.scala | 22 +- 6 files changed, 248 insertions(+), 118 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala index 6f5c3c7ac5e2..fc76e70178cc 100644 --- a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala +++ b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala @@ -185,23 +185,20 @@ trait Dynamic { val vargss = termArgss(tree) def handleRepeated(base: untpd.Tree, possiblyCurried: List[List[Tree]], isReflectSelectable: Boolean) = { - val handled = possiblyCurried.map { args => - val isRepeated = args.exists(_.tpe.widen.isRepeatedParam) - if isRepeated && isReflectSelectable then - List(untpd.TypedSplice(tpd.repeatedSeq(args, TypeTree(defn.AnyType)))) - else args.map { t => - val clzSym = t.tpe.resultType.classSymbol.asClass - if ValueClasses.isDerivedValueClass(clzSym) && isReflectSelectable then - val underlying = ValueClasses.valueClassUnbox(clzSym).asTerm - tpd.Select(t, underlying.name) - else - t - }.map(untpd.TypedSplice(_)) + val args = possiblyCurried.flatten.map { t => + val clzSym = t.tpe.resultType.classSymbol.asClass + if clzSym.isDerivedValueClass && isReflectSelectable then + val underlying = ValueClasses.valueClassUnbox(clzSym).asTerm + tpd.Select(t, underlying.name) + else + t } + val handledFlat = + if isReflectSelectable then List(untpd.TypedSplice(tpd.repeatedSeq(args, TypeTree(defn.AnyType)))) + else if args.isEmpty then List() + else List(untpd.TypedSplice(tpd.repeatedSeq(args, TypeTree(args.map(_.tpe).reduce(_ | _))))) - if isReflectSelectable - then untpd.Apply(base, handled.flatten) - else handled.foldLeft(base)((base, args) => untpd.Apply(base, args)) + untpd.Apply(base, handledFlat) } def structuralCall(selectorName: TermName, classOfs: => List[Tree]) = { @@ -265,7 +262,7 @@ trait Dynamic { */ def maybeBoxingCast(tpe: Type) = val maybeBoxed = - if tpe.classSymbol.isDerivedValueClass && qual.tpe <:< defn.ReflectSelectableTypeRef then + if tpe.classSymbol.isDerivedValueClass && qual.tpe.isReflectSelectableTypeRef then val genericUnderlying = ValueClasses.valueClassUnbox(tpe.classSymbol.asClass) val underlying = tpe.select(genericUnderlying).widen.resultType New(tpe.widen, tree.cast(underlying) :: Nil) @@ -295,7 +292,7 @@ trait Dynamic { fail(i"has a parameter type with an unstable erasure") :: Nil else TypeErasure.erasure(tpe).asInstanceOf[MethodType].paramInfos.map { tpe => - if ValueClasses.isDerivedValueClass(tpe.widen.classSymbol) then + if tpe.widen.classSymbol.isDerivedValueClass then clsOf(ValueClasses.valueClassUnbox(tpe.classSymbol.asClass).info) else clsOf(tpe) diff --git a/tests/pos/i17100.scala b/tests/pos/i17100.scala index 1858e0383f8d..5d63f6d1a794 100644 --- a/tests/pos/i17100.scala +++ b/tests/pos/i17100.scala @@ -2,7 +2,7 @@ trait Sel extends Selectable extension (s: Sel) def selectDynamic(name: String) = ??? - def applyDynamic(name: String)(x: Int) = ??? + def applyDynamic(name: String)(x: Int*) = ??? def applyDynamic(name: String)() = ??? val sel = (new Sel {}).asInstanceOf[Sel{ def foo: String; def bar(x: Int): Int; def baz(): Int }] diff --git a/tests/run/i14340.scala b/tests/run/i14340.scala index 0670c7e471ac..230e08d53428 100644 --- a/tests/run/i14340.scala +++ b/tests/run/i14340.scala @@ -1,8 +1,8 @@ class Container1 extends reflect.Selectable -class Container2(values: Map[String, Any], methods: Map[String, Int => Any]) extends Selectable: +class Container2(values: Map[String, Any], methods: Map[String, Seq[Int] => Any]) extends Selectable: def selectDynamic(name: String) = values(name) - def applyDynamic(name: String)(arg: Int) = methods(name)(arg) + def applyDynamic(name: String)(arg: Int*) = methods(name)(arg) class Foo(val value: Int) extends AnyVal class Bar[A](val value: A) extends AnyVal @@ -29,7 +29,7 @@ object Helpers: ) val cont2methods = Map( - "fooFromInt" -> { (i: Int) => Foo(i) } + "fooFromInt" -> { (i: Seq[Int]) => Foo(i.head) } ) val cont2 = Container2(cont2values, cont2methods).asInstanceOf[Container2 { @@ -39,7 +39,7 @@ object Helpers: def qux2: Bar[Container2 { def foo: Foo }] def fooFromInt(i: Int): Foo }] - + println(cont1.foo.value) println(cont2.foo.value) @@ -49,7 +49,7 @@ object Helpers: println(cont1.qux1.value.foo.value) println(cont2.qux1.value.foo.value) - + println(cont1.qux2.value.foo.value) println(cont2.qux2.value.foo.value) diff --git a/tests/run/i16995.check b/tests/run/i16995.check index ade44fc1ed36..45aa0643ce08 100644 --- a/tests/run/i16995.check +++ b/tests/run/i16995.check @@ -1,10 +1,62 @@ -1 -check -6 -7 -5 -3 -3 -3 -7 -3 \ No newline at end of file +foo +foo0 +fooI 1 +fooN 1 +fooII 1 2 +fooNN 1 2 +fooIvarargs 1 2 +fooNvarargs 1 2 +fooIseq 1 2 +fooNseq 1 2 +fooIIvarargs 1 2 +fooNNvarargs 1 2 +fooI_I 1 2 +fooN_N 1 2 +foo0_II 1 2 +foo0_NN 1 2 +foo0_Ivarargs 1 2 +foo0_Nvarargs 1 2 +foo0_I_I 1 2 +foo0_N_N 1 2 + +foo +foo0 +fooI 1 +fooN 1 +fooII 1 2 +fooNN 1 2 +fooIvarargs 1 2 +fooNvarargs 1 2 +fooIseq 1 2 +fooNseq 1 2 +fooIIvarargs 1 2 3 +fooNNvarargs 1 2 +fooI_I 1 2 +fooN_N 1 2 +foo0_II 1 2 +foo0_NN 1 2 +foo0_Ivarargs 1 2 +foo0_Nvarargs 1 2 +foo0_I_I 1 2 +foo0_N_N 1 2 + +foo +foo0 +fooI 1 +fooN 1 +fooII 1 2 +fooNN 1 2 +fooIvarargs 1 2 +fooNvarargs 1 2 +fooIseq 1 2 +fooNseq 1 2 +fooIIvarargs 1 2 +fooNNvarargs 1 2 +fooI_I 1 2 +fooN_N 1 2 +foo0_II 1 2 +foo0_NN 1 2 +foo0_Ivarargs 1 2 +foo0_Nvarargs 1 2 +foo0_I_I 1 2 +foo0_N_N 1 2 diff --git a/tests/run/i16995.scala b/tests/run/i16995.scala index 163a5b070de3..d23e9b0f0d8d 100644 --- a/tests/run/i16995.scala +++ b/tests/run/i16995.scala @@ -1,83 +1,154 @@ -class Foo(val i: Int) extends AnyVal -class Argument(val x: String) extends AnyVal -class Reflective extends reflect.Selectable - -type ReflectiveType = { - def reflectiveCall(arg1: Int)(arg2: Int): Int -} - -class ClassWithReflectiveCall { - def reflectiveCall(x: Int)(y: Int): Int = x + y -} - -class ScalaSelectable(values: Map[String, Any], methods: Map[String, (Int, Seq[Foo]) => Int]) extends Selectable { - def selectDynamic(name: String): Any = values(name) - - def applyDynamic(name: String)(i: Int, foos: Foo*): Int = methods(name)(i, foos) - - def applyDynamic(name: String)(foo: Foo)(argument: Argument)(someInt: Int): Int = foo.i + argument.x.length + someInt -} - -@main def Test: Unit = - val reflective = new Reflective { - def bar(foo: Foo) = foo.i - def fun(argument: Argument) = argument - def manyArgs(argument: Argument, foo: Foo, someInt: Int) = foo.i + someInt + argument.x.length - def varargs(x: Int, foo: Foo*) = foo.map(_.i).sum + x - def letsHaveSeq(args: Seq[Argument]) = args.map(_.x.length).sum - def curried(foo: Foo)(arg1: Argument)(someInt: Int): Int = foo.i + arg1.x.length + someInt +import scala.language.dynamics +import scala.reflect.Selectable.reflectiveSelectable + +object Test { + class Num(val i: Int) extends AnyVal + + def show(x: Int | Num | Seq[Int | Num]): String = x match + case i: Int => i.toString + case num: Num => num.i.toString + case seq: Seq[Int | Num] => seq.map(show).mkString(" ") + + trait Nonreflective extends Selectable: + def selectDynamic(name: String): String = name + def applyDynamic(name: String)(args: (Int | Num | Seq[Int | Num])*): String = + val argsString = args.map(show).mkString(" ", " ", "") + s"${name}${argsString}" + + trait Dynamic0 extends Dynamic: + def selectDynamic(name: String): String = name + + trait Dynamic1 extends Dynamic: + def applyDynamic(name: String)(args1: (Int | Num | Seq[Int | Num])*): String = + val argsString = args1.map(show).mkString(" ", " ", "") + s"${name}${argsString}" + + trait Dynamic2 extends Dynamic: + def applyDynamic(name: String)(args1: (Int | Num)*)(args2: (Int | Num)*): String = + val argsString = (args1 ++ args2).map(show).mkString(" ", " ", "") + s"${name}${argsString}" + + trait Dynamic3 extends Dynamic: + def applyDynamic(name: String)(args1: (Int | Num)*)(args2: (Int | Num)*)(args3: (Int | Num)*): String = + val argsString = (args1 ++ args2 ++ args3).map(show).mkString(" ", " ", "") + s"${name}${argsString}" + + type Api = { + def foo: String + def foo0(): String + def fooI(i: Int): String + def fooN(n: Num): String + def fooII(i1: Int, i2: Int): String + def fooNN(n1: Num, n2: Num): String + def fooIvarargs(is: Int*): String + def fooNvarargs(ns: Num*): String + def fooIseq(is: Seq[Int]): String + def fooNseq(ns: Seq[Num]): String + def fooIIvarargs(i1: Int, is: Int*): String + def fooNNvarargs(n1: Num, ns: Num*): String + def fooI_I(i1: Int)(i2: Int): String + def fooN_N(n1: Num)(n2: Num): String + def foo0_II()(i1: Int, i2: Int): String + def foo0_NN()(n1: Num, n2: Num): String + def foo0_Ivarargs()(is: Int*): String + def foo0_Nvarargs()(ns: Num*): String + def foo0_I_I()(i1: Int)(i2: Int): String + def foo0_N_N()(n1: Num)(n2: Num): String } - - val i = reflective.bar(Foo(1)) - println(i) - - val arg = Argument("check") - val k = reflective.fun(arg).x - println(k) - - val length4 = Argument("four") - val foo = Foo(1) - val x = 1 - val j = reflective.manyArgs(length4, foo, x) - println(j) - - val varargs = List(Foo(1), Foo(2), Foo(3)) - val m = reflective.varargs(1, varargs:_*) - println(m) - - val foo1 = Foo(1) - val foo2 = Foo(2) - val foo3 = Foo(3) - val n = reflective.varargs(2, foo1, foo2) - println(n) - val arg1 = Argument("1") - val seq = Seq(arg1, arg1, arg1) - val p = reflective.letsHaveSeq(seq) - println(p) - - println(reflective.curried(foo1)(arg1)(1)) - - val cont2values = Map.empty[String, Any] - - val cont2methods = Map[String, (Int, Seq[Foo]) => Int]( - "varargs" -> { (i: Int, foos: Seq[Foo]) => foos.map(_.i).sum + i } - ) - - val cont2 = ScalaSelectable(cont2values, cont2methods).asInstanceOf[ScalaSelectable { - def varargs(i: Int, foos: Foo*): Int - def curried(foo: Foo)(argument: Argument)(someInt: Int): Int - }] - - println(cont2.varargs(1, Foo(1), Foo(1))) - - println(cont2.curried(Foo(1))(Argument("123"))(3)) + class ClassImpl { + def foo: String = "foo" + def foo0(): String = "foo0" + def fooI(i: Int): String = s"fooI ${i}" + def fooN(n: Num): String = s"fooN ${n.i}" + def fooII(i1: Int, i2: Int): String = s"fooII ${i1} ${i2}" + def fooNN(n1: Num, n2: Num): String = s"fooNN ${n1.i} ${n2.i}" + def fooIvarargs(is: Int*): String = s"fooIvarargs${is.mkString(" ", " ", "")}" + def fooNvarargs(ns: Num*): String = s"fooNvarargs${ns.map(_.i).mkString(" ", " ", "")}" + def fooIseq(is: Seq[Int]): String = s"fooIseq${is.mkString(" ", " ", "")}" + def fooNseq(ns: Seq[Num]): String = s"fooNseq${ns.map(_.i).mkString(" ", " ", "")}" + def fooIIvarargs(i1: Int, is: Int*): String = s"fooIIvarargs ${i1}${is.mkString(" ", " ", "")}" + def fooNNvarargs(n1: Num, ns: Num*): String = s"fooNNvarargs ${n1.i}${ns.map(_.i).mkString(" ", " ", "")}" + def fooI_I(i1: Int)(i2: Int): String = s"fooI_I ${i1} ${i2}" + def fooN_N(n1: Num)(n2: Num): String = s"fooN_N ${n1.i} ${n2.i}" + def foo0_II()(i1: Int, i2: Int): String = s"foo0_II ${i1} ${i2}" + def foo0_NN()(n1: Num, n2: Num): String = s"foo0_NN ${n1.i} ${n2.i}" + def foo0_Ivarargs()(is: Int*): String = s"foo0_Ivarargs${is.mkString(" ", " ", "")}" + def foo0_Nvarargs()(ns: Num*): String = s"foo0_Nvarargs${ns.map(_.i).mkString(" ", " ", "")}" + def foo0_I_I()(i1: Int)(i2: Int): String = s"foo0_I_I ${i1} ${i2}" + def foo0_N_N()(n1: Num)(n2: Num): String = s"foo0_N_N ${n1.i} ${n2.i}" + } - { - import scala.reflect.Selectable.reflectiveSelectable - val obj = new ClassWithReflectiveCall() - def instantiate(): ReflectiveType = obj - val rtype = instantiate() - println(rtype.reflectiveCall(1)(2)) - } \ No newline at end of file + def main(args: Array[String]): Unit = { + val reflective: Api = new ClassImpl() + val nonreflective: Nonreflective & Api = (new Nonreflective {}).asInstanceOf[Nonreflective & Api] + val dynamic0 = new Dynamic0 {} + val dynamic1 = new Dynamic1 {} + val dynamic2 = new Dynamic2 {} + val dynamic3 = new Dynamic3 {} + + println(reflective.foo) + println(reflective.foo0()) + println(reflective.fooI(1)) + println(reflective.fooN(new Num(1))) + println(reflective.fooII(1, 2)) + println(reflective.fooNN(new Num(1), new Num(2))) + println(reflective.fooIvarargs(1, 2)) + println(reflective.fooNvarargs(new Num(1), new Num(2))) + println(reflective.fooIseq(Seq(1, 2))) + println(reflective.fooNseq(Seq(new Num(1), new Num(2)))) + println(reflective.fooIIvarargs(1, 2)) + println(reflective.fooNNvarargs(new Num(1), new Num(2))) + println(reflective.fooI_I(1)(2)) + println(reflective.fooN_N(new Num(1))(new Num(2))) + println(reflective.foo0_II()(1, 2)) + println(reflective.foo0_NN()(new Num(1), new Num(2))) + println(reflective.foo0_Ivarargs()(1, 2)) + println(reflective.foo0_Nvarargs()(new Num(1), new Num(2))) + println(reflective.foo0_I_I()(1)(2)) + println(reflective.foo0_N_N()(new Num(1))(new Num(2))) + println() + println(nonreflective.foo) + println(nonreflective.foo0()) + println(nonreflective.fooI(1)) + println(nonreflective.fooN(new Num(1))) + println(nonreflective.fooII(1, 2)) + println(nonreflective.fooNN(new Num(1), new Num(2))) + println(nonreflective.fooIvarargs(1, 2)) + println(nonreflective.fooNvarargs(new Num(1), new Num(2))) + println(nonreflective.fooIseq(Seq(1, 2))) + println(nonreflective.fooNseq(Seq(new Num(1), new Num(2)))) + println(nonreflective.fooIIvarargs(1, 2, 3)) + println(nonreflective.fooNNvarargs(new Num(1), new Num(2))) + println(nonreflective.fooI_I(1)(2)) + println(nonreflective.fooN_N(new Num(1))(new Num(2))) + println(nonreflective.foo0_II()(1, 2)) + println(nonreflective.foo0_NN()(new Num(1), new Num(2))) + println(nonreflective.foo0_Ivarargs()(1, 2)) + println(nonreflective.foo0_Nvarargs()(new Num(1), new Num(2))) + println(nonreflective.foo0_I_I()(1)(2)) + println(nonreflective.foo0_N_N()(new Num(1))(new Num(2))) + println() + println(dynamic0.foo) + println(dynamic1.foo0()) + println(dynamic1.fooI(1)) + println(dynamic1.fooN(new Num(1))) + println(dynamic1.fooII(1, 2)) + println(dynamic1.fooNN(new Num(1), new Num(2))) + println(dynamic1.fooIvarargs(1, 2)) + println(dynamic1.fooNvarargs(new Num(1), new Num(2))) + println(dynamic1.fooIseq(Seq(1, 2))) + println(dynamic1.fooNseq(Seq(new Num(1), new Num(2)))) + println(dynamic1.fooIIvarargs(1, 2)) + println(dynamic1.fooNNvarargs(new Num(1), new Num(2))) + println(dynamic2.fooI_I(1)(2)) + println(dynamic2.fooN_N(new Num(1))(new Num(2))) + println(dynamic2.foo0_II()(1, 2)) + println(dynamic2.foo0_NN()(new Num(1), new Num(2))) + println(dynamic2.foo0_Ivarargs()(1, 2)) + println(dynamic2.foo0_Nvarargs()(new Num(1), new Num(2))) + println(dynamic3.foo0_I_I()(1)(2)) + println(dynamic3.foo0_N_N()(new Num(1))(new Num(2))) + } +} \ No newline at end of file diff --git a/tests/sjs-junit/test/org/scalajs/testsuite/compiler/CustomReflectSelectableTestScala3.scala b/tests/sjs-junit/test/org/scalajs/testsuite/compiler/CustomReflectSelectableTestScala3.scala index 19740a31f48a..a6517088bc82 100644 --- a/tests/sjs-junit/test/org/scalajs/testsuite/compiler/CustomReflectSelectableTestScala3.scala +++ b/tests/sjs-junit/test/org/scalajs/testsuite/compiler/CustomReflectSelectableTestScala3.scala @@ -34,8 +34,12 @@ class CustomReflectSelectableTestScala3 { @Test def callSelectableWithVarargs(): Unit = { val cont2values = Map.empty[String, Any] - val cont2methods = Map[String, (Int, Seq[Bar]) => Int]( - "varargs" -> { (i: Int, bars: Seq[Bar]) => bars.map(_.x).sum + i } + val cont2methods = Map[String, Seq[(Int | Bar | Seq[Bar])] => Int]( + "varargs" -> { (args: Seq[(Int | Bar | Seq[Bar])]) => args.map { + case x: Int => x + case b: Bar => b.x + case bs: Seq[Bar] => bs.map(_.x).sum + } .sum } ) val cont = ScalaSelectable(cont2values, cont2methods).asInstanceOf[ScalaSelectable { def varargs(i: Int, foos: Bar*): Int @@ -45,8 +49,14 @@ class CustomReflectSelectableTestScala3 { @Test def callSelectableWithVarargsExpansion(): Unit = { val cont2values = Map.empty[String, Any] - val cont2methods = Map[String, (Int, Seq[Bar]) => Int]( - "varargs" -> { (i: Int, bars: Seq[Bar]) => bars.map(_.x).sum + i } + val cont2methods = Map[String, Seq[(Int | Bar | Seq[Bar])] => Int]( + "varargs" -> { + (args: Seq[(Int | Bar | Seq[Bar])]) => args.map { + case x: Int => x + case b: Bar => b.x + case bs: Seq[Bar] => bs.map(_.x).sum + }.sum + } ) val cont = ScalaSelectable(cont2values, cont2methods).asInstanceOf[ScalaSelectable { def varargs(i: Int, foos: Bar*): Int @@ -68,9 +78,9 @@ object CustomReflectSelectableTestScala3 { def varargs(x: Int, args: Bar*) = args.map(_.x).sum + x } - class ScalaSelectable(values: Map[String, Any], methods: Map[String, (Int, Seq[Bar]) => Int]) extends Selectable { + class ScalaSelectable(values: Map[String, Any], methods: Map[String, Seq[(Int | Bar | Seq[Bar])] => Int]) extends Selectable { def selectDynamic(name: String): Any = values(name) - def applyDynamic(name: String)(i: Int, bars: Bar*): Int = methods(name)(i, bars) + def applyDynamic(name: String)(args: (Int | Bar | Seq[Bar])*): Int = methods(name)(args) } }