Skip to content

Commit ac126dd

Browse files
committed
add support for curried applyDynamic methods
1 parent 2d57200 commit ac126dd

File tree

4 files changed

+37
-17
lines changed

4 files changed

+37
-17
lines changed

compiler/src/dotty/tools/dotc/typer/Dynamic.scala

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ package dotc
33
package typer
44

55
import dotty.tools.dotc.ast.Trees.*
6-
import dotty.tools.dotc.ast.tpd
7-
import dotty.tools.dotc.ast.untpd
6+
import dotty.tools.dotc.ast.{tpd, untpd}
87
import dotty.tools.dotc.core.Constants.Constant
98
import dotty.tools.dotc.core.Contexts.*
109
import dotty.tools.dotc.core.Names.{Name, TermName}
@@ -180,18 +179,20 @@ trait Dynamic {
180179
val fun @ Select(qual, name) = funPart(tree): @unchecked
181180
val vargss = termArgss(tree)
182181

183-
def handleRepeated(args: List[List[Tree]]) =
184-
val isRepeated = args.flatten.exists(_.tpe.widen.isRepeatedParam)
185-
if isRepeated && qual.tpe <:< defn.ReflectSelectableTypeRef then
186-
List(untpd.TypedSplice(tpd.repeatedSeq(args.flatten, TypeTree(defn.AnyType))))
187-
else args.flatten.map { t =>
188-
val clzSym = t.tpe.resultType.classSymbol.asClass
189-
if ValueClasses.isDerivedValueClass(clzSym) then
190-
val underlying = ValueClasses.valueClassUnbox(clzSym).asTerm
191-
tpd.Select(t, underlying.name)
192-
else
193-
t
194-
}.map(untpd.TypedSplice(_))
182+
def handleRepeated(base: Tree, possiblyCurried: List[List[Tree]]) =
183+
possiblyCurried.map { args =>
184+
val isRepeated = args.exists(_.tpe.widen.isRepeatedParam)
185+
if isRepeated && qual.tpe <:< defn.ReflectSelectableTypeRef then
186+
List(untpd.TypedSplice(tpd.repeatedSeq(args, TypeTree(defn.AnyType))))
187+
else args.map { t =>
188+
val clzSym = t.tpe.resultType.classSymbol.asClass
189+
if ValueClasses.isDerivedValueClass(clzSym) && qual.tpe <:< defn.ReflectSelectableTypeRef then
190+
val underlying = ValueClasses.valueClassUnbox(clzSym).asTerm
191+
tpd.Select(t, underlying.name)
192+
else
193+
t
194+
}.map(untpd.TypedSplice(_))
195+
}.foldLeft(base)((base, args) => untpd.Apply(base, args))
195196

196197
def structuralCall(selectorName: TermName, classOfs: => List[Tree]) = {
197198
val selectable = adapt(qual, defn.SelectableClass.typeRef | defn.DynamicClass.typeRef)
@@ -204,7 +205,7 @@ trait Dynamic {
204205

205206
val scall =
206207
if (vargss.isEmpty) base
207-
else untpd.Apply(base, handleRepeated(vargss))
208+
else handleRepeated(base, vargss)
208209

209210
// If function is an `applyDynamic` that takes a Class* parameter,
210211
// add `classOfs`.

docs/_docs/reference/changed-features/structural-types-spec.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,18 @@ Both versions are passed the actual arguments in the `args` parameter. The secon
5555
if `applyDynamic` is implemented using Java reflection, but it could be
5656
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.
5757

58+
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.
59+
To overcome this they should be wrapped in `Seq(elems)` to be considered as multiple parameters at callsite of `scala.reflect.Selectable.applyDynamic`.
60+
```scala
61+
class Reflective extends reflect.Selectable
62+
class Foo(val i: Int) extends AnyVal
63+
val reflective = new Reflective {
64+
def varargs(x: Int, foo: Foo*) = foo.map(_.i).sum + x
65+
}
66+
val varargs = List(Foo(1), Foo(2), Foo(3))
67+
reflective.varargs(1, varargs:_*)
68+
```
69+
5870
Given a value `v` of type `C { Rs }`, where `C` is a class reference
5971
and `Rs` are structural refinement declarations, and given `v.a` of type `U`, we consider three distinct cases:
6072

tests/run/i16995.check

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ check
44
7
55
5
66
3
7-
3
7+
3
8+
7

tests/run/i16995.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ class ScalaSelectable(values: Map[String, Any], methods: Map[String, (Int, Seq[F
66
def selectDynamic(name: String): Any = values(name)
77

88
def applyDynamic(name: String)(i: Int, foos: Foo*): Int = methods(name)(i, foos)
9+
10+
def applyDynamic(name: String)(foo: Foo)(argument: Argument)(someInt: Int): Int = foo.i + argument.x.length + someInt
911
}
1012

1113
@main def Test: Unit =
@@ -15,6 +17,7 @@ class ScalaSelectable(values: Map[String, Any], methods: Map[String, (Int, Seq[F
1517
def manyArgs(argument: Argument, foo: Foo, someInt: Int) = foo.i + someInt + argument.x.length
1618
def varargs(x: Int, foo: Foo*) = foo.map(_.i).sum + x
1719
def letsHaveSeq(args: Seq[Argument]) = args.map(_.x.length).sum
20+
def curried(foo: Foo)(arg1: Argument)(someInt: Int): Int = foo.i + arg1.x.length + someInt
1821
}
1922

2023
val i = reflective.bar(Foo(1))
@@ -53,6 +56,9 @@ class ScalaSelectable(values: Map[String, Any], methods: Map[String, (Int, Seq[F
5356

5457
val cont2 = ScalaSelectable(cont2values, cont2methods).asInstanceOf[ScalaSelectable {
5558
def varargs(i: Int, foos: Foo*): Int
59+
def curried(foo: Foo)(argument: Argument)(someInt: Int): Int
5660
}]
5761

58-
println(cont2.varargs(1, Foo(1), Foo(1)))
62+
println(cont2.varargs(1, Foo(1), Foo(1)))
63+
64+
println(cont2.curried(Foo(1))(Argument("123"))(3))

0 commit comments

Comments
 (0)