From c59183fd80f077585b0c93034ac7bc34a061b7fa Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Thu, 19 Apr 2018 19:03:08 +0200 Subject: [PATCH 1/4] Add LambdaType#instantiateParamInfos This generalizes TypeLambda#instantiateBounds to all LambdaTypes. --- compiler/src/dotty/tools/dotc/ast/tpd.scala | 2 +- compiler/src/dotty/tools/dotc/core/Types.scala | 10 ++++++---- compiler/src/dotty/tools/dotc/typer/Applications.scala | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index a3be000dbbc2..572bd6fa91fd 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -193,7 +193,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def polyDefDef(sym: TermSymbol, rhsFn: List[Type] => List[List[Tree]] => Tree)(implicit ctx: Context): DefDef = { val (tparams, mtp) = sym.info match { case tp: PolyType => - val tparams = ctx.newTypeParams(sym, tp.paramNames, EmptyFlags, tp.instantiateBounds) + val tparams = ctx.newTypeParams(sym, tp.paramNames, EmptyFlags, tp.instantiateParamInfos(_)) (tparams, tp.instantiate(tparams map (_.typeRef))) case tp => (Nil, tp) } diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 01144c3ddbfd..b8e07b9fb058 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -2664,6 +2664,12 @@ object Types { myParamRefs } + /** Like `paramInfos` but substitute parameter references with the given arguments */ + final def instantiateParamInfos(argTypes: => List[Type])(implicit ctx: Context): List[Type] = + if (isParamDependent) paramInfos.mapConserve(_.substParams(this, argTypes)) + else paramInfos + + /** Like `resultType` but substitute parameter references with the given arguments */ final def instantiate(argTypes: => List[Type])(implicit ctx: Context): Type = if (isResultDependent) resultType.substParams(this, argTypes) else resultType @@ -2998,10 +3004,6 @@ object Types { lazy val typeParams: List[LambdaParam] = paramNames.indices.toList.map(new LambdaParam(this, _)) - /** Instantiate parameter bounds by substituting parameters with given arguments */ - final def instantiateBounds(argTypes: List[Type])(implicit ctx: Context): List[Type] = - paramInfos.mapConserve(_.substParams(this, argTypes)) - def derivedLambdaAbstraction(paramNames: List[TypeName], paramInfos: List[TypeBounds], resType: Type)(implicit ctx: Context): Type = resType match { case resType @ TypeAlias(alias) => diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 7d77920e8741..2ecdc8372e1e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1120,7 +1120,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => val tp1Params = tp1.newLikeThis(tp1.paramNames, tp1.paramInfos, defn.AnyType) fullyDefinedType(tp1Params, "type parameters of alternative", alt1.symbol.pos) - val tparams = ctx.newTypeParams(alt1.symbol, tp1.paramNames, EmptyFlags, tp1.instantiateBounds) + val tparams = ctx.newTypeParams(alt1.symbol, tp1.paramNames, EmptyFlags, tp1.instantiateParamInfos(_)) isAsSpecific(alt1, tp1.instantiate(tparams.map(_.typeRef)), alt2, tp2) } case _ => // (3) From 5e0a6de22b0e4a97a7aa37ba130d9eeb8df48fff Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Thu, 19 Apr 2018 19:15:45 +0200 Subject: [PATCH 2/4] Fix #4345: properly instantiate dependent methods The raw paramInfoss of a method type contain parameter references that should be instantiated before using the type anywhere. --- .../dotc/transform/FullParameterization.scala | 17 +++++++++-------- tests/pos/i4345.scala | 10 ++++++++++ 2 files changed, 19 insertions(+), 8 deletions(-) create mode 100644 tests/pos/i4345.scala diff --git a/compiler/src/dotty/tools/dotc/transform/FullParameterization.scala b/compiler/src/dotty/tools/dotc/transform/FullParameterization.scala index a950bf7c4f49..256a3591c1e3 100644 --- a/compiler/src/dotty/tools/dotc/transform/FullParameterization.scala +++ b/compiler/src/dotty/tools/dotc/transform/FullParameterization.scala @@ -233,14 +233,15 @@ trait FullParameterization { fun.appliedToArgss(originalDef.vparamss.nestedMap(vparam => ref(vparam.symbol))) else { // this type could have changed on forwarding. Need to insert a cast. - val args = (originalDef.vparamss, fun.tpe.paramInfoss).zipped.map((vparams, paramTypes) => - (vparams, paramTypes).zipped.map((vparam, paramType) => { - assert(vparam.tpe <:< paramType.widen) // type should still conform to widened type - ref(vparam.symbol).ensureConforms(paramType) - }) - ) - fun.appliedToArgss(args) - + originalDef.vparamss.foldLeft(fun)((acc, vparams) => { + val meth = acc.tpe.asInstanceOf[MethodType] + val paramTypes = meth.instantiateParamInfos(vparams.map(_.tpe)) + acc.appliedToArgs( + (vparams, paramTypes).zipped.map((vparam, paramType) => { + assert(vparam.tpe <:< paramType.widen) // type should still conform to widened type + ref(vparam.symbol).ensureConforms(paramType) + })) + }) }).withPos(originalDef.rhs.pos) } } diff --git a/tests/pos/i4345.scala b/tests/pos/i4345.scala new file mode 100644 index 000000000000..cec9617ad75a --- /dev/null +++ b/tests/pos/i4345.scala @@ -0,0 +1,10 @@ +import scala.annotation.tailrec +class Context { + type Tree +} + +class Test { + @tailrec + final def loop(c: Context)(trees: List[c.Tree]): Boolean = + loop(c)(trees) +} From c84a0840e7146360d839f608bcbc47f210b39699 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Thu, 19 Apr 2018 20:46:05 +0200 Subject: [PATCH 3/4] tpd#polyDefDef: Handle intra-parameter list dependencies The paramInfos of a MethodType may contain TermParamRefs, when creating symbols for these parameters, the TermParamRefs need to be replaced by TermRef to previously created parameter symbols. --- compiler/src/dotty/tools/dotc/ast/tpd.scala | 17 +++++++++++++++-- tests/pos/i4345.scala | 4 ++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 572bd6fa91fd..b8943dcfeae6 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -200,11 +200,24 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def valueParamss(tp: Type): (List[List[TermSymbol]], Type) = tp match { case tp: MethodType => - def valueParam(name: TermName, info: Type): TermSymbol = { + val isParamDependent = tp.isParamDependent + val previousParamRefs = if (isParamDependent) new mutable.ListBuffer[TermRef]() else null + + def valueParam(name: TermName, origInfo: Type): TermSymbol = { val maybeImplicit = if (tp.isImplicitMethod) Implicit else EmptyFlags val maybeErased = if (tp.isErasedMethod) Erased else EmptyFlags - ctx.newSymbol(sym, name, TermParam | maybeImplicit | maybeErased, info, coord = sym.coord) + + def makeSym(info: Type) = ctx.newSymbol(sym, name, TermParam | maybeImplicit | maybeErased, info, coord = sym.coord) + + if (isParamDependent) { + val sym = makeSym(origInfo.substParams(tp, previousParamRefs.toList)) + previousParamRefs += sym.termRef + sym + } + else + makeSym(origInfo) } + val params = (tp.paramNames, tp.paramInfos).zipped.map(valueParam) val (paramss, rtp) = valueParamss(tp.instantiate(params map (_.termRef))) (params :: paramss, rtp) diff --git a/tests/pos/i4345.scala b/tests/pos/i4345.scala index cec9617ad75a..4d1d7bb1ff5b 100644 --- a/tests/pos/i4345.scala +++ b/tests/pos/i4345.scala @@ -7,4 +7,8 @@ class Test { @tailrec final def loop(c: Context)(trees: List[c.Tree]): Boolean = loop(c)(trees) + + @tailrec + final def loop2(c: Context, trees: List[c.Tree]): Boolean = + loop2(c, trees) } From 97f90263a0f87a33ed06c3d954b91225cfcc775a Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Thu, 19 Apr 2018 21:14:46 +0200 Subject: [PATCH 4/4] Add more testcases for this PR --- tests/pos/i4345.scala | 44 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/tests/pos/i4345.scala b/tests/pos/i4345.scala index 4d1d7bb1ff5b..6bb1f0de980b 100644 --- a/tests/pos/i4345.scala +++ b/tests/pos/i4345.scala @@ -3,7 +3,7 @@ class Context { type Tree } -class Test { +class TestSimple { @tailrec final def loop(c: Context)(trees: List[c.Tree]): Boolean = loop(c)(trees) @@ -11,4 +11,46 @@ class Test { @tailrec final def loop2(c: Context, trees: List[c.Tree]): Boolean = loop2(c, trees) + + @tailrec + final def loop3[A <: Context](c: A, trees: List[c.Tree]): Boolean = + loop3(c, trees) +} + +class TestVCParameterized[C <: Context](val classC: C) extends AnyVal { + @tailrec + final def loop(c: C)(trees: List[c.Tree]): List[c.Tree] = + loop(c)(trees) + + @tailrec + final def loop2(c: C, trees: List[c.Tree]): List[c.Tree] = + loop2(c, trees) + + @tailrec + final def loop3[A <: C](c: A, trees: List[c.Tree]): List[c.Tree] = + loop3(c, trees) + + @tailrec + final def loop4(trees: List[classC.Tree]): List[classC.Tree] = + loop4(trees) + + def loopNonRec(c: C)(trees: List[c.Tree]): List[c.Tree] = { + loopNonRec(c)(trees) + loopNonRec(c)(trees) + } + + def loopNonRec2(c: C, trees: List[c.Tree]): List[c.Tree] = { + loopNonRec2(c, trees) + loopNonRec2(c, trees) + } + + def loopNonRec3[A <: Context](c: A, trees: List[c.Tree]): List[classC.Tree] = { + loopNonRec3(c, trees) + loopNonRec3(c, trees) + } + + def loopNonRec4(trees: List[classC.Tree]): List[classC.Tree] = { + loopNonRec4(trees) + loopNonRec4(trees) + } }