aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2017-01-07 14:47:18 +0700
committerMartin Odersky <odersky@gmail.com>2017-01-07 14:47:18 +0700
commit69feaa89167ebeb708535dd4abf3a79a410130f0 (patch)
treeec93cdc0b05ccb22628ca9dee454e33e4df3e8ee
parent7e3f69ace983adfbc9bbf44954f9a5845add3d43 (diff)
downloaddotty-69feaa89167ebeb708535dd4abf3a79a410130f0.tar.gz
dotty-69feaa89167ebeb708535dd4abf3a79a410130f0.tar.bz2
dotty-69feaa89167ebeb708535dd4abf3a79a410130f0.zip
Change scheme to use Selectable
Use base types instead of implicits. This is more robust in the presence of type abstraction.
-rw-r--r--compiler/src/dotty/tools/dotc/core/Definitions.scala3
-rw-r--r--compiler/src/dotty/tools/dotc/core/StdNames.scala2
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Dynamic.scala41
-rw-r--r--library/src/scala/Projector.scala10
-rw-r--r--library/src/scala/Selectable.scala8
-rw-r--r--library/src/scala/reflect/Selectable.scala (renamed from library/src/scala/reflect/Projector.scala)18
-rw-r--r--tests/pos/zoo2.scala1
-rw-r--r--tests/run/structural.scala16
-rw-r--r--tests/run/structuralNoSuchMethod.scala2
9 files changed, 47 insertions, 54 deletions
diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala
index c900f0b64..6e67087a3 100644
--- a/compiler/src/dotty/tools/dotc/core/Definitions.scala
+++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala
@@ -508,8 +508,7 @@ 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 SelectableType: TypeRef = ctx.requiredClassRef("scala.Selectable")
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 f53571bdc..4a9c50dad 100644
--- a/compiler/src/dotty/tools/dotc/core/StdNames.scala
+++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala
@@ -414,7 +414,6 @@ 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"
@@ -482,6 +481,7 @@ object StdNames {
val sameElements: N = "sameElements"
val scala_ : N = "scala"
val selectDynamic: N = "selectDynamic"
+ val selectDynamicMethod: N = "selectDynamicMethod"
val selectOverloadedMethod: N = "selectOverloadedMethod"
val selectTerm: N = "selectTerm"
val selectType: N = "selectType"
diff --git a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala
index 4b7584559..49dd98523 100644
--- a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala
@@ -33,8 +33,8 @@ object Dynamic {
*
* The first matching rule of is applied.
*
- * 2. Translates member seclections on structural types by means of an implicit
- * Projector instance. @See handleStructural.
+ * 2. Translates member selections on structural types to calls of `selectDynamic`
+ * or `selectDynamicMethod` on a `Selectable` instance. @See handleStructural.
*
*/
trait Dynamic { self: Typer with Applications =>
@@ -116,44 +116,45 @@ trait Dynamic { self: Typer with Applications =>
*
* If `U` is a value type, map `x.a` to the equivalent of:
*
- * implicitly[Projector[T]].get(x, "a").asInstanceOf[U]
+ * (x: Selectable).selectDynamic(x, "a").asInstanceOf[U]
*
* If `U` is a method type (T1,...,Tn)R, map `x.a` to the equivalent of:
*
- * implicitly[Projector[T]].getMethod(x, "a")(CT1, ..., CTn).asInstanceOf[(T1,...,Tn) => R]
+ * (x: Selectable).selectDynamicMethod(x, "a")(CT1, ..., CTn).asInstanceOf[(T1,...,Tn) => R]
*
- * where CT1,...,CTn are the classtags representing the erasure of T1,...,Tn.
+ * where CT1,...,CTn are the class tags 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).
+ * 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]) = {
+ def structuralCall(selectorName: TermName, formals: List[Tree]) = {
+ val selectable = adapt(qual, defn.SelectableType)
val scall = untpd.Apply(
- untpd.TypedSplice(projector.select(getterName)),
- (qual :: Literal(Constant(name.toString)) :: formals).map(untpd.TypedSplice(_)))
+ untpd.TypedSplice(selectable.select(selectorName)),
+ (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 {
+
+ tree.tpe.widen 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())
+ else {
+ def issueError(msgFn: String => String): Unit = ctx.error(msgFn(""), tree.pos)
+ val ctags = tpe.paramTypes.map(pt =>
+ inferImplicitArg(defn.ClassTagType.appliedTo(pt :: Nil), issueError, tree.pos.endPos))
+ structuralCall(nme.selectDynamicMethod, ctags).asInstance(tpe.toFunctionType())
+ }
case tpe: ValueType =>
- structuralCall(nme.get, Nil).asInstance(tpe)
+ structuralCall(nme.selectDynamic, Nil).asInstance(tpe)
case tpe: PolyType =>
fail("is polymorphic")
case tpe =>
diff --git a/library/src/scala/Projector.scala b/library/src/scala/Projector.scala
deleted file mode 100644
index cff73f84d..000000000
--- a/library/src/scala/Projector.scala
+++ /dev/null
@@ -1,10 +0,0 @@
-package scala
-import scala.reflect.ClassTag
-import scala.annotation.implicitNotFound
-
-@implicitNotFound("no projector instance found to implement reflective access to structural type ${T}")
-trait Projector[-T] extends Any {
- def get(receiver: T, name: String): Any
- def getMethod(receiver: T, name: String, paramClasses: ClassTag[_]*): Any =
- new UnsupportedOperationException("getMethod")
-}
diff --git a/library/src/scala/Selectable.scala b/library/src/scala/Selectable.scala
new file mode 100644
index 000000000..c5c714ca9
--- /dev/null
+++ b/library/src/scala/Selectable.scala
@@ -0,0 +1,8 @@
+package scala
+import scala.reflect.ClassTag
+
+trait Selectable extends Any {
+ def selectDynamic(name: String): Any
+ def selectDynamicMethod(name: String, paramClasses: ClassTag[_]*): Any =
+ new UnsupportedOperationException("selectDynamicMethod")
+}
diff --git a/library/src/scala/reflect/Projector.scala b/library/src/scala/reflect/Selectable.scala
index b5732ee17..0dbdbc293 100644
--- a/library/src/scala/reflect/Projector.scala
+++ b/library/src/scala/reflect/Selectable.scala
@@ -1,8 +1,7 @@
package scala.reflect
-class Projector extends scala.Projector[Any] {
- import Projector._
- def get(receiver: Any, name: String): Any = {
+class Selectable(val receiver: Any) extends AnyVal with scala.Selectable {
+ def selectDynamic(name: String): Any = {
val rcls = receiver.getClass
try {
val fld = rcls.getField(name)
@@ -10,11 +9,11 @@ class Projector extends scala.Projector[Any] {
}
catch {
case ex: NoSuchFieldError =>
- getMethod(receiver, name).asInstanceOf[() => Any]()
+ selectDynamicMethod(name).asInstanceOf[() => Any]()
}
}
- override def getMethod(receiver: Any, name: String, paramTypes: ClassTag[_]*): Any = {
+ override def selectDynamicMethod(name: String, paramTypes: ClassTag[_]*): Any = {
val rcls = receiver.getClass
val paramClasses = paramTypes.map(_.runtimeClass)
val mth = rcls.getMethod(name, paramClasses: _*)
@@ -66,6 +65,9 @@ class Projector extends scala.Projector[Any] {
}
}
-object Projector {
- implicit val reflectiveProjector: scala.Projector[Any] = new Projector
-} \ No newline at end of file
+object Selectable {
+ implicit def reflectiveSelectable(receiver: Any): scala.Selectable = receiver match {
+ case receiver: scala.Selectable => receiver
+ case _ => new Selectable(receiver)
+ }
+}
diff --git a/tests/pos/zoo2.scala b/tests/pos/zoo2.scala
index 9911416d3..06210fe67 100644
--- a/tests/pos/zoo2.scala
+++ b/tests/pos/zoo2.scala
@@ -1,3 +1,4 @@
+import scala.reflect.Selectable.reflectiveSelectable
object Test {
type Meat = {
type IsMeat = Any
diff --git a/tests/run/structural.scala b/tests/run/structural.scala
index 43f008b5f..0f18f4579 100644
--- a/tests/run/structural.scala
+++ b/tests/run/structural.scala
@@ -1,22 +1,14 @@
-case class Record(elems: (String, Any)*)
-
-object Record {
-
- implicit def projector: Projector[Record] = new Projector[Record] {
- def get(receiver: Record, name: String): Any =
- receiver.elems.find(_._1 == name).get._2
- }
-
+case class Record(elems: (String, Any)*) extends Selectable {
+ def selectDynamic(name: String): Any = elems.find(_._1 == name).get._2
}
object Test {
- import scala.reflect.Projector.reflectiveProjector
- import Record.projector
+ import scala.reflect.Selectable.reflectiveSelectable
def f(closeable: { def close(): Unit }) =
closeable.close()
- type RN = Record { val name: String }
+ type RN = Record { val name: String; val age: Int }
def g(r: RN) = r.name
diff --git a/tests/run/structuralNoSuchMethod.scala b/tests/run/structuralNoSuchMethod.scala
index 3d33c9d8b..476d7ed82 100644
--- a/tests/run/structuralNoSuchMethod.scala
+++ b/tests/run/structuralNoSuchMethod.scala
@@ -1,4 +1,4 @@
-import scala.reflect.Projector.reflectiveProjector
+import scala.reflect.Selectable.reflectiveSelectable
/** Demonstrates limitation of structural method dispatch (in Scala 2.x and dotty).
* The method must be defined at exactly the argument types given in the structural type;