Skip to content

Commit 7b32f65

Browse files
committed
Fix #1468: Add type parameter support for scala.Dynamic
1 parent 265ade0 commit 7b32f65

File tree

7 files changed

+85
-35
lines changed

7 files changed

+85
-35
lines changed

src/dotty/tools/dotc/ast/TreeInfo.scala

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -630,20 +630,6 @@ object TreeInfo {
630630
}
631631
}
632632
633-
def isApplyDynamicName(name: Name) = (name == nme.updateDynamic) || (name == nme.selectDynamic) || (name == nme.applyDynamic) || (name == nme.applyDynamicNamed)
634-
635-
class DynamicApplicationExtractor(nameTest: Name => Boolean) {
636-
def unapply(tree: Tree) = tree match {
637-
case Apply(TypeApply(Select(qual, oper), _), List(Literal(Constant(name)))) if nameTest(oper) => Some((qual, name))
638-
case Apply(Select(qual, oper), List(Literal(Constant(name)))) if nameTest(oper) => Some((qual, name))
639-
case Apply(Ident(oper), List(Literal(Constant(name)))) if nameTest(oper) => Some((EmptyTree(), name))
640-
case _ => None
641-
}
642-
}
643-
object DynamicUpdate extends DynamicApplicationExtractor(_ == nme.updateDynamic)
644-
object DynamicApplication extends DynamicApplicationExtractor(isApplyDynamicName)
645-
object DynamicApplicationNamed extends DynamicApplicationExtractor(_ == nme.applyDynamicNamed)
646-
647633
object MacroImplReference {
648634
private def refPart(tree: Tree): Tree = tree match {
649635
case TypeApply(fun, _) => refPart(fun)

src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -578,8 +578,10 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
578578
case ErrorType => tree.withType(ErrorType)
579579
case TryDynamicCallType =>
580580
tree match {
581-
case tree @ Apply(Select(qual, name), args) if !isDynamicMethod(name) =>
582-
typedDynamicApply(qual, name, args, pt)(tree)
581+
case Apply(Select(qual, name), args) if !isDynamicMethod(name) =>
582+
typedDynamicApply(qual, name, None, args, pt)(tree)
583+
case Apply(TypeApply(Select(qual, name), targs), args) if !isDynamicMethod(name) =>
584+
typedDynamicApply(qual, name, Some(targs), args, pt)(tree)
583585
case _ =>
584586
handleUnexpectedFunType(tree, fun1)
585587
}
@@ -667,7 +669,18 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
667669
}
668670
case _ =>
669671
}
670-
assignType(cpy.TypeApply(tree)(typedFn, typedArgs), typedFn, typedArgs)
672+
if (typedFn.tpe eq TryDynamicCallType) {
673+
(pt, typedFn) match {
674+
case (_: FunProto, _)=>
675+
tree.withType(TryDynamicCallType)
676+
case (_, Select(qual, name)) =>
677+
typedDynamicSelect(qual, name, Some(typedArgs), pt)
678+
case _ =>
679+
tree.withType(TryDynamicCallType)
680+
}
681+
} else {
682+
assignType(cpy.TypeApply(tree)(typedFn, typedArgs), typedFn, typedArgs)
683+
}
671684
}
672685

673686
/** Rewrite `new Array[T](....)` if T is an unbounded generic to calls to newGenericArray.

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

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import dotty.tools.dotc.core.Contexts.Context
1010
import dotty.tools.dotc.core.Names.Name
1111
import dotty.tools.dotc.core.StdNames._
1212
import dotty.tools.dotc.core.Types._
13-
import dotty.tools.dotc.core.Mode
1413
import dotty.tools.dotc.core.Decorators._
1514

1615
object Dynamic {
@@ -30,10 +29,12 @@ object Dynamic {
3029
trait Dynamic { self: Typer with Applications =>
3130

3231
/** Translate selection that does not typecheck according to the normal rules into a applyDynamic/applyDynamicNamed.
33-
* foo.bar(baz0, baz1, ...) ~~> foo.applyDynamic(bar)(baz0, baz1, ...)
34-
* foo.bar(x = bazX, y = bazY, baz, ...) ~~> foo.applyDynamicNamed("bar")(("x", bazX), ("y", bazY), ("", baz), ...)
32+
* foo.bar(baz0, baz1, ...) ~~> foo.applyDynamic(bar)(baz0, baz1, ...)
33+
* foo.bar[T0, ...](baz0, baz1, ...) ~~> foo.applyDynamic[T0, ...](bar)(baz0, baz1, ...)
34+
* foo.bar(x = bazX, y = bazY, baz, ...) ~~> foo.applyDynamicNamed("bar")(("x", bazX), ("y", bazY), ("", baz), ...)
35+
* foo.bar[T0, ...](x = bazX, y = bazY, baz, ...) ~~> foo.applyDynamicNamed[T0, ...]("bar")(("x", bazX), ("y", bazY), ("", baz), ...)
3536
*/
36-
def typedDynamicApply(qual: untpd.Tree, name: Name, args: List[untpd.Tree], pt: Type)(original: untpd.Apply)(
37+
def typedDynamicApply(qual: untpd.Tree, name: Name, targsOpt: Option[List[untpd.Tree]], args: List[untpd.Tree], pt: Type)(original: untpd.Apply)(
3738
implicit ctx: Context): Tree = {
3839
def isNamedArg(arg: untpd.Tree): Boolean = arg match { case NamedArg(_, _) => true; case _ => false }
3940
val dynName = if (args.exists(isNamedArg)) nme.applyDynamicNamed else nme.applyDynamic
@@ -47,25 +48,32 @@ trait Dynamic { self: Typer with Applications =>
4748
case arg => namedArgTuple("", arg)
4849
}
4950
val args1 = if (dynName == nme.applyDynamic) args else namedArgs
50-
typedApply(untpd.Apply(coreDynamic(qual, dynName, name), args1), pt)
51+
typedApply(untpd.Apply(coreDynamic(qual, dynName, name, targsOpt), args1), pt)
5152
}
5253
}
5354

