aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2017-01-05 16:02:09 +0700
committerMartin Odersky <odersky@gmail.com>2017-01-05 18:00:06 +0700
commitaa6ebe938639f07dd6f5612e645f1449f37a86eb (patch)
tree24d257c1f12d0f5cb9b76102043046e3a904d98a /compiler/src/dotty/tools
parent42eb864dc752254fc3b8b0428570fe94aa1dafc7 (diff)
downloaddotty-aa6ebe938639f07dd6f5612e645f1449f37a86eb.tar.gz
dotty-aa6ebe938639f07dd6f5612e645f1449f37a86eb.tar.bz2
dotty-aa6ebe938639f07dd6f5612e645f1449f37a86eb.zip
Implement structural type member access
New scheme for implementing structural type member access.
Diffstat (limited to 'compiler/src/dotty/tools')
-rw-r--r--compiler/src/dotty/tools/dotc/ast/TreeInfo.scala22
-rw-r--r--compiler/src/dotty/tools/dotc/core/Definitions.scala5
-rw-r--r--compiler/src/dotty/tools/dotc/core/StdNames.scala1
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Dynamic.scala64
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Typer.scala7
5 files changed, 93 insertions, 6 deletions
diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
index da83d0644..429b7235b 100644
--- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
+++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
@@ -629,6 +629,28 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
case nil =>
Nil
}
+
+ /** Is this a selection of a member of a structural type that is not a member
+ * of an underlying class or trait?
+ */
+ def isStructuralTermSelect(tree: Tree)(implicit ctx: Context) = tree match {
+ case tree: Select =>
+ def hasRefinement(qualtpe: Type): Boolean = qualtpe.dealias match {
+ case RefinedType(parent, rname, rinfo) =>
+ rname == tree.name && tree.tpe.widen <:< rinfo || hasRefinement(parent)
+ case tp: TypeProxy =>
+ hasRefinement(tp.underlying)
+ case tp: OrType =>
+ hasRefinement(tp.tp1) || hasRefinement(tp.tp2)
+ case tp: AndType =>
+ hasRefinement(tp.tp1) && hasRefinement(tp.tp2)
+ case _ =>
+ false
+ }
+ !tree.symbol.exists && tree.isTerm && hasRefinement(tree.qualifier.tpe)
+ case _ =>
+ false
+ }
}
object TreeInfo {
diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala
index 45e37eb8b..c900f0b64 100644
--- a/compiler/src/dotty/tools/dotc/core/Definitions.scala
+++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala
@@ -26,6 +26,9 @@ object Definitions {
* else without affecting the set of programs that can be compiled.
*/
val MaxImplementedFunctionArity = 22
+
+ /** The maximal arity of a function thta can be accessed as member of a structrual type */
+ val MaxStructuralMethodArity = 7
}
/** A class defining symbols and types of standard definitions
@@ -505,6 +508,8 @@ class Definitions {
lazy val LanguageModuleRef = ctx.requiredModule("scala.language")
def LanguageModuleClass(implicit ctx: Context) = LanguageModuleRef.symbol.moduleClass.asClass
lazy val NonLocalReturnControlType: TypeRef = ctx.requiredClassRef("scala.runtime.NonLocalReturnControl")
+ lazy val ProjectorType: TypeRef = ctx.requiredClassRef("scala.Projector")
+ def ProjectorClass(implicit ctx: Context) = ProjectorType.symbol.asClass
lazy val ClassTagType = ctx.requiredClassRef("scala.reflect.ClassTag")
def ClassTagClass(implicit ctx: Context) = ClassTagType.symbol.asClass
diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala
index 716959648..f53571bdc 100644
--- a/compiler/src/dotty/tools/dotc/core/StdNames.scala
+++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala
@@ -414,6 +414,7 @@ object StdNames {
val genericArrayOps: N = "genericArrayOps"
val get: N = "get"
val getClass_ : N = "getClass"
+ val getMethod : N = "getMethod"
val getOrElse: N = "getOrElse"
val hasNext: N = "hasNext"
val hashCode_ : N = "hashCode"
diff --git a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala
index 4039c8b81..7cfd6327b 100644
--- a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala
@@ -7,10 +7,13 @@ 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.Names.{Name, TermName}
import dotty.tools.dotc.core.StdNames._
import dotty.tools.dotc.core.Types._
import dotty.tools.dotc.core.Decorators._
+import core.Symbols._
+import core.Definitions
+import Inferencing._
import ErrorReporting._
object Dynamic {
@@ -18,7 +21,10 @@ object Dynamic {
name == nme.applyDynamic || name == nme.selectDynamic || name == nme.updateDynamic || name == nme.applyDynamicNamed
}
-/** Translates selection that does not typecheck according to the scala.Dynamic rules:
+/** Handles programmable member selections of `Dynamic` instances and values
+ * with structural types. Two functionalities:
+ *
+ * 1. 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), ...)
@@ -26,6 +32,10 @@ object Dynamic {
* foo.bar ~~> foo.selectDynamic(bar)
*
* The first matching rule of is applied.
+ *
+ * 2. Translates member seclections on structural types by means of an implicit
+ * Projector instance. @See handleStructural.
+ *
*/
trait Dynamic { self: Typer with Applications =>
import Dynamic._
@@ -100,4 +110,54 @@ trait Dynamic { self: Typer with Applications =>
else untpd.TypeApply(select, targs)
untpd.Apply(selectWithTypes, Literal(Constant(name.toString)))
}
+
+ /** Handle reflection-based dispatch for members of structural types.
+ * Given `x.a`, where `x` is of (widened) type `T` and `x.a` is of type `U`:
+ *
+ * If `U` is a value type, map `x.a` to the equivalent of:
+ *
+ * implicitly[Projection[T]].get(x, "a").asInstanceOf[U]
+ *
+ * If `U` is a method type (T1,...,Tn)R, map `x.a` to the equivalent of:
+ *
+ * implicitly[Projection[T]].getMethod(x, "a")(CT1, ..., CTn).asInstanceOf[(T1,...,Tn) => R]
+ *
+ * where CT1,...,CTn are the classtags representing the erasure of T1,...,Tn.
+ *
+ * The small print: (1) T is forced to be fully defined. (2) It's an error if
+ * U is neither a value nor a method type, or a dependent method type, or of too
+ * large arity (limit is Definitions.MaxStructuralMethodArity).
+ */
+ def handleStructural(tree: Tree)(implicit ctx: Context): Tree = {
+ val Select(qual, name) = tree
+
+ def issueError(msgFn: String => String): Unit = ctx.error(msgFn("reflective call"), tree.pos)
+ def implicitArg(tpe: Type) = inferImplicitArg(tpe, issueError, tree.pos.endPos)
+ val projector = implicitArg(defn.ProjectorType.appliedTo(qual.tpe.widen))
+
+ def structuralCall(getterName: TermName, formals: List[Tree]) = {
+ val scall = untpd.Apply(
+ untpd.TypedSplice(projector.select(getterName)),
+ (qual :: Literal(Constant(name.toString)) :: formals).map(untpd.TypedSplice(_)))
+ typed(scall)
+ }
+ def fail(reason: String) =
+ errorTree(tree, em"Structural access not allowed on method $name because it $reason")
+ fullyDefinedType(tree.tpe.widen, "structural access", tree.pos) match {
+ case tpe: MethodType =>
+ if (tpe.isDependent)
+ fail(i"has a dependent method type")
+ else if (tpe.paramNames.length > Definitions.MaxStructuralMethodArity)
+ fail(i"takes too many parameters")
+ val ctags = tpe.paramTypes.map(pt =>
+ implicitArg(defn.ClassTagType.appliedTo(pt :: Nil)))
+ structuralCall(nme.getMethod, ctags).asInstance(tpe.toFunctionType())
+ case tpe: ValueType =>
+ structuralCall(nme.get, Nil).asInstance(tpe)
+ case tpe: PolyType =>
+ fail("is polymorphic")
+ case tpe =>
+ fail(i"has an unsupported type: $tpe")
+ }
+ }
}
diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala
index 6dd3f45fc..4c238518e 100644
--- a/compiler/src/dotty/tools/dotc/typer/Typer.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala
@@ -1039,9 +1039,6 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
for (refinement <- refinements1) { // TODO: get clarity whether we want to enforce these conditions
typr.println(s"adding refinement $refinement")
checkRefinementNonCyclic(refinement, refineCls, seen)
- val rsym = refinement.symbol
- if (rsym.is(Method) && rsym.allOverriddenSymbols.isEmpty)
- ctx.error(i"refinement $rsym without matching type in parent $tpt1", refinement.pos)
}
assignType(cpy.RefinedTypeTree(tree)(tpt1, refinements1), tpt1, refinements1, refineCls)
}
@@ -1808,6 +1805,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
case Apply(_, _) => " more"
case _ => ""
}
+ println(i"tree = $tree, pt = $pt")
errorTree(tree, em"$methodStr does not take$more parameters")
}
}
@@ -2045,7 +2043,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
adaptInterpolated(tree.appliedToTypeTrees(typeArgs), pt, original))
}
case wtp =>
- pt match {
+ if (isStructuralTermSelect(tree)) adapt(handleStructural(tree), pt)
+ else pt match {
case pt: FunProto =>
adaptToArgs(wtp, pt)
case pt: PolyProto =>