diff options
-rw-r--r-- | src/dotty/tools/dotc/transform/FullParameterization.scala | 34 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/TailRec.scala | 16 | ||||
-rw-r--r-- | tests/pos/tailcall/i1089.scala | 26 |
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() +} |