Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add some logging to sinful typevar methods.

These super-mutation-oriented methods should enthusiastically
communicate what they are doing, especially when they encounter
anything unexpected. None of this work should be taken as an
endorsement of any of the worked-upon code.
  • Loading branch information...
commit c10df64f4d05d5c01c027c4f519715cf7fb44e1e 1 parent 305a987
Paul Phillips authored
View
42 src/compiler/scala/tools/nsc/typechecker/Contexts.scala
@@ -8,6 +8,7 @@ package typechecker
import scala.collection.mutable
import scala.annotation.tailrec
+import scala.reflect.internal.util.shortClassOfInstance
/**
* @author Martin Odersky
@@ -175,6 +176,7 @@ trait Contexts { self: Analyzer =>
if ((owner eq NoSymbol) || (owner.isClass) || (owner.isMethod)) this
else outer.enclClassOrMethod
+ def enclosingCaseDef = nextEnclosing(_.tree.isInstanceOf[CaseDef])
def undetparamsString =
if (undetparams.isEmpty) ""
else undetparams.mkString("undetparams=", ", ", "")
@@ -584,23 +586,39 @@ trait Contexts { self: Analyzer =>
}
def pushTypeBounds(sym: Symbol) {
+ sym.info match {
+ case tb: TypeBounds => if (!tb.isEmptyBounds) log(s"Saving $sym info=$tb")
+ case info => devWarning(s"Something other than a TypeBounds seen in pushTypeBounds: $info is a ${shortClassOfInstance(info)}")
+ }
savedTypeBounds ::= ((sym, sym.info))
}
def restoreTypeBounds(tp: Type): Type = {
- var current = tp
- for ((sym, info) <- savedTypeBounds) {
- debuglog("resetting " + sym + " to " + info)
- sym.info match {
- case TypeBounds(lo, hi) if (hi <:< lo && lo <:< hi) =>
- current = current.instantiateTypeParams(List(sym), List(lo))
-//@M TODO: when higher-kinded types are inferred, probably need a case PolyType(_, TypeBounds(...)) if ... =>
- case _ =>
- }
- sym.setInfo(info)
+ def restore(): Type = savedTypeBounds.foldLeft(tp) { case (current, (sym, savedInfo)) =>
+ def bounds_s(tb: TypeBounds) = if (tb.isEmptyBounds) "<empty bounds>" else s"TypeBounds(lo=${tb.lo}, hi=${tb.hi})"
+ //@M TODO: when higher-kinded types are inferred, probably need a case PolyType(_, TypeBounds(...)) if ... =>
+ val tb @ TypeBounds(lo, hi) = sym.info.bounds
+ val isUnique = lo <:< hi && hi <:< lo
+ val isPresent = current contains sym
+ def saved_s = bounds_s(savedInfo.bounds)
+ def current_s = bounds_s(sym.info.bounds)
+
+ if (isUnique && isPresent)
+ devWarningResult(s"Preserving inference: ${sym.nameString}=$hi in $current (based on $current_s) before restoring $sym to saved $saved_s")(
+ current.instantiateTypeParams(List(sym), List(hi))
+ )
+ else if (isPresent)
+ devWarningResult(s"Discarding inferred $current_s because it does not uniquely determine $sym in")(current)
+ else
+ logResult(s"Discarding inferred $current_s because $sym does not appear in")(current)
+ }
+ try restore()
+ finally {
+ for ((sym, savedInfo) <- savedTypeBounds)
+ sym setInfo debuglogResult(s"Discarding inferred $sym=${sym.info}, restoring saved info")(savedInfo)
+
+ savedTypeBounds = Nil
}
- savedTypeBounds = List()
- current
}
private var implicitsCache: List[List[ImplicitInfo]] = null
View
57 src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -1317,15 +1317,18 @@ trait Infer extends Checkable {
}
}
- def instBounds(tvar: TypeVar): (Type, Type) = {
- val tparam = tvar.origin.typeSymbol
- val instType = toOrigin(tvar.constr.inst)
+ def instBounds(tvar: TypeVar): TypeBounds = {
+ val tparam = tvar.origin.typeSymbol
+ val instType = toOrigin(tvar.constr.inst)
+ val TypeBounds(lo, hi) = tparam.info.bounds
val (loBounds, hiBounds) =
- if (instType != NoType && isFullyDefined(instType)) (List(instType), List(instType))
+ if (isFullyDefined(instType)) (List(instType), List(instType))
else (tvar.constr.loBounds, tvar.constr.hiBounds)
- val lo = lub(tparam.info.bounds.lo :: loBounds map toOrigin)
- val hi = glb(tparam.info.bounds.hi :: hiBounds map toOrigin)
- (lo, hi)
+
+ TypeBounds(
+ lub(lo :: loBounds map toOrigin),
+ glb(hi :: hiBounds map toOrigin)
+ )
}
def isInstantiatable(tvars: List[TypeVar]) = {
@@ -1335,33 +1338,25 @@ trait Infer extends Checkable {
solve(tvars1, tvars1 map (_.origin.typeSymbol), tvars1 map (_ => Variance.Covariant), false)
}
- // this is quite nasty: it destructively changes the info of the syms of e.g., method type params (see #3692, where the type param T's bounds were set to >: T <: T, so that parts looped)
+ // this is quite nasty: it destructively changes the info of the syms of e.g., method type params
+ // (see #3692, where the type param T's bounds were set to > : T <: T, so that parts looped)
// the changes are rolled back by restoreTypeBounds, but might be unintentially observed in the mean time
def instantiateTypeVar(tvar: TypeVar) {
- val tparam = tvar.origin.typeSymbol
- if (false &&
- tvar.constr.inst != NoType &&
- isFullyDefined(tvar.constr.inst) &&
- (tparam.info.bounds containsType tvar.constr.inst)) {
- context.nextEnclosing(_.tree.isInstanceOf[CaseDef]).pushTypeBounds(tparam)
- tparam setInfo tvar.constr.inst
- tparam resetFlag DEFERRED
- debuglog("new alias of " + tparam + " = " + tparam.info)
- } else {
- val (lo, hi) = instBounds(tvar)
- if (lo <:< hi) {
- if (!((lo <:< tparam.info.bounds.lo) && (tparam.info.bounds.hi <:< hi)) // bounds were improved
- && tparam != lo.typeSymbolDirect && tparam != hi.typeSymbolDirect) { // don't create illegal cycles
- context.nextEnclosing(_.tree.isInstanceOf[CaseDef]).pushTypeBounds(tparam)
- tparam setInfo TypeBounds(lo, hi)
- debuglog("new bounds of " + tparam + " = " + tparam.info)
- } else {
- debuglog("redundant: "+tparam+" "+tparam.info+"/"+lo+" "+hi)
- }
- } else {
- debuglog("inconsistent: "+tparam+" "+lo+" "+hi)
+ val tparam = tvar.origin.typeSymbol
+ val TypeBounds(lo0, hi0) = tparam.info.bounds
+ val tb @ TypeBounds(lo1, hi1) = instBounds(tvar)
+
+ if (lo1 <:< hi1) {
+ if (lo1 <:< lo0 && hi0 <:< hi1) // bounds unimproved
+ log(s"redundant bounds: discarding TypeBounds($lo1, $hi1) for $tparam, no improvement on TypeBounds($lo0, $hi0)")
+ else if (tparam == lo1.typeSymbolDirect || tparam == hi1.typeSymbolDirect)
+ log(s"cyclical bounds: discarding TypeBounds($lo1, $hi1) for $tparam because $tparam appears as bounds")
+ else {
+ context.enclosingCaseDef pushTypeBounds tparam
+ tparam setInfo logResult(s"updated bounds: $tparam from ${tparam.info} to")(tb)
}
}
+ else log(s"inconsistent bounds: discarding TypeBounds($lo1, $hi1)")
}
/** Type intersection of simple type tp1 with general type tp2.
@@ -1524,7 +1519,7 @@ trait Infer extends Checkable {
// todo: missing test case for bests.isEmpty
bests match {
case best :: Nil => tree setSymbol best setType (pre memberType best)
- case best :: competing :: _ if alts0.nonEmpty =>
+ case best :: competing :: _ if alts0.nonEmpty =>
// SI-6912 Don't give up and leave an OverloadedType on the tree.
// Originally I wrote this as `if (secondTry) ... `, but `tryTwice` won't attempt the second try
// unless an error is issued. We're not issuing an error, in the assumption that it would be
View
7 src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -2438,16 +2438,13 @@ trait Typers extends Adaptations with Tags {
else typed(cdef.guard, BooleanClass.tpe)
var body1: Tree = typed(cdef.body, pt)
- val contextWithTypeBounds = context.nextEnclosing(_.tree.isInstanceOf[CaseDef])
- if (contextWithTypeBounds.savedTypeBounds.nonEmpty) {
- body1 modifyType (contextWithTypeBounds restoreTypeBounds _)
-
+ if (context.enclosingCaseDef.savedTypeBounds.nonEmpty) {
+ body1 modifyType context.enclosingCaseDef.restoreTypeBounds
// insert a cast if something typechecked under the GADT constraints,
// but not in real life (i.e., now that's we've reset the method's type skolems'
// infos back to their pre-GADT-constraint state)
if (isFullyDefined(pt) && !(body1.tpe <:< pt))
body1 = typedPos(body1.pos)(gen.mkCast(body1, pt.dealiasWiden))
-
}
// body1 = checkNoEscaping.locals(context.scope, pt, body1)
Please sign in to comment.
Something went wrong with that request. Please try again.