diff options
-rw-r--r-- | src/dotty/tools/dotc/ast/TreeInfo.scala | 14 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Applications.scala | 19 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Dynamic.scala | 32 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Typer.scala | 12 | ||||
-rw-r--r-- | tests/neg/t6663.check (renamed from tests/untried/neg/t6663.check) | 0 | ||||
-rw-r--r-- | tests/neg/t6663.scala (renamed from tests/untried/neg/t6663.scala) | 2 | ||||
-rw-r--r-- | tests/run/dynamicDynamicTests.scala | 41 |
7 files changed, 85 insertions, 35 deletions
diff --git a/src/dotty/tools/dotc/ast/TreeInfo.scala b/src/dotty/tools/dotc/ast/TreeInfo.scala index a48651ebf..7c3f7f385 100644 --- a/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -630,20 +630,6 @@ object TreeInfo { } } - def isApplyDynamicName(name: Name) = (name == nme.updateDynamic) || (name == nme.selectDynamic) || (name == nme.applyDynamic) || (name == nme.applyDynamicNamed) - - class DynamicApplicationExtractor(nameTest: Name => Boolean) { - def unapply(tree: Tree) = tree match { - case Apply(TypeApply(Select(qual, oper), _), List(Literal(Constant(name)))) if nameTest(oper) => Some((qual, name)) - case Apply(Select(qual, oper), List(Literal(Constant(name)))) if nameTest(oper) => Some((qual, name)) - case Apply(Ident(oper), List(Literal(Constant(name)))) if nameTest(oper) => Some((EmptyTree(), name)) - case _ => None - } - } - object DynamicUpdate extends DynamicApplicationExtractor(_ == nme.updateDynamic) - object DynamicApplication extends DynamicApplicationExtractor(isApplyDynamicName) - object DynamicApplicationNamed extends DynamicApplicationExtractor(_ == nme.applyDynamicNamed) - object MacroImplReference { private def refPart(tree: Tree): Tree = tree match { case TypeApply(fun, _) => refPart(fun) diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index 099105de3..318f2f8ff 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -593,8 +593,10 @@ trait Applications extends Compatibility { self: Typer with Dynamic => case ErrorType => tree.withType(ErrorType) case TryDynamicCallType => tree match { - case tree @ Apply(Select(qual, name), args) if !isDynamicMethod(name) => - typedDynamicApply(qual, name, args, pt)(tree) + case Apply(Select(qual, name), args) if !isDynamicMethod(name) => + typedDynamicApply(qual, name, None, args, pt)(tree) + case Apply(TypeApply(Select(qual, name), targs), args) if !isDynamicMethod(name) => + typedDynamicApply(qual, name, Some(targs), args, pt)(tree) case _ => handleUnexpectedFunType(tree, fun1) } @@ -679,7 +681,18 @@ trait Applications extends Compatibility { self: Typer with Dynamic => } case _ => } - assignType(cpy.TypeApply(tree)(typedFn, typedArgs), typedFn, typedArgs) + if (typedFn.tpe eq TryDynamicCallType) { + (pt, typedFn) match { + case (_: FunProto, _)=> + tree.withType(TryDynamicCallType) + case (_, Select(qual, name)) => + typedDynamicSelect(qual, name, Some(typedArgs), pt) + case _ => + tree.withType(TryDynamicCallType) + } + } else { + assignType(cpy.TypeApply(tree)(typedFn, typedArgs), typedFn, typedArgs) + } } /** Rewrite `new Array[T](....)` if T is an unbounded generic to calls to newGenericArray. diff --git a/src/dotty/tools/dotc/typer/Dynamic.scala b/src/dotty/tools/dotc/typer/Dynamic.scala index aeb3cca8c..f5303b833 100644 --- a/src/dotty/tools/dotc/typer/Dynamic.scala +++ b/src/dotty/tools/dotc/typer/Dynamic.scala @@ -10,7 +10,6 @@ import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.Names.Name import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.core.Types._ -import dotty.tools.dotc.core.Mode import dotty.tools.dotc.core.Decorators._ object Dynamic { @@ -30,10 +29,12 @@ object Dynamic { trait Dynamic { self: Typer with Applications => /** Translate selection that does not typecheck according to the normal rules into a applyDynamic/applyDynamicNamed. - * foo.bar(baz0, baz1, ...) ~~> foo.applyDynamic(bar)(baz0, baz1, ...) - * foo.bar(x = bazX, y = bazY, baz, ...) ~~> foo.applyDynamicNamed("bar")(("x", bazX), ("y", bazY), ("", baz), ...) + * foo.bar(baz0, baz1, ...) ~~> foo.applyDynamic(bar)(baz0, baz1, ...) + * foo.bar[T0, ...](baz0, baz1, ...) ~~> foo.applyDynamic[T0, ...](bar)(baz0, baz1, ...) + * foo.bar(x = bazX, y = bazY, baz, ...) ~~> foo.applyDynamicNamed("bar")(("x", bazX), ("y", bazY), ("", baz), ...) + * foo.bar[T0, ...](x = bazX, y = bazY, baz, ...) ~~> foo.applyDynamicNamed[T0, ...]("bar")(("x", bazX), ("y", bazY), ("", baz), ...) */ - def typedDynamicApply(qual: untpd.Tree, name: Name, args: List[untpd.Tree], pt: Type)(original: untpd.Apply)( + def typedDynamicApply(qual: untpd.Tree, name: Name, targsOpt: Option[List[untpd.Tree]], args: List[untpd.Tree], pt: Type)(original: untpd.Apply)( implicit ctx: Context): Tree = { def isNamedArg(arg: untpd.Tree): Boolean = arg match { case NamedArg(_, _) => true; case _ => false } val dynName = if (args.exists(isNamedArg)) nme.applyDynamicNamed else nme.applyDynamic @@ -47,25 +48,32 @@ trait Dynamic { self: Typer with Applications => case arg => namedArgTuple("", arg) } val args1 = if (dynName == nme.applyDynamic) args else namedArgs - typedApply(untpd.Apply(coreDynamic(qual, dynName, name), args1), pt) + typedApply(untpd.Apply(coreDynamic(qual, dynName, name, targsOpt), args1), pt) } } /** Translate selection that does not typecheck according to the normal rules into a selectDynamic. - * foo.bar ~~> foo.selectDynamic(bar) + * foo.bar ~~> foo.selectDynamic(bar) + * foo.bar[T0, ...] ~~> foo.selectDynamic[T0, ...](bar) * * Note: inner part of translation foo.bar(baz) = quux ~~> foo.selectDynamic(bar).update(baz, quux) is achieved * through an existing transformation of in typedAssign [foo.bar(baz) = quux ~~> foo.bar.update(baz, quux)]. */ - def typedDynamicSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = - typedApply(coreDynamic(tree.qualifier, nme.selectDynamic, tree.name), pt) + def typedDynamicSelect(qualifier: untpd.Tree, name: Name, targsOpt: Option[List[Tree]], pt: Type)(implicit ctx: Context): Tree = + typedApply(coreDynamic(qualifier, nme.selectDynamic, name, targsOpt), pt) /** Translate selection that does not typecheck according to the normal rules into a updateDynamic. * foo.bar = baz ~~> foo.updateDynamic(bar)(baz) */ - def typedDynamicAssign(qual: untpd.Tree, name: Name, rhs: untpd.Tree, pt: Type)(implicit ctx: Context): Tree = - typedApply(untpd.Apply(coreDynamic(qual, nme.updateDynamic, name), rhs), pt) + def typedDynamicAssign(qual: untpd.Tree, name: Name, targsOpt: Option[List[untpd.Tree]], rhs: untpd.Tree, pt: Type)(implicit ctx: Context): Tree = + typedApply(untpd.Apply(coreDynamic(qual, nme.updateDynamic, name, targsOpt), rhs), pt) - private def coreDynamic(qual: untpd.Tree, dynName: Name, name: Name)(implicit ctx: Context): untpd.Apply = - untpd.Apply(untpd.Select(qual, dynName), Literal(Constant(name.toString))) + private def coreDynamic(qual: untpd.Tree, dynName: Name, name: Name, targsOpt: Option[List[untpd.Tree]])(implicit ctx: Context): untpd.Apply = { + val select = untpd.Select(qual, dynName) + val selectWithTypes = targsOpt match { + case Some(targs) => untpd.TypeApply(select, targs) + case None => select + } + untpd.Apply(selectWithTypes, Literal(Constant(name.toString))) + } } diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index fdcfe347b..cce692b42 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -318,10 +318,10 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit if (tree.name.isTypeName) checkStable(qual1.tpe, qual1.pos) val select = typedSelect(tree, pt, qual1) pt match { - case _: FunProto | AssignProto => select - case _ => - if (select.tpe eq TryDynamicCallType) typedDynamicSelect(tree, pt) - else select + case _ if select.tpe ne TryDynamicCallType => select + case _: FunProto | AssignProto => select + case PolyProto(_,_) => select + case _ => typedDynamicSelect(tree.qualifier, tree.name, None, pt) } } @@ -520,7 +520,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case TryDynamicCallType => tree match { case Assign(Select(qual, name), rhs) if !isDynamicMethod(name) => - typedDynamicAssign(qual, name, rhs, pt) + typedDynamicAssign(qual, name, None, rhs, pt) + case Assign(TypeApply(Select(qual, name), targs), rhs) if !isDynamicMethod(name) => + typedDynamicAssign(qual, name, Some(targs), rhs, pt) case _ => reassignmentToVal } case tpe => diff --git a/tests/untried/neg/t6663.check b/tests/neg/t6663.check index aa4faa4a4..aa4faa4a4 100644 --- a/tests/untried/neg/t6663.check +++ b/tests/neg/t6663.check diff --git a/tests/untried/neg/t6663.scala b/tests/neg/t6663.scala index 4a358dfbc..aa4ab08ed 100644 --- a/tests/untried/neg/t6663.scala +++ b/tests/neg/t6663.scala @@ -13,7 +13,7 @@ object Test extends App { // but, before fixing SI-6663, became // C(42).selectDynamic("foo").get, ignoring // the [String] type parameter - var v = new C(42).foo[String].get :Int + var v = new C(42).foo[String].get :Int // error println(v) } diff --git a/tests/run/dynamicDynamicTests.scala b/tests/run/dynamicDynamicTests.scala index 3f8da8298..05b878f1c 100644 --- a/tests/run/dynamicDynamicTests.scala +++ b/tests/run/dynamicDynamicTests.scala @@ -23,7 +23,16 @@ class Baz extends scala.Dynamic { def updateDynamic(name: String)(value: String): String = "updateDynamic(" + name + ")(" + value + ")" } +class Qux extends scala.Dynamic { + def selectDynamic[T](name: String): String = "selectDynamic(" + name + ")" + def applyDynamic[T](name: String)(args: String*): String = "applyDynamic(" + name + ")" + args.mkString("(", ", ", ")") + def applyDynamicNamed[T](name: String)(args: (String, Any)*): String = "applyDynamicNamed(" + name + ")" + args.mkString("(", ", ", ")") + def updateDynamic[T](name: String)(value: T): String = "updateDynamic(" + name + ")(" + value + ")" +} + object Test { + val qux = new Qux + implicit class StringUpdater(str: String) { def update(name: String, v: String) = s"$str.update(" + name + ", " + v + ")" } @@ -42,6 +51,7 @@ object Test { runFooTests2() runBarTests() runBazTests() + runQuxTests() assert(!failed) } @@ -161,4 +171,35 @@ object Test { assertEquals("selectDynamic(bazSelectUpdate).update(7, value)", baz.bazSelectUpdate(7) = "value") assertEquals("selectDynamic(bazSelectUpdate).update(7, 10)", baz.bazSelectUpdate(7) = 10) } + + /** Test correct lifting of type parameters */ + def runQuxTests() = { + implicit def intToString(n: Int): String = n.toString + + val qux = new Qux + + assertEquals("selectDynamic(quxSelect)", qux.quxSelect) + assertEquals("selectDynamic(quxSelect)", qux.quxSelect[Int]) + + assertEquals("applyDynamic(quxApply)()", qux.quxApply()) + assertEquals("applyDynamic(quxApply)()", qux.quxApply[Int]()) + assertEquals("applyDynamic(quxApply)(1)", qux.quxApply(1)) + assertEquals("applyDynamic(quxApply)(1)", qux.quxApply[Int](1)) + assertEquals("applyDynamic(quxApply)(1, 2, 3)", qux.quxApply(1, 2, 3)) + assertEquals("applyDynamic(quxApply)(1, 2, 3)", qux.quxApply[Int](1, 2, 3)) + assertEquals("applyDynamic(quxApply)(1, 2, a)", qux.quxApply(1, 2, "a")) + assertEquals("applyDynamic(quxApply)(1, 2, a)", qux.quxApply[Int](1, 2, "a")) + + assertEquals("applyDynamicNamed(quxApplyNamed)((a,1))", qux.quxApplyNamed(a = 1)) + assertEquals("applyDynamicNamed(quxApplyNamed)((a,1))", qux.quxApplyNamed[Int](a = 1)) + assertEquals("applyDynamicNamed(quxApplyNamed)((a,1), (b,2))", qux.quxApplyNamed(a = 1, b = "2")) + assertEquals("applyDynamicNamed(quxApplyNamed)((a,1), (b,2))", qux.quxApplyNamed[Int](a = 1, b = "2")) + assertEquals("applyDynamicNamed(quxApplyNamed)((a,1), (,abc))", qux.quxApplyNamed(a = 1, "abc")) + assertEquals("applyDynamicNamed(quxApplyNamed)((a,1), (,abc))", qux.quxApplyNamed[Int](a = 1, "abc")) + + assertEquals("updateDynamic(quxUpdate)(abc)", qux.quxUpdate = "abc") + + assertEquals("selectDynamic(quxSelectUpdate).update(key, value)", qux.quxSelectUpdate("key") = "value") + assertEquals("selectDynamic(quxSelectUpdate).update(key, value)", qux.quxSelectUpdate[Int]("key") = "value") + } } |