From b5b491f79b3195df1dbec7a6dd9eccc4a141ed89 Mon Sep 17 00:00:00 2001 From: odersky Date: Wed, 15 Mar 2023 11:35:57 +0100 Subject: [PATCH] Allow selectDynamic and applyDynamic to be an extension methods Allow selectDynamic and applyDynamic to be an extension methods when dispatching structurally. Fixes #17100 --- compiler/src/dotty/tools/dotc/typer/Dynamic.scala | 8 ++++---- tests/pos/i17100.scala | 10 ++++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) create mode 100644 tests/pos/i17100.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala index 8ec4e9416151..1513872cf47a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala +++ b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala @@ -186,7 +186,7 @@ trait Dynamic { // ($qual: Selectable).$selectorName("$name") val base = untpd.Apply( - untpd.TypedSplice(selectable.select(selectorName)).withSpan(fun.span), + untpd.Select(untpd.TypedSplice(selectable), selectorName).withSpan(fun.span), (Literal(Constant(name.encode.toString)) :: Nil).map(untpd.TypedSplice(_))) val scall = @@ -219,19 +219,19 @@ trait Dynamic { extension (tree: Tree) /** The implementations of `selectDynamic` and `applyDynamic` in `scala.reflect.SelectDynamic` have no information about the expected return type of a value/method which was declared in the refinement, * only the JVM type after erasure can be obtained through reflection, e.g. - * + * * class Foo(val i: Int) extends AnyVal * class Reflective extends reflect.Selectable * val reflective = new Reflective { * def foo = Foo(1) // Foo at compile time, java.lang.Integer in reflection * } - * + * * Because of that reflective access cannot be implemented properly in `scala.reflect.SelectDynamic` itself * because it's not known there if the value should be wrapped in a value class constructor call or not. * Hence the logic of wrapping is performed here, relying on the fact that the implementations of `selectDynamic` and `applyDynamic` in `scala.reflect.SelectDynamic` are final. */ def maybeBoxingCast(tpe: Type) = - val maybeBoxed = + val maybeBoxed = if ValueClasses.isDerivedValueClass(tpe.classSymbol) && qual.tpe <:< defn.ReflectSelectableTypeRef then val genericUnderlying = ValueClasses.valueClassUnbox(tpe.classSymbol.asClass) val underlying = tpe.select(genericUnderlying).widen.resultType diff --git a/tests/pos/i17100.scala b/tests/pos/i17100.scala new file mode 100644 index 000000000000..2fd01bcbe5ff --- /dev/null +++ b/tests/pos/i17100.scala @@ -0,0 +1,10 @@ +trait Sel extends Selectable + +extension (s: Sel) + def selectDynamic(name: String) = ??? + def applyDynamic(name: String)(x: Int) = ??? + +val sel = (new Sel {}).asInstanceOf[Sel{ def foo: String; def bar(x: Int): Int }] +val foo = sel.selectDynamic("foo") +val foo2 = sel.foo +val foo3 = sel.bar(2)