Skip to content

Commit 9ae9a20

Browse files
authored
Merge pull request #1469 from dotty-staging/fix-scala.Dynamic
Fixes for scala.Dynamic
2 parents 5d8f132 + 0ee74cc commit 9ae9a20

39 files changed

+215
-181
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: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -591,13 +591,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
591591

592592
fun1.tpe match {
593593
case ErrorType => tree.withType(ErrorType)
594-
case TryDynamicCallType =>
595-
tree match {
596-
case tree @ Apply(Select(qual, name), args) if !isDynamicMethod(name) =>
597-
typedDynamicApply(qual, name, args, pt)(tree)
598-
case _ =>
599-
handleUnexpectedFunType(tree, fun1)
600-
}
594+
case TryDynamicCallType => typedDynamicApply(tree, pt)
601595
case _ =>
602596
tryEither {
603597
implicit ctx => simpleApply(fun1, proto)
@@ -679,7 +673,12 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
679673
}
680674
case _ =>
681675
}
682-
assignType(cpy.TypeApply(tree)(typedFn, typedArgs), typedFn, typedArgs)
676+
def tryDynamicTypeApply(): Tree = typedFn match {
677+
case typedFn: Select if !pt.isInstanceOf[FunProto] => typedDynamicSelect(typedFn, typedArgs, pt)
678+
case _ => tree.withType(TryDynamicCallType)
679+
}
680+
if (typedFn.tpe eq TryDynamicCallType) tryDynamicTypeApply()
681+
else assignType(cpy.TypeApply(tree)(typedFn, typedArgs), typedFn, typedArgs)
683682
}
684683

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

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

Lines changed: 59 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@ package dotty.tools
22
package dotc
33
package typer
44

