aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools
diff options
context:
space:
mode:
authorNicolas Stucki <nicolas.stucki@gmail.com>2016-05-30 09:20:21 +0200
committerNicolas Stucki <nicolas.stucki@gmail.com>2016-07-07 11:10:44 +0200
commit75da0358fd7866f3dccdfcf4fbeae9af8ccc69f3 (patch)
tree142087645d00858f20eaead2f805afb333d2bddc /src/dotty/tools
parent07fd8a357ed660ef15163efb2788928fec290fdd (diff)
downloaddotty-75da0358fd7866f3dccdfcf4fbeae9af8ccc69f3.tar.gz
dotty-75da0358fd7866f3dccdfcf4fbeae9af8ccc69f3.tar.bz2
dotty-75da0358fd7866f3dccdfcf4fbeae9af8ccc69f3.zip
Fix #657: Add scala.Dynamic support.
Diffstat (limited to 'src/dotty/tools')
-rw-r--r--src/dotty/tools/dotc/core/Types.scala3
-rw-r--r--src/dotty/tools/dotc/typer/Applications.scala10
-rw-r--r--src/dotty/tools/dotc/typer/Dynamic.scala71
-rw-r--r--src/dotty/tools/dotc/typer/ProtoTypes.scala2
-rw-r--r--src/dotty/tools/dotc/typer/TypeAssigner.scala9
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala21
6 files changed, 109 insertions, 7 deletions
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala
index f514a329e..8a4bf727b 100644
--- a/src/dotty/tools/dotc/core/Types.scala
+++ b/src/dotty/tools/dotc/core/Types.scala
@@ -3021,6 +3021,9 @@ object Types {
object ErrorType extends ErrorType
+ /* Type used to track Select nodes that could not resolve a member and their qualifier is a scala.Dynamic. */
+ object TryDynamicCallType extends ErrorType
+
/** Wildcard type, possibly with bounds */
abstract case class WildcardType(optBounds: Type) extends CachedGroundType with TermType {
def derivedWildcardType(optBounds: Type)(implicit ctx: Context) =
diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala
index a9184c7e5..bab85f2a4 100644
--- a/src/dotty/tools/dotc/typer/Applications.scala
+++ b/src/dotty/tools/dotc/typer/Applications.scala
@@ -86,11 +86,12 @@ object Applications {
import Applications._
-trait Applications extends Compatibility { self: Typer =>
+trait Applications extends Compatibility { self: Typer with Dynamic =>
import Applications._
import tpd.{ cpy => _, _ }
import untpd.cpy
+ import Dynamic.isDynamicMethod
/** @tparam Arg the type of arguments, could be tpd.Tree, untpd.Tree, or Type
* @param methRef the reference to the method of the application
@@ -553,6 +554,13 @@ trait Applications extends Compatibility { self: Typer =>
fun1.tpe match {
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 _ =>
+ handleUnexpectedFunType(tree, fun1)
+ }
case _ => methPart(fun1).tpe match {
case funRef: TermRef =>
tryEither { implicit ctx =>
diff --git a/src/dotty/tools/dotc/typer/Dynamic.scala b/src/dotty/tools/dotc/typer/Dynamic.scala
new file mode 100644
index 000000000..aeb3cca8c
--- /dev/null
+++ b/src/dotty/tools/dotc/typer/Dynamic.scala
@@ -0,0 +1,71 @@
+package dotty.tools
+package dotc
+package typer
+
+import dotty.tools.dotc.ast.Trees.NamedArg
+import dotty.tools.dotc.ast.tpd._
+import dotty.tools.dotc.ast.untpd
+import dotty.tools.dotc.core.Constants.Constant
+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 {
+ def isDynamicMethod(name: Name): Boolean =
+ name == nme.applyDynamic || name == nme.selectDynamic || name == nme.updateDynamic || name == nme.applyDynamicNamed
+}
+
+/** Translates selection that does not typecheck according to the scala.Dynamic rules:
+ * foo.bar(baz) = quux ~~> foo.selectDynamic(bar).update(baz, quux)
+ * foo.bar = baz ~~> foo.updateDynamic("bar")(baz)
+ * 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 ~~> foo.selectDynamic(bar)
+ *
+ * The first matching rule of is applied.
+ */
+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), ...)
+ */
+ def typedDynamicApply(qual: untpd.Tree, name: Name, 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
+ if (dynName == nme.applyDynamicNamed && untpd.isWildcardStarArgList(args)) {
+ ctx.error("applyDynamicNamed does not support passing a vararg parameter", original.pos)
+ original.withType(ErrorType)
+ } else {
+ def namedArgTuple(name: String, arg: untpd.Tree) = untpd.Tuple(List(Literal(Constant(name)), arg))
+ def namedArgs = args.map {
+ case NamedArg(argName, arg) => namedArgTuple(argName.toString, arg)
+ case arg => namedArgTuple("", arg)
+ }
+ val args1 = if (dynName == nme.applyDynamic) args else namedArgs
+ typedApply(untpd.Apply(coreDynamic(qual, dynName, name), args1), pt)
+ }
+ }
+
+ /** Translate selection that does not typecheck according to the normal rules into a selectDynamic.
+ * foo.bar ~~> foo.selectDynamic(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)
+
+ /** 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)
+
+ 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)))
+}
diff --git a/src/dotty/tools/dotc/typer/ProtoTypes.scala b/src/dotty/tools/dotc/typer/ProtoTypes.scala
index 740258821..622e3b9d8 100644
--- a/src/dotty/tools/dotc/typer/ProtoTypes.scala
+++ b/src/dotty/tools/dotc/typer/ProtoTypes.scala
@@ -430,6 +430,8 @@ object ProtoTypes {
(if (theMap != null) theMap else new WildApproxMap).mapOver(tp)
}
+ @sharable object AssignProto extends UncachedGroundType with MatchAlways
+
private[ProtoTypes] class WildApproxMap(implicit ctx: Context) extends TypeMap {
def apply(tp: Type) = wildApprox(tp, this)
}
diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala
index 995fa43ca..5e65df75e 100644
--- a/src/dotty/tools/dotc/typer/TypeAssigner.scala
+++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala
@@ -196,11 +196,16 @@ trait TypeAssigner {
def selectionType(site: Type, name: Name, pos: Position)(implicit ctx: Context): Type = {
val mbr = site.member(name)
if (reallyExists(mbr)) site.select(name, mbr)
- else {
+ else if (site.derivesFrom(defn.DynamicClass) && !Dynamic.isDynamicMethod(name)) {
+ TryDynamicCallType
+ } else {
if (!site.isErroneous) {
ctx.error(
if (name == nme.CONSTRUCTOR) d"$site does not have a constructor"
- else d"$name is not a member of $site", pos)
+ else if (site.derivesFrom(defn.DynamicClass)) {
+ d"$name is not a member of $site\n" +
+ "possible cause: maybe a wrong Dynamic method signature?"
+ } else d"$name is not a member of $site", pos)
}
ErrorType
}
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index 268020ec5..019c460e8 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -58,11 +58,12 @@ object Typer {
assert(tree.pos.exists, s"position not set for $tree # ${tree.uniqueId}")
}
-class Typer extends Namer with TypeAssigner with Applications with Implicits with Checking {
+class Typer extends Namer with TypeAssigner with Applications with Implicits with Dynamic with Checking {
import Typer._
import tpd.{cpy => _, _}
import untpd.cpy
+ import Dynamic.isDynamicMethod
/** A temporary data item valid for a single typed ident:
* The set of all root import symbols that have been
@@ -316,7 +317,13 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
def asSelect(implicit ctx: Context): Tree = {
val qual1 = typedExpr(tree.qualifier, selectionProto(tree.name, pt, this))
if (tree.name.isTypeName) checkStable(qual1.tpe, qual1.pos)
- typedSelect(tree, pt, qual1)
+ val select = typedSelect(tree, pt, qual1)
+ pt match {
+ case _: FunProto | AssignProto => select
+ case _ =>
+ if (select.tpe eq TryDynamicCallType) typedDynamicSelect(tree, pt)
+ else select
+ }
}
def asJavaSelectFromTypeTree(implicit ctx: Context): Tree = {
@@ -480,7 +487,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
val appliedUpdate = cpy.Apply(fn)(wrappedUpdate, (args map untpd.TypedSplice) :+ tree.rhs)
typed(appliedUpdate, pt)
case lhs =>
- val lhsCore = typedUnadapted(lhs)
+ val lhsCore = typedUnadapted(lhs, AssignProto)
def lhs1 = typed(untpd.TypedSplice(lhsCore))
def canAssign(sym: Symbol) = // allow assignments from the primary constructor to class fields
sym.is(Mutable, butNot = Accessor) ||
@@ -508,6 +515,12 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
case _ =>
reassignmentToVal
}
+ case TryDynamicCallType =>
+ tree match {
+ case Assign(Select(qual, name), rhs) if !isDynamicMethod(name) =>
+ typedDynamicAssign(qual, name, rhs, pt)
+ case _ => reassignmentToVal
+ }
case tpe =>
reassignmentToVal
}
@@ -1665,7 +1678,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
tree match {
case _: MemberDef | _: PackageDef | _: Import | _: WithoutTypeOrPos[_] => tree
case _ => tree.tpe.widen match {
- case ErrorType =>
+ case _: ErrorType =>
tree
case ref: TermRef =>
pt match {