aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/dotty/tools/dotc/transform/FullParameterization.scala34
-rw-r--r--src/dotty/tools/dotc/transform/TailRec.scala16
-rw-r--r--tests/pos/tailcall/i1089.scala26
3 files changed, 61 insertions, 15 deletions
diff --git a/src/dotty/tools/dotc/transform/FullParameterization.scala b/src/dotty/tools/dotc/transform/FullParameterization.scala
index e9057e885..cc8aeb9c4 100644
--- a/src/dotty/tools/dotc/transform/FullParameterization.scala
+++ b/src/dotty/tools/dotc/transform/FullParameterization.scala
@@ -12,6 +12,8 @@ import NameOps._
import ast._
import ast.Trees._
+import scala.reflect.internal.util.Collections
+
/** Provides methods to produce fully parameterized versions of instance methods,
* where the `this` of the enclosing class is abstracted out in an extra leading
* `$this` parameter and type parameters of the class become additional type
@@ -86,9 +88,12 @@ trait FullParameterization {
* }
*
* If a self type is present, $this has this self type as its type.
+ *
* @param abstractOverClass if true, include the type parameters of the class in the method's list of type parameters.
+ * @param liftThisType if true, require created $this to be $this: (Foo[A] & Foo,this).
+ * This is needed of created member stays inside scope of Foo(as in tailrec)
*/
- def fullyParameterizedType(info: Type, clazz: ClassSymbol, abstractOverClass: Boolean = true)(implicit ctx: Context): Type = {
+ def fullyParameterizedType(info: Type, clazz: ClassSymbol, abstractOverClass: Boolean = true, liftThisType: Boolean = false)(implicit ctx: Context): Type = {
val (mtparamCount, origResult) = info match {
case info @ PolyType(mtnames) => (mtnames.length, info.resultType)
case info: ExprType => (0, info.resultType)
@@ -100,7 +105,8 @@ trait FullParameterization {
/** The method result type */
def resultType(mapClassParams: Type => Type) = {
val thisParamType = mapClassParams(clazz.classInfo.selfType)
- MethodType(nme.SELF :: Nil, thisParamType :: Nil)(mt =>
+ val firstArgType = if (liftThisType) thisParamType & clazz.thisType else thisParamType
+ MethodType(nme.SELF :: Nil, firstArgType :: Nil)(mt =>
mapClassParams(origResult).substThisUnlessStatic(clazz, MethodParam(mt, 0)))
}
@@ -217,12 +223,26 @@ trait FullParameterization {
* - the `this` of the enclosing class,
* - the value parameters of the original method `originalDef`.
*/
- def forwarder(derived: TermSymbol, originalDef: DefDef, abstractOverClass: Boolean = true)(implicit ctx: Context): Tree =
+ def forwarder(derived: TermSymbol, originalDef: DefDef, abstractOverClass: Boolean = true, liftThisType: Boolean = false)(implicit ctx: Context): Tree = {
+ val fun =
ref(derived.termRef)
- .appliedToTypes(allInstanceTypeParams(originalDef, abstractOverClass).map(_.typeRef))
- .appliedTo(This(originalDef.symbol.enclosingClass.asClass))
- .appliedToArgss(originalDef.vparamss.nestedMap(vparam => ref(vparam.symbol)))
- .withPos(originalDef.rhs.pos)
+ .appliedToTypes(allInstanceTypeParams(originalDef, abstractOverClass).map(_.typeRef))
+ .appliedTo(This(originalDef.symbol.enclosingClass.asClass))
+
+ (if (!liftThisType)
+ fun.appliedToArgss(originalDef.vparamss.nestedMap(vparam => ref(vparam.symbol)))
+ else {
+ // this type could have changed on forwarding. Need to insert a cast.
+ val args = Collections.map2(originalDef.vparamss, fun.tpe.paramTypess)((vparams, paramTypes) =>
+ Collections.map2(vparams, paramTypes)((vparam, paramType) => {
+ assert(vparam.tpe <:< paramType.widen) // type should still conform to widened type
+ ref(vparam.symbol).ensureConforms(paramType)
+ })
+ )
+ fun.appliedToArgss(args)
+
+ }).withPos(originalDef.rhs.pos)
+ }
}
object FullParameterization {
diff --git a/src/dotty/tools/dotc/transform/TailRec.scala b/src/dotty/tools/dotc/transform/TailRec.scala
index 58fe7a6c9..558206960 100644
--- a/src/dotty/tools/dotc/transform/TailRec.scala
+++ b/src/dotty/tools/dotc/transform/TailRec.scala
@@ -77,7 +77,7 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete
private def mkLabel(method: Symbol, abstractOverClass: Boolean)(implicit c: Context): TermSymbol = {
val name = c.freshName(labelPrefix)
- c.newSymbol(method, name.toTermName, labelFlags, fullyParameterizedType(method.info, method.enclosingClass.asClass, abstractOverClass))
+ c.newSymbol(method, name.toTermName, labelFlags, fullyParameterizedType(method.info, method.enclosingClass.asClass, abstractOverClass, liftThisType = true))
}
override def transformDefDef(tree: tpd.DefDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
@@ -112,7 +112,7 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete
if (rewrote) {
val dummyDefDef = cpy.DefDef(tree)(rhs = rhsSemiTransformed)
val res = fullyParameterizedDef(label, dummyDefDef, abstractOverClass = defIsTopLevel)
- val call = forwarder(label, dd, abstractOverClass = defIsTopLevel)
+ val call = forwarder(label, dd, abstractOverClass = defIsTopLevel, liftThisType = true)
Block(List(res), call)
} else {
if (mandatory)
@@ -175,8 +175,8 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete
case x => (x, x, accArgs, accT, x.symbol)
}
- val (reciever, call, arguments, typeArguments, symbol) = receiverArgumentsAndSymbol(tree)
- val recv = noTailTransform(reciever)
+ val (prefix, call, arguments, typeArguments, symbol) = receiverArgumentsAndSymbol(tree)
+ val recv = noTailTransform(prefix)
val targs = typeArguments.map(noTailTransform)
val argumentss = arguments.map(noTailTransforms)
@@ -215,12 +215,12 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete
targs ::: classTypeArgs.map(x => ref(x.typeSymbol))
} else targs
- val method = Apply(if (callTargs.nonEmpty) TypeApply(Ident(label.termRef), callTargs) else Ident(label.termRef),
- List(receiver))
+ val method = if (callTargs.nonEmpty) TypeApply(Ident(label.termRef), callTargs) else Ident(label.termRef)
+ val thisPassed = method appliedTo(receiver.ensureConforms(method.tpe.widen.firstParamTypes.head))
val res =
- if (method.tpe.widen.isParameterless) method
- else argumentss.foldLeft(method) {
+ if (thisPassed.tpe.widen.isParameterless) thisPassed
+ else argumentss.foldLeft(thisPassed) {
(met, ar) => Apply(met, ar) // Dotty deviation no auto-detupling yet.
}
res
diff --git a/tests/pos/tailcall/i1089.scala b/tests/pos/tailcall/i1089.scala
new file mode 100644
index 000000000..8eb69cb9b
--- /dev/null
+++ b/tests/pos/tailcall/i1089.scala
@@ -0,0 +1,26 @@
+package hello
+
+import scala.annotation.tailrec
+
+class Enclosing {
+ class SomeData(val x: Int)
+
+ def localDef(): Unit = {
+ def foo(data: SomeData): Int = data.x
+
+ @tailrec
+ def test(i: Int, data: SomeData): Unit = {
+ if (i != 0) {
+ println(foo(data))
+ test(i - 1, data)
+ }
+ }
+
+ test(3, new SomeData(42))
+ }
+}
+
+object world extends App {
+ println("hello dotty!")
+ new Enclosing().localDef()
+}