5455
/** Translate selection that does not typecheck according to the normal rules into a selectDynamic.
55-
* foo.bar ~~> foo.selectDynamic(bar)
56+
* foo.bar ~~> foo.selectDynamic(bar)
57+
* foo.bar[T0, ...] ~~> foo.selectDynamic[T0, ...](bar)
5658
*
5759
* Note: inner part of translation foo.bar(baz) = quux ~~> foo.selectDynamic(bar).update(baz, quux) is achieved
5860
* through an existing transformation of in typedAssign [foo.bar(baz) = quux ~~> foo.bar.update(baz, quux)].
5961
*/
60-
def typedDynamicSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree =
61-
typedApply(coreDynamic(tree.qualifier, nme.selectDynamic, tree.name), pt)
62+
def typedDynamicSelect(qualifier: untpd.Tree, name: Name, targsOpt: Option[List[Tree]], pt: Type)(implicit ctx: Context): Tree =
63+
typedApply(coreDynamic(qualifier, nme.selectDynamic, name, targsOpt), pt)
6264

6365
/** Translate selection that does not typecheck according to the normal rules into a updateDynamic.
6466
* foo.bar = baz ~~> foo.updateDynamic(bar)(baz)
6567
*/
66-
def typedDynamicAssign(qual: untpd.Tree, name: Name, rhs: untpd.Tree, pt: Type)(implicit ctx: Context): Tree =
67-
typedApply(untpd.Apply(coreDynamic(qual, nme.updateDynamic, name), rhs), pt)
68+
def typedDynamicAssign(qual: untpd.Tree, name: Name, targsOpt: Option[List[untpd.Tree]], rhs: untpd.Tree, pt: Type)(implicit ctx: Context): Tree =
69+
typedApply(untpd.Apply(coreDynamic(qual, nme.updateDynamic, name, targsOpt), rhs), pt)
6870

69-
private def coreDynamic(qual: untpd.Tree, dynName: Name, name: Name)(implicit ctx: Context): untpd.Apply =
70-
untpd.Apply(untpd.Select(qual, dynName), Literal(Constant(name.toString)))
71+
private def coreDynamic(qual: untpd.Tree, dynName: Name, name: Name, targsOpt: Option[List[untpd.Tree]])(implicit ctx: Context): untpd.Apply = {
72+
val select = untpd.Select(qual, dynName)
73+
val selectWithTypes = targsOpt match {
74+
case Some(targs) => untpd.TypeApply(select, targs)
75+
case None => select
76+
}
77+
untpd.Apply(selectWithTypes, Literal(Constant(name.toString)))
78+
}
7179
}

src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -318,10 +318,10 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
318318
if (tree.name.isTypeName) checkStable(qual1.tpe, qual1.pos)
319319
val select = typedSelect(tree, pt, qual1)
320320
pt match {
321-
case _: FunProto | AssignProto => select
322-
case _ =>
323-
if (select.tpe eq TryDynamicCallType) typedDynamicSelect(tree, pt)
324-
else select
321+
case _ if select.tpe ne TryDynamicCallType => select
322+
case _: FunProto | AssignProto => select
323+
case PolyProto(_,_) => select
324+
case _ => typedDynamicSelect(tree.qualifier, tree.name, None, pt)
325325
}
326326
}
327327

@@ -514,7 +514,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
514514
case TryDynamicCallType =>
515515
tree match {
516516
case Assign(Select(qual, name), rhs) if !isDynamicMethod(name) =>
517-
typedDynamicAssign(qual, name, rhs, pt)
517+
typedDynamicAssign(qual, name, None, rhs, pt)
518+
case Assign(TypeApply(Select(qual, name), targs), rhs) if !isDynamicMethod(name) =>
519+
typedDynamicAssign(qual, name, Some(targs), rhs, pt)
518520
case _ => reassignmentToVal
519521
}
520522
case tpe =>
File renamed without changes.

