Skip to content

Fix #4345: issues with dependent methods #4346

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 20, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -193,18 +193,31 @@ 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)
}

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)
Expand Down
10 changes: 6 additions & 4 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
56 changes: 56 additions & 0 deletions tests/pos/i4345.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import scala.annotation.tailrec
class Context {
type Tree
}

class TestSimple {
@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)

@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)
}
}