Skip to content

Fix #779 recursive hk types #787

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 2 commits into from
Sep 14, 2015
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
12 changes: 11 additions & 1 deletion src/dotty/tools/dotc/core/TypeApplications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,17 @@ class TypeApplications(val self: Type) extends AnyVal {
if (args.isEmpty || ctx.erasedTypes) self
else {
val res = instantiate(self, self)
if (res.isInstantiatedLambda) res.select(tpnme.Apply) else res
if (res.isInstantiatedLambda)
// Note: isInstantiatedLambda needs to be conservative, using isSafeLambda
// in order to avoid cyclic reference errors. But this means that some fully
// instantiated types will remain unprojected, which essentially means
// that they stay as higher-kinded types. checkNonCyclic checks the type again
// and potentially inserts an #Apply then. Hopefully, this catches all types
// that fall through the hole. Not adding an #Apply typically manifests itself
// with a <:< failure of two types that "look the same". An example is #779,
// where compiling scala.immutable.Map gives a bounds violation.
res.select(tpnme.Apply)
else res
}
}

Expand Down
7 changes: 5 additions & 2 deletions src/dotty/tools/dotc/core/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
* for more complicated types.
*/
final def asSeenFrom(tp: Type, pre: Type, cls: Symbol): Type = {
val m = if (pre.isStable || !ctx.phase.isTyper) null else new AsSeenFromMap(pre, cls)
val m = if (isLegalPrefix(pre)) null else new AsSeenFromMap(pre, cls)
var res = asSeenFrom(tp, pre, cls, m)
if (m != null && m.unstable) asSeenFrom(tp, SkolemType(pre), cls) else res
}
Expand All @@ -61,7 +61,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
if ((pre eq NoType) || (pre eq NoPrefix) || (cls is PackageClass))
tp
else if (thiscls.derivesFrom(cls) && pre.baseTypeRef(thiscls).exists) {
if (theMap != null && theMap.currentVariance <= 0 && !pre.isStable)
if (theMap != null && theMap.currentVariance <= 0 && !isLegalPrefix(pre))
theMap.unstable = true
pre match {
case SuperType(thispre, _) => thispre
Expand Down Expand Up @@ -111,6 +111,9 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
}
}

private def isLegalPrefix(pre: Type)(implicit ctx: Context) =
pre.isStable || !ctx.phase.isTyper

/** The TypeMap handling the asSeenFrom in more complicated cases */
class AsSeenFromMap(pre: Type, cls: Symbol) extends TypeMap {
def apply(tp: Type) = asSeenFrom(tp, pre, cls, this)
Expand Down
20 changes: 18 additions & 2 deletions src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,18 @@ object Checking {
val parent1 = this(parent)
val saved = cycleOK
cycleOK = nestedCycleOK
try tp.derivedRefinedType(parent1, name, this(tp.refinedInfo))
finally cycleOK = saved

/** A derived refined type with two possible tweaks:
* (1) LazyRefs in parents are pulled out,
* (2) #Apply is added if the type is a fully applied type lambda.
*/
def derivedType(p: Type): Type = p match {
case p: LazyRef => LazyRef(() => derivedType(p.ref))
case _ =>
val res = tp.derivedRefinedType(p, name, this(tp.refinedInfo))
if (res.isSafeLambda && res.typeParams.isEmpty) res.select(tpnme.Apply) else res
}
try derivedType(parent1) finally cycleOK = saved
case tp @ TypeRef(pre, name) =>
try {
// A prefix is interesting if it might contain (transitively) a reference
Expand All @@ -130,6 +140,9 @@ object Checking {
case SuperType(thistp, _) => isInteresting(thistp)
case AndType(tp1, tp2) => isInteresting(tp1) || isInteresting(tp2)
case OrType(tp1, tp2) => isInteresting(tp1) && isInteresting(tp2)
case _: RefinedType => false
// Note: it's important not to visit parents of RefinedTypes,
// since otherwise spurious #Apply projections might be inserted.
case _ => false
}
// If prefix is interesting, check info of typeref recursively, marking the referred symbol
Expand Down Expand Up @@ -158,6 +171,9 @@ object Checking {
* @pre sym is not yet initialized (i.e. its type is a Completer).
* @return `info` where every legal F-bounded reference is proctected
* by a `LazyRef`, or `ErrorType` if a cycle was detected and reported.
* Furthermore: Add an #Apply to a fully instantiated type lambda, if none was
* given before. This is necessary here because sometimes type lambdas are not
* recognized when they are first formed.
*/
def checkNonCyclic(sym: Symbol, info: Type, reportErrors: Boolean)(implicit ctx: Context): Type = {
val checker = new CheckNonCyclicMap(sym, reportErrors)(ctx.addMode(Mode.CheckCyclic))
Expand Down