tests/untried/neg/t6663.scala renamed to tests/neg/t6663.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ object Test extends App {
1313
// but, before fixing SI-6663, became
1414
// C(42).selectDynamic("foo").get, ignoring
1515
// the [String] type parameter
16-
var v = new C(42).foo[String].get :Int
16+
var v = new C(42).foo[String].get :Int // error
1717
println(v)
1818
}
1919

tests/run/dynamicDynamicTests.scala

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,16 @@ class Baz extends scala.Dynamic {
2323
def updateDynamic(name: String)(value: String): String = "updateDynamic(" + name + ")(" + value + ")"
2424
}
2525

26+
class Qux extends scala.Dynamic {
27+
def selectDynamic[T](name: String): String = "selectDynamic(" + name + ")"
28+
def applyDynamic[T](name: String)(args: String*): String = "applyDynamic(" + name + ")" + args.mkString("(", ", ", ")")
29+
def applyDynamicNamed[T](name: String)(args: (String, Any)*): String = "applyDynamicNamed(" + name + ")" + args.mkString("(", ", ", ")")
30+
def updateDynamic[T](name: String)(value: T): String = "updateDynamic(" + name + ")(" + value + ")"
31+
}
32+
2633
object Test {
34+
val qux = new Qux
35+
2736
implicit class StringUpdater(str: String) {
2837
def update(name: String, v: String) = s"$str.update(" + name + ", " + v + ")"
2938
}
@@ -42,6 +51,7 @@ object Test {
4251
runFooTests2()
4352
runBarTests()
4453
runBazTests()
54+
runQuxTests()
4555
assert(!failed)
4656
}
4757

@@ -161,4 +171,35 @@ object Test {
161171
assertEquals("selectDynamic(bazSelectUpdate).update(7, value)", baz.bazSelectUpdate(7) = "value")
162172
assertEquals("selectDynamic(bazSelectUpdate).update(7, 10)", baz.bazSelectUpdate(7) = 10)
163173
}
174+
175+
/** Test correct lifting of type parameters */
176+
def runQuxTests() = {
177+
implicit def intToString(n: Int): String = n.toString
178+
179+
val qux = new Qux
180+
181+
assertEquals("selectDynamic(quxSelect)", qux.quxSelect)
182+
assertEquals("selectDynamic(quxSelect)", qux.quxSelect[Int])
183+
184+
assertEquals("applyDynamic(quxApply)()", qux.quxApply())
185+
assertEquals("applyDynamic(quxApply)()", qux.quxApply[Int]())
186+
assertEquals("applyDynamic(quxApply)(1)", qux.quxApply(1))
187+
assertEquals("applyDynamic(quxApply)(1)", qux.quxApply[Int](1))
188+
assertEquals("applyDynamic(quxApply)(1, 2, 3)", qux.quxApply(1, 2, 3))
189+
assertEquals("applyDynamic(quxApply)(1, 2, 3)", qux.quxApply[Int](1, 2, 3))
190+
assertEquals("applyDynamic(quxApply)(1, 2, a)", qux.quxApply(1, 2, "a"))
191+
assertEquals("applyDynamic(quxApply)(1, 2, a)", qux.quxApply[Int](1, 2, "a"))
192+
193+
assertEquals("applyDynamicNamed(quxApplyNamed)((a,1))", qux.quxApplyNamed(a = 1))
194+
assertEquals("applyDynamicNamed(quxApplyNamed)((a,1))", qux.quxApplyNamed[Int](a = 1))
195+
assertEquals("applyDynamicNamed(quxApplyNamed)((a,1), (b,2))", qux.quxApplyNamed(a = 1, b = "2"))
196+
assertEquals("applyDynamicNamed(quxApplyNamed)((a,1), (b,2))", qux.quxApplyNamed[Int](a = 1, b = "2"))
197+
assertEquals("applyDynamicNamed(quxApplyNamed)((a,1), (,abc))", qux.quxApplyNamed(a = 1, "abc"))
198+
assertEquals("applyDynamicNamed(quxApplyNamed)((a,1), (,abc))", qux.quxApplyNamed[Int](a = 1, "abc"))
199+
200+
assertEquals("updateDynamic(quxUpdate)(abc)", qux.quxUpdate = "abc")
201+
202+
assertEquals("selectDynamic(quxSelectUpdate).update(key, value)", qux.quxSelectUpdate("key") = "value")
203+
assertEquals("selectDynamic(quxSelectUpdate).update(key, value)", qux.quxSelectUpdate[Int]("key") = "value")
204+
}
164205
}

0 commit comments

Comments
 (0)