5-
import dotty.tools.dotc.ast.Trees.NamedArg
6-
import dotty.tools.dotc.ast.tpd._
5+
import dotty.tools.dotc.ast.Trees._
6+
import dotty.tools.dotc.ast.tpd
77
import dotty.tools.dotc.ast.untpd
88
import dotty.tools.dotc.core.Constants.Constant
99
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 {
@@ -28,44 +27,78 @@ object Dynamic {
2827
* The first matching rule of is applied.
2928
*/
3029
trait Dynamic { self: Typer with Applications =>
30+
import Dynamic._
31+
import tpd._
3132

3233
/** 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), ...)
34+
* foo.bar(baz0, baz1, ...) ~~> foo.applyDynamic(bar)(baz0, baz1, ...)
35+
* foo.bar[T0, ...](baz0, baz1, ...) ~~> foo.applyDynamic[T0, ...](bar)(baz0, baz1, ...)
36+
* foo.bar(x = bazX, y = bazY, baz, ...) ~~> foo.applyDynamicNamed("bar")(("x", bazX), ("y", bazY), ("", baz), ...)
37+
* foo.bar[T0, ...](x = bazX, y = bazY, baz, ...) ~~> foo.applyDynamicNamed[T0, ...]("bar")(("x", bazX), ("y", bazY), ("", baz), ...)
3538
*/
36-
def typedDynamicApply(qual: untpd.Tree, name: Name, args: List[untpd.Tree], pt: Type)(original: untpd.Apply)(
37-
implicit ctx: Context): Tree = {
38-
def isNamedArg(arg: untpd.Tree): Boolean = arg match { case NamedArg(_, _) => true; case _ => false }
39-
val dynName = if (args.exists(isNamedArg)) nme.applyDynamicNamed else nme.applyDynamic
40-
if (dynName == nme.applyDynamicNamed && untpd.isWildcardStarArgList(args)) {
41-
ctx.error("applyDynamicNamed does not support passing a vararg parameter", original.pos)
42-
original.withType(ErrorType)
43-
} else {
44-
def namedArgTuple(name: String, arg: untpd.Tree) = untpd.Tuple(List(Literal(Constant(name)), arg))
45-
def namedArgs = args.map {
46-
case NamedArg(argName, arg) => namedArgTuple(argName.toString, arg)
47-
case arg => namedArgTuple("", arg)
39+
def typedDynamicApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = {
40+
def typedDynamicApply(qual: untpd.Tree, name: Name, targs: List[untpd.Tree]): Tree = {
41+
def isNamedArg(arg: untpd.Tree): Boolean = arg match { case NamedArg(_, _) => true; case _ => false }
42+
val args = tree.args
43+
val dynName = if (args.exists(isNamedArg)) nme.applyDynamicNamed else nme.applyDynamic
44+
if (dynName == nme.applyDynamicNamed && untpd.isWildcardStarArgList(args)) {
45+
ctx.error("applyDynamicNamed does not support passing a vararg parameter", tree.pos)
46+
tree.withType(ErrorType)
47+
} else {
48+
def namedArgTuple(name: String, arg: untpd.Tree) = untpd.Tuple(List(Literal(Constant(name)), arg))
49+
def namedArgs = args.map {
50+
case NamedArg(argName, arg) => namedArgTuple(argName.toString, arg)
51+
case arg => namedArgTuple("", arg)
52+
}
53+
val args1 = if (dynName == nme.applyDynamic) args else namedArgs
54+
typedApply(untpd.Apply(coreDynamic(qual, dynName, name, targs), args1), pt)
4855
}
49-
val args1 = if (dynName == nme.applyDynamic) args else namedArgs
50-
typedApply(untpd.Apply(coreDynamic(qual, dynName, name), args1), pt)
56+
}
57+
58+
tree.fun match {
59+
case Select(qual, name) if !isDynamicMethod(name) =>
60+
typedDynamicApply(qual, name, Nil)
61+
case TypeApply(Select(qual, name), targs) if !isDynamicMethod(name) =>
62+
typedDynamicApply(qual, name, targs)
63+
case TypeApply(fun, targs) =>
64+
typedDynamicApply(fun, nme.apply, targs)
65+
case fun =>
66+
typedDynamicApply(fun, nme.apply, Nil)
5167
}
5268
}
5369

5470
/** Translate selection that does not typecheck according to the normal rules into a selectDynamic.
55-
* foo.bar ~~> foo.selectDynamic(bar)
71+
* foo.bar ~~> foo.selectDynamic(bar)
72+
* foo.bar[T0, ...] ~~> foo.selectDynamic[T0, ...](bar)
5673
*
5774
* Note: inner part of translation foo.bar(baz) = quux ~~> foo.selectDynamic(bar).update(baz, quux) is achieved
5875
* through an existing transformation of in typedAssign [foo.bar(baz) = quux ~~> foo.bar.update(baz, quux)].
5976
*/
60-
def typedDynamicSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree =
61-
typedApply(coreDynamic(tree.qualifier, nme.selectDynamic, tree.name), pt)
77+
def typedDynamicSelect(tree: untpd.Select, targs: List[Tree], pt: Type)(implicit ctx: Context): Tree =
78+
typedApply(coreDynamic(tree.qualifier, nme.selectDynamic, tree.name, targs), pt)
6279

6380
/** Translate selection that does not typecheck according to the normal rules into a updateDynamic.
6481
* foo.bar = baz ~~> foo.updateDynamic(bar)(baz)
6582
*/
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)
83+
def typedDynamicAssign(tree: untpd.Assign, pt: Type)(implicit ctx: Context): Tree = {
84+
def typedDynamicAssign(qual: untpd.Tree, name: Name, targs: List[untpd.Tree]): Tree =
85+
typedApply(untpd.Apply(coreDynamic(qual, nme.updateDynamic, name, targs), tree.rhs), pt)
86+
tree.lhs match {
87+
case Select(qual, name) if !isDynamicMethod(name) =>
88+
typedDynamicAssign(qual, name, Nil)
89+
case TypeApply(Select(qual, name), targs) if !isDynamicMethod(name) =>
90+
typedDynamicAssign(qual, name, targs)
91+
case _ =>
92+
ctx.error("reassignment to val", tree.pos)
93+
tree.withType(ErrorType)
94+
}
95+
}
6896

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)))
97+
private def coreDynamic(qual: untpd.Tree, dynName: Name, name: Name, targs: List[untpd.Tree])(implicit ctx: Context): untpd.Apply = {
98+
val select = untpd.Select(qual, dynName)
99+
val selectWithTypes =
100+
if (targs.isEmpty) select
101+
else untpd.TypeApply(select, targs)
102+
untpd.Apply(selectWithTypes, Literal(Constant(name.toString)))
103+
}
71104
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,9 @@ trait TypeAssigner {
168168
val d2 = pre.nonPrivateMember(name)
169169
if (reallyExists(d2) && firstTry)
170170
test(tpe.shadowed.withDenot(d2), false)
171-
else {
171+
else if (pre.derivesFrom(defn.DynamicClass)) {
172+
TryDynamicCallType
173+
} else {
172174
val alts = tpe.denot.alternatives.map(_.symbol).filter(_.exists)
173175
val what = alts match {
174176
case Nil =>

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

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -317,12 +317,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
317317
val qual1 = typedExpr(tree.qualifier, selectionProto(tree.name, pt, this))
318318
if (tree.name.isTypeName) checkStable(qual1.tpe, qual1.pos)
319319
val select = typedSelect(tree, pt, qual1)
320-
pt match {
321-
case _: FunProto | AssignProto => select
322-
case _ =>
323-
if (select.tpe eq TryDynamicCallType) typedDynamicSelect(tree, pt)
324-
else select
325-
}
320+
if (select.tpe ne TryDynamicCallType) select
321+
else if (pt.isInstanceOf[PolyProto] || pt.isInstanceOf[FunProto] || pt == AssignProto) select
322+
else typedDynamicSelect(tree, Nil, pt)
326323
}
327324

328325
def asJavaSelectFromTypeTree(implicit ctx: Context): Tree = {
@@ -518,11 +515,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
518515
reassignmentToVal
519516
}
520517
case TryDynamicCallType =>
521-
tree match {
522-
case Assign(Select(qual, name), rhs) if !isDynamicMethod(name) =>
523-
typedDynamicAssign(qual, name, rhs, pt)
524-
case _ => reassignmentToVal
525-
}
518+
typedDynamicAssign(tree, pt)
526519
case tpe =>
527520
reassignmentToVal
528521
}

tests/neg/applydynamic_sip.check

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
tests/neg/applydynamic_sip.scala:8: error: value applyDynamic is not a member of Dynamic(Test.qual)
2+
possible cause: maybe a wrong Dynamic method signature?
3+
qual.sel(a, a2: _*) // error
4+
^
5+
tests/neg/applydynamic_sip.scala:9: error: applyDynamicNamed does not support passing a vararg parameter
6+
qual.sel(arg = a, a2: _*) // error
7+
^
8+
tests/neg/applydynamic_sip.scala:10: error: applyDynamicNamed does not support passing a vararg parameter
9+
qual.sel(arg, arg2 = "a2", a2: _*) // error
10+
^
11+
tests/neg/applydynamic_sip.scala:20: error: type mismatch:
12+
found : String("sel")
13+
required: Int
14+
bad1.sel // error
15+
^
16+
tests/neg/applydynamic_sip.scala:21: error: type mismatch:
17+
found : String("sel")
18+
required: Int
19+
bad1.sel(1) // error // error
20+
^
21+
tests/neg/applydynamic_sip.scala:21: error: method applyDynamic in class Bad1 does not take more parameters
22+
bad1.sel(1) // error // error
23+
^
24+
tests/neg/applydynamic_sip.scala:22: error: type mismatch:
25+
found : String("sel")
26+
required: Int
27+
bad1.sel(a = 1) // error // error
28+
^
29+
tests/neg/applydynamic_sip.scala:22: error: method applyDynamicNamed in class Bad1 does not take more parameters
30+
bad1.sel(a = 1) // error // error
31+
^
32+
tests/neg/applydynamic_sip.scala:23: error: type mismatch:
33+
found : String("sel")
34+
required: Int
35+
bad1.sel = 1 // error // error
36+
^
37+
tests/neg/applydynamic_sip.scala:23: error: method updateDynamic in class Bad1 does not take more parameters
38+
bad1.sel = 1 // error // error
39+
^
40+
tests/neg/applydynamic_sip.scala:32: error: method selectDynamic in class Bad2 does not take parameters
41+
bad2.sel // error
42+
^
43+
tests/neg/applydynamic_sip.scala:33: error: method applyDynamic in class Bad2 does not take parameters
44+
bad2.sel(1) // error
45+
^
46+
tests/neg/applydynamic_sip.scala:34: error: method applyDynamicNamed in class Bad2 does not take parameters
47+
bad2.sel(a = 1) // error
48+
^
49+
tests/neg/applydynamic_sip.scala:35: error: method updateDynamic in class Bad2 does not take parameters
50+
bad2.sel = 1 // error
51+
^
52+
14 errors found

tests/neg/applydynamic_sip.scala

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import scala.language.dynamics
2+
object Test extends App {
3+
val qual: Dynamic = ???
4+
val expr = "expr"
5+
val a = "a"
6+
val a2 = "a2"
7+
8+
qual.sel(a, a2: _*) // error
9+
qual.sel(arg = a, a2: _*) // error
10+
qual.sel(arg, arg2 = "a2", a2: _*) // error
11+
12+
class Bad1 extends Dynamic {
13+
def selectDynamic(n: Int) = n
14+
def applyDynamic(n: Int) = n
15+
def applyDynamicNamed(n: Int) = n
16+
def updateDynamic(n: Int) = n
17+
18+
}
19+
val bad1 = new Bad1
20+
bad1.sel // error
21+
bad1.sel(1) // error // error
22+
bad1.sel(a = 1) // error // error
23+
bad1.sel = 1 // error // error
24+
25+
class Bad2 extends Dynamic {
26+
def selectDynamic = 1
27+
def applyDynamic = 1
28+
def applyDynamicNamed = 1
29+
def updateDynamic = 1
30+
}
31+
val bad2 = new Bad2
32+
bad2.sel // error
33+
bad2.sel(1) // error
34+
bad2.sel(a = 1) // error
35+
bad2.sel = 1 // error
36+
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
t6355b.scala:14: error: value applyDynamic is not a member of A
22
error after rewriting to x.<applyDynamic: error>("bippy")
33
possible cause: maybe a wrong Dynamic method signature?
4-
println(x.bippy(42))
4+
println(x.bippy(42)) // error
55
^
66
t6355b.scala:15: error: value applyDynamic is not a member of A
77
error after rewriting to x.<applyDynamic: error>("bippy")
88
possible cause: maybe a wrong Dynamic method signature?
9-
println(x.bippy("42"))
9+
println(x.bippy("42")) // error
1010
^
1111
two errors found

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class B(method: String) {
1111
object Test {
1212
def main(args: Array[String]): Unit = {
1313
val x = new A
14-
println(x.bippy(42))
15-
println(x.bippy("42"))
14+
println(x.bippy(42)) // error
15+
println(x.bippy("42")) // error
1616
}
1717
}
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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
t6920.scala:9: error: too many arguments for method applyDynamicNamed: (values: Seq[(String, Any)])String
22
error after rewriting to CompilerError.this.test.applyDynamicNamed("crushTheCompiler")(scala.Tuple2("a", 1), scala.Tuple2("b", 2))
33
possible cause: maybe a wrong Dynamic method signature?
4-
test.crushTheCompiler(a = 1, b = 2)
4+
test.crushTheCompiler(a = 1, b = 2) // error
55
^
66
one error found

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@ class DynTest extends Dynamic {
66

77
class CompilerError {
88
val test = new DynTest
9-
test.crushTheCompiler(a = 1, b = 2)
9+
test.crushTheCompiler(a = 1, b = 2) // error
1010
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
t8006.scala:3: error: too many arguments for method applyDynamicNamed: (value: (String, Any))String
22
error after rewriting to X.this.d.applyDynamicNamed("meth")(scala.Tuple2("value1", 10), scala.Tuple2("value2", 100))
33
possible cause: maybe a wrong Dynamic method signature?
4-
d.meth(value1 = 10, value2 = 100) // two arguments here, but only one is allowed
4+
d.meth(value1 = 10, value2 = 100) // error: two arguments here, but only one is allowed
55
^
66
one error found

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
object X {
22
val d = new D
3-
d.meth(value1 = 10, value2 = 100) // two arguments here, but only one is allowed
3+
d.meth(value1 = 10, value2 = 100) // error: two arguments here, but only one is allowed
44
}
55
import language.dynamics
66
class D extends Dynamic {

tests/pending/run/applydynamic_sip.scala renamed to tests/run/applydynamic_sip.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import scala.language.dynamics
12
object Test extends dotty.runtime.LegacyApp {
23
object stubUpdate {
34
def update(as: Any*) = println(".update"+as.toList.mkString("(",", ", ")"))

0 commit comments

Comments
 (0)