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