Skip to content

Commit 428b5ea

Browse files
authored
Merge pull request #4346 from dotty-staging/fix/termparamref-subtyping
Fix #4345: issues with dependent methods
2 parents 9c83d27 + 97f9026 commit 428b5ea

File tree

5 files changed

+88
-16
lines changed

5 files changed

+88
-16
lines changed

compiler/src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,18 +193,31 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
193193
def polyDefDef(sym: TermSymbol, rhsFn: List[Type] => List[List[Tree]] => Tree)(implicit ctx: Context): DefDef = {
194194
val (tparams, mtp) = sym.info match {
195195
case tp: PolyType =>
196-
val tparams = ctx.newTypeParams(sym, tp.paramNames, EmptyFlags, tp.instantiateBounds)
196+
val tparams = ctx.newTypeParams(sym, tp.paramNames, EmptyFlags, tp.instantiateParamInfos(_))
197197
(tparams, tp.instantiate(tparams map (_.typeRef)))
198198
case tp => (Nil, tp)
199199
}
200200

201201
def valueParamss(tp: Type): (List[List[TermSymbol]], Type) = tp match {
202202
case tp: MethodType =>
203-
def valueParam(name: TermName, info: Type): TermSymbol = {
203+
val isParamDependent = tp.isParamDependent
204+
val previousParamRefs = if (isParamDependent) new mutable.ListBuffer[TermRef]() else null
205+
206+
def valueParam(name: TermName, origInfo: Type): TermSymbol = {
204207
val maybeImplicit = if (tp.isImplicitMethod) Implicit else EmptyFlags
205208
val maybeErased = if (tp.isErasedMethod) Erased else EmptyFlags
206-
ctx.newSymbol(sym, name, TermParam | maybeImplicit | maybeErased, info, coord = sym.coord)
209+
210+
def makeSym(info: Type) = ctx.newSymbol(sym, name, TermParam | maybeImplicit | maybeErased, info, coord = sym.coord)
211+
212+
if (isParamDependent) {
213+
val sym = makeSym(origInfo.substParams(tp, previousParamRefs.toList))
214+
previousParamRefs += sym.termRef
215+
sym
216+
}
217+
else
218+
makeSym(origInfo)
207219
}
220+
208221
val params = (tp.paramNames, tp.paramInfos).zipped.map(valueParam)
209222
val (paramss, rtp) = valueParamss(tp.instantiate(params map (_.termRef)))
210223
(params :: paramss, rtp)

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2664,6 +2664,12 @@ object Types {
26642664
myParamRefs
26652665
}
26662666

2667+
/** Like `paramInfos` but substitute parameter references with the given arguments */
2668+
final def instantiateParamInfos(argTypes: => List[Type])(implicit ctx: Context): List[Type] =
2669+
if (isParamDependent) paramInfos.mapConserve(_.substParams(this, argTypes))
2670+
else paramInfos
2671+
2672+
/** Like `resultType` but substitute parameter references with the given arguments */
26672673
final def instantiate(argTypes: => List[Type])(implicit ctx: Context): Type =
26682674
if (isResultDependent) resultType.substParams(this, argTypes)
26692675
else resultType
@@ -2998,10 +3004,6 @@ object Types {
29983004
lazy val typeParams: List[LambdaParam] =
29993005
paramNames.indices.toList.map(new LambdaParam(this, _))
30003006

3001-
/** Instantiate parameter bounds by substituting parameters with given arguments */
3002-
final def instantiateBounds(argTypes: List[Type])(implicit ctx: Context): List[Type] =
3003-
paramInfos.mapConserve(_.substParams(this, argTypes))
3004-
30053007
def derivedLambdaAbstraction(paramNames: List[TypeName], paramInfos: List[TypeBounds], resType: Type)(implicit ctx: Context): Type =
30063008
resType match {
30073009
case resType @ TypeAlias(alias) =>

compiler/src/dotty/tools/dotc/transform/FullParameterization.scala

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -233,14 +233,15 @@ trait FullParameterization {
233233
fun.appliedToArgss(originalDef.vparamss.nestedMap(vparam => ref(vparam.symbol)))
234234
else {
235235
// this type could have changed on forwarding. Need to insert a cast.
236-
val args = (originalDef.vparamss, fun.tpe.paramInfoss).zipped.map((vparams, paramTypes) =>
237-
(vparams, paramTypes).zipped.map((vparam, paramType) => {
238-
assert(vparam.tpe <:< paramType.widen) // type should still conform to widened type
239-
ref(vparam.symbol).ensureConforms(paramType)
240-
})
241-
)
242-
fun.appliedToArgss(args)
243-
236+
originalDef.vparamss.foldLeft(fun)((acc, vparams) => {
237+
val meth = acc.tpe.asInstanceOf[MethodType]
238+
val paramTypes = meth.instantiateParamInfos(vparams.map(_.tpe))
239+
acc.appliedToArgs(
240+
(vparams, paramTypes).zipped.map((vparam, paramType) => {
241+
assert(vparam.tpe <:< paramType.widen) // type should still conform to widened type
242+
ref(vparam.symbol).ensureConforms(paramType)
243+
}))
244+
})
244245
}).withPos(originalDef.rhs.pos)
245246
}
246247
}

compiler/src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1120,7 +1120,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
11201120
val tp1Params = tp1.newLikeThis(tp1.paramNames, tp1.paramInfos, defn.AnyType)
11211121
fullyDefinedType(tp1Params, "type parameters of alternative", alt1.symbol.pos)
11221122

1123-
val tparams = ctx.newTypeParams(alt1.symbol, tp1.paramNames, EmptyFlags, tp1.instantiateBounds)
1123+
val tparams = ctx.newTypeParams(alt1.symbol, tp1.paramNames, EmptyFlags, tp1.instantiateParamInfos(_))
11241124
isAsSpecific(alt1, tp1.instantiate(tparams.map(_.typeRef)), alt2, tp2)
11251125
}
11261126
case _ => // (3)

tests/pos/i4345.scala

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import scala.annotation.tailrec
2+
class Context {
3+
type Tree
4+
}
5+
6+
class TestSimple {
7+
@tailrec
8+
final def loop(c: Context)(trees: List[c.Tree]): Boolean =
9+
loop(c)(trees)
10+
11+
@tailrec
12+
final def loop2(c: Context, trees: List[c.Tree]): Boolean =
13+
loop2(c, trees)
14+
15+
@tailrec
16+
final def loop3[A <: Context](c: A, trees: List[c.Tree]): Boolean =
17+
loop3(c, trees)
18+
}
19+
20+
class TestVCParameterized[C <: Context](val classC: C) extends AnyVal {
21+
@tailrec
22+
final def loop(c: C)(trees: List[c.Tree]): List[c.Tree] =
23+
loop(c)(trees)
24+
25+
@tailrec
26+
final def loop2(c: C, trees: List[c.Tree]): List[c.Tree] =
27+
loop2(c, trees)
28+
29+
@tailrec
30+
final def loop3[A <: C](c: A, trees: List[c.Tree]): List[c.Tree] =
31+
loop3(c, trees)
32+
33+
@tailrec
34+
final def loop4(trees: List[classC.Tree]): List[classC.Tree] =
35+
loop4(trees)
36+
37+
def loopNonRec(c: C)(trees: List[c.Tree]): List[c.Tree] = {
38+
loopNonRec(c)(trees)
39+
loopNonRec(c)(trees)
40+
}
41+
42+
def loopNonRec2(c: C, trees: List[c.Tree]): List[c.Tree] = {
43+
loopNonRec2(c, trees)
44+
loopNonRec2(c, trees)
45+
}
46+
47+
def loopNonRec3[A <: Context](c: A, trees: List[c.Tree]): List[classC.Tree] = {
48+
loopNonRec3(c, trees)
49+
loopNonRec3(c, trees)
50+
}
51+
52+
def loopNonRec4(trees: List[classC.Tree]): List[classC.Tree] = {
53+
loopNonRec4(trees)
54+
loopNonRec4(trees)
55+
}
56+
}

0 commit comments

Comments
 (0)