aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/dotty/tools/dotc/transform/FullParameterization.scala69
-rw-r--r--tests/pending/pos/class-dependent-extension-method.scala3
-rw-r--r--tests/pos/enum-interop.scala13
-rw-r--r--tests/pos/tailcall/t6574.scala8
4 files changed, 75 insertions, 18 deletions
diff --git a/src/dotty/tools/dotc/transform/FullParameterization.scala b/src/dotty/tools/dotc/transform/FullParameterization.scala
index 3f02e11a7..092e16086 100644
--- a/src/dotty/tools/dotc/transform/FullParameterization.scala
+++ b/src/dotty/tools/dotc/transform/FullParameterization.scala
@@ -22,6 +22,31 @@ import ast.Trees._
* - implementations of trait methods
* - static protected accessors
* - local methods produced by tailrec transform
+ *
+ * Note that the methods lift out type parameters of the class containing
+ * the instance method, but not type parameters of enclosing classes. The
+ * fully instantiated method therefore needs to be put in a scope "close"
+ * to the original method, i.e. they need to share the same outer pointer.
+ * Examples of legal positions are: in the companion object, or as a local
+ * method inside the original method.
+ *
+ * Note: The scheme does not handle yet methods where type parameter bounds
+ * depend on value parameters of the enclosing class, as in:
+ *
+ * class C(val a: String) extends AnyVal {
+ * def foo[U <: a.type]: Unit = ...
+ * }
+ *
+ * The expansion of method `foo` would lead to
+ *
+ * def foo$extension[U <: $this.a.type]($this: C): Unit = ...
+ *
+ * which is not typable. Not clear yet what to do. Maybe allow PolyTypes
+ * to follow method parameters and translate to the following:
+ *
+ * def foo$extension($this: C)[U <: $this.a.type]: Unit = ...
+ *
+ * @see class-dependent-extension-method.scala in pending/pos.
*/
trait FullParameterization {
@@ -110,21 +135,21 @@ trait FullParameterization {
/** Given an instance method definition `originalDef`, return a
* fully parameterized method definition derived from `originalDef`, which
- * has`derived` as symbol and `fullyParameterizedType(originalDef.symbol.info)`
+ * has `derived` as symbol and `fullyParameterizedType(originalDef.symbol.info)`
* as info.
*/
- def fullyParameterizedDef(derived: TermSymbol, originalDef: DefDef)(implicit ctx: Context): Tree = {
- val origMeth = originalDef.symbol
- val origClass = origMeth.owner.asClass
- val origTParams = allInstanceTypeParams(originalDef)
- val origVParams = originalDef.vparamss.flatten map (_.symbol)
+ def fullyParameterizedDef(derived: TermSymbol, originalDef: DefDef)(implicit ctx: Context): Tree =
polyDefDef(derived, trefs => vrefss => {
+ val origMeth = originalDef.symbol
+ val origClass = origMeth.owner.asClass
+ val origTParams = allInstanceTypeParams(originalDef)
+ val origVParams = originalDef.vparamss.flatten map (_.symbol)
val thisRef :: argRefs = vrefss.flatten
/** If tree should be rewired, the rewired tree, otherwise EmptyTree.
* @param targs Any type arguments passed to the rewired tree.
*/
- def rewire(tree: Tree, targs: List[Tree])(implicit ctx: Context): Tree = {
+ def rewireTree(tree: Tree, targs: List[Tree])(implicit ctx: Context): Tree = {
def rewireCall(thisArg: Tree): Tree = {
val sym = tree.symbol
val rewired = rewiredTarget(sym, derived)
@@ -141,28 +166,40 @@ trait FullParameterization {
case Select(qual, _) => rewireCall(qual)
case tree @ TypeApply(fn, targs1) =>
assert(targs.isEmpty)
- rewire(fn, targs1)
- case Block(stats, expr) =>
- // need special casing here, because an original termRef might leak out as
- // the result of the Block if Block is copied using `cpy`.
- val expr1 = rewire(expr, targs)
- if (expr1.isEmpty) EmptyTree else Block(stats, expr1) withPos tree.pos
+ rewireTree(fn, targs1)
case _ => EmptyTree
}
}
+ /** Type rewiring is needed because a previous reference to an instance
+ * method might still persist in the types of enclosing nodes. Example:
+ *
+ * if (true) this.imeth else this.imeth
+ *
+ * is rewritten to
+ *
+ * if (true) xmeth($this) else xmeth($this)
+ *
+ * but the type `this.imeth` still persists as the result type of the `if`,
+ * because it is kept by the `cpy` operation of the tree transformer.
+ * It needs to be rewritten to the common result type of `imeth` and `xmeth`.
+ */
+ def rewireType(tpe: Type) = tpe match {
+ case tpe: TermRef if rewiredTarget(tpe.symbol, derived).exists => tpe.widen
+ case _ => tpe
+ }
+
new TreeTypeMap(
- typeMap = _
+ typeMap = rewireType(_)
.subst(origTParams, trefs)
.subst(origVParams, argRefs.map(_.tpe))
.substThis(origClass, thisRef.tpe),
ownerMap = (sym => if (sym eq origMeth) derived else sym),
treeMap = {
case tree: This if tree.symbol == origClass => thisRef
- case tree => rewire(tree, Nil) orElse tree
+ case tree => rewireTree(tree, Nil) orElse tree
}).transform(originalDef.rhs)
})
- }
/** A forwarder expression which calls `derived`, passing along
* - the type parameters and enclosing class parameters of `originalDef`,
diff --git a/tests/pending/pos/class-dependent-extension-method.scala b/tests/pending/pos/class-dependent-extension-method.scala
new file mode 100644
index 000000000..b557dfa8f
--- /dev/null
+++ b/tests/pending/pos/class-dependent-extension-method.scala
@@ -0,0 +1,3 @@
+class C(val a: String) extends AnyVal {
+ def foo[U <: a.type]: Unit = foo[U]
+}
diff --git a/tests/pos/enum-interop.scala b/tests/pos/enum-interop.scala
new file mode 100644
index 000000000..0c9aca79b
--- /dev/null
+++ b/tests/pos/enum-interop.scala
@@ -0,0 +1,13 @@
+
+
+object Test {
+
+ val cls: java.lang.Class[_] = ???
+
+ def myAsInstanceOf[T <: Class[T]](cls: java.lang.Class[_]): Class[T] = cls.asInstanceOf[Class[T]]
+ Enum.valueOf(myAsInstanceOf(cls), "")
+
+
+
+
+}
diff --git a/tests/pos/tailcall/t6574.scala b/tests/pos/tailcall/t6574.scala
index cd0fdbb8d..d8377ddbc 100644
--- a/tests/pos/tailcall/t6574.scala
+++ b/tests/pos/tailcall/t6574.scala
@@ -4,8 +4,12 @@ class Bad[X, Y](val v: Int) extends AnyVal {
this.foo[Z](a)(b)
}
- @annotation.tailrec final def differentReceiver : Unit = {
- {(); new Bad[X, Y](0)}.differentReceiver
+ @annotation.tailrec final def differentReceiver : Unit =
+ {(); new Bad[X, Y](0)}.differentReceiver
+
+ @annotation.tailrec final def differentReceiver2 : Unit = {
+ if (true) {(); new Bad[X, Y](0)}.differentReceiver2
+ else {(); new Bad[X, Y](0)}.differentReceiver2
}
@annotation.tailrec final def dependent[Z](a: Int)(b: String): b.type = {