Skip to content

Commit 6c05e6c

Browse files
authored
Allow selectDynamic and applyDynamic to be extension methods (#17106)
Allow selectDynamic and applyDynamic to be extension methods when dispatching structurally. Fixes #17100
2 parents 2dec682 + fa29ba5 commit 6c05e6c

File tree

5 files changed

+40
-10
lines changed

5 files changed

+40
-10
lines changed

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -181,12 +181,12 @@ trait Dynamic {
181181
val vargss = termArgss(tree)
182182

183183
def structuralCall(selectorName: TermName, classOfs: => List[Tree]) = {
184-
val selectable = adapt(qual, defn.SelectableClass.typeRef)
184+
val selectable = adapt(qual, defn.SelectableClass.typeRef | defn.DynamicClass.typeRef)
185185

186186
// ($qual: Selectable).$selectorName("$name")
187187
val base =
188188
untpd.Apply(
189-
untpd.TypedSplice(selectable.select(selectorName)).withSpan(fun.span),
189+
untpd.Select(untpd.TypedSplice(selectable), selectorName).withSpan(fun.span),
190190
(Literal(Constant(name.encode.toString)) :: Nil).map(untpd.TypedSplice(_)))
191191

192192
val scall =
@@ -219,19 +219,19 @@ trait Dynamic {
219219
extension (tree: Tree)
220220
/** 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,
221221
* only the JVM type after erasure can be obtained through reflection, e.g.
222-
*
222+
*
223223
* class Foo(val i: Int) extends AnyVal
224224
* class Reflective extends reflect.Selectable
225225
* val reflective = new Reflective {
226226
* def foo = Foo(1) // Foo at compile time, java.lang.Integer in reflection
227227
* }
228-
*
228+
*
229229
* Because of that reflective access cannot be implemented properly in `scala.reflect.SelectDynamic` itself
230230
* because it's not known there if the value should be wrapped in a value class constructor call or not.
231231
* 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.
232232
*/
233233
def maybeBoxingCast(tpe: Type) =
234-
val maybeBoxed =
234+
val maybeBoxed =
235235
if ValueClasses.isDerivedValueClass(tpe.classSymbol) && qual.tpe <:< defn.ReflectSelectableTypeRef then
236236
val genericUnderlying = ValueClasses.valueClassUnbox(tpe.classSymbol.asClass)
237237
val underlying = tpe.select(genericUnderlying).widen.resultType

tests/pos/i17100.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
trait Sel extends Selectable
2+
3+
extension (s: Sel)
4+
def selectDynamic(name: String) = ???
5+
def applyDynamic(name: String)(x: Int) = ???
6+
def applyDynamic(name: String)() = ???
7+
8+
val sel = (new Sel {}).asInstanceOf[Sel{ def foo: String; def bar(x: Int): Int; def baz(): Int }]
9+
val foo = sel.selectDynamic("foo")
10+
val foo2 = sel.foo
11+
val foo3 = sel.bar(2)
12+
val foo4 = sel.baz()
13+
14+

tests/pos/i17100a.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
import scala.language.dynamics
3+
trait Sel extends Dynamic
4+
5+
extension (s: Sel)
6+
def selectDynamic(name: String) = ???
7+
8+
val sel = new Sel {}
9+
val foo = sel.foo
10+
val sel2 = (new Sel {}).asInstanceOf[Sel{ def foo: String }]
11+
val foo2 = sel2.foo
12+

tests/semanticdb/expect/Advanced.expect.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ class Wildcards/*<-advanced::Wildcards#*/ {
2525
object Test/*<-advanced::Test.*/ {
2626
val s/*<-advanced::Test.s.*/ = new Structural/*->advanced::Structural#*/
2727
val s1/*<-advanced::Test.s1.*/ = s/*->advanced::Test.s.*/.s1/*->advanced::Structural#s1().*/
28-
val s1x/*<-advanced::Test.s1x.*/ = s/*->advanced::Test.s.*/.s1/*->advanced::Structural#s1().*/.x
28+
val s1x/*<-advanced::Test.s1x.*/ = s/*->advanced::Test.s.*/.s1/*->advanced::Structural#s1().*/.x/*->scala::reflect::Selectable#selectDynamic().*/
2929
val s2/*<-advanced::Test.s2.*/ = s/*->advanced::Test.s.*/.s2/*->advanced::Structural#s2().*/
30-
val s2x/*<-advanced::Test.s2x.*/ = s/*->advanced::Test.s.*/.s2/*->advanced::Structural#s2().*/.x
30+
val s2x/*<-advanced::Test.s2x.*/ = s/*->advanced::Test.s.*/.s2/*->advanced::Structural#s2().*/.x/*->scala::reflect::Selectable#selectDynamic().*/
3131
val s3/*<-advanced::Test.s3.*/ = s/*->advanced::Test.s.*/.s3/*->advanced::Structural#s3().*/
32-
val s3x/*<-advanced::Test.s3x.*/ = s/*->advanced::Test.s.*/.s3/*->advanced::Structural#s3().*/.m(???/*->scala::Predef.`???`().*/)
32+
val s3x/*<-advanced::Test.s3x.*/ = s/*->advanced::Test.s.*/.s3/*->advanced::Structural#s3().*/.m/*->scala::reflect::Selectable#applyDynamic().*/(???/*->scala::Predef.`???`().*/)
3333

3434
val e/*<-advanced::Test.e.*/ = new Wildcards/*->advanced::Wildcards#*/
3535
val e1/*<-advanced::Test.e1.*/ = e/*->advanced::Test.e.*/.e1/*->advanced::Wildcards#e1().*/
@@ -45,7 +45,7 @@ object Test/*<-advanced::Test.*/ {
4545

4646
// see: https://github.com/lampepfl/dotty/pull/14608#discussion_r835642563
4747
lazy val foo/*<-advanced::Test.foo.*/: (reflect.Selectable/*->scala::reflect::Selectable#*/ { type A/*<-local16*/ = Int/*->scala::Int#*/ }) &/*->scala::`&`#*/ (reflect.Selectable/*->scala::reflect::Selectable#*/ { type A/*<-local17*/ = Int/*->scala::Int#*/; val a/*<-local18*/: A/*->local17*/ }) = ???/*->scala::Predef.`???`().*/
48-
def bar/*<-advanced::Test.bar().*/: foo/*->advanced::Test.foo.*/.A/*->local17*/ = foo/*->advanced::Test.foo.*/.a
48+
def bar/*<-advanced::Test.bar().*/: foo/*->advanced::Test.foo.*/.A/*->local17*/ = foo/*->advanced::Test.foo.*/.a/*->scala::reflect::Selectable#selectDynamic().*/
4949
}
5050

5151

tests/semanticdb/metac.expect

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ Uri => Advanced.scala
4949
Text => empty
5050
Language => Scala
5151
Symbols => 60 entries
52-
Occurrences => 134 entries
52+
Occurrences => 138 entries
5353
Synthetics => 3 entries
5454

5555
Symbols:
@@ -187,18 +187,21 @@ Occurrences:
187187
[27:6..27:9): s1x <- advanced/Test.s1x.
188188
[27:12..27:13): s -> advanced/Test.s.
189189
[27:14..27:16): s1 -> advanced/Structural#s1().
190+
[27:16..27:18): .x -> scala/reflect/Selectable#selectDynamic().
190191
[28:6..28:8): s2 <- advanced/Test.s2.
191192
[28:11..28:12): s -> advanced/Test.s.
192193
[28:13..28:15): s2 -> advanced/Structural#s2().
193194
[29:6..29:9): s2x <- advanced/Test.s2x.
194195
[29:12..29:13): s -> advanced/Test.s.
195196
[29:14..29:16): s2 -> advanced/Structural#s2().
197+
[29:16..29:18): .x -> scala/reflect/Selectable#selectDynamic().
196198
[30:6..30:8): s3 <- advanced/Test.s3.
197199
[30:11..30:12): s -> advanced/Test.s.
198200
[30:13..30:15): s3 -> advanced/Structural#s3().
199201
[31:6..31:9): s3x <- advanced/Test.s3x.
200202
[31:12..31:13): s -> advanced/Test.s.
201203
[31:14..31:16): s3 -> advanced/Structural#s3().
204+
[31:16..31:18): .m -> scala/reflect/Selectable#applyDynamic().
202205
[31:19..31:22): ??? -> scala/Predef.`???`().
203206
[33:6..33:7): e <- advanced/Test.e.
204207
[33:14..33:23): Wildcards -> advanced/Wildcards#
@@ -233,6 +236,7 @@ Occurrences:
233236
[47:11..47:14): foo -> advanced/Test.foo.
234237
[47:15..47:16): A -> local17
235238
[47:19..47:22): foo -> advanced/Test.foo.
239+
[47:22..47:24): .a -> scala/reflect/Selectable#selectDynamic().
236240
[52:6..52:13): HKClass <- advanced/HKClass#
237241
[52:14..52:15): F <- advanced/HKClass#[F]
238242
[52:20..52:21): T <- advanced/HKClass#`<init>`().[F][T]

0 commit comments

Comments
 (0)