From c10df64f4d05d5c01c027c4f519715cf7fb44e1e Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Mon, 4 Mar 2013 08:34:56 -0800 Subject: 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. --- .../scala/tools/nsc/typechecker/Contexts.scala | 42 +++++++++++----- .../scala/tools/nsc/typechecker/Infer.scala | 57 ++++++++++------------ .../scala/tools/nsc/typechecker/Typers.scala | 7 +-- 3 files changed, 58 insertions(+), 48 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index eb91251930..26e39d3d1b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/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) "" 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 diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index db3759d65f..a29cc93b6d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/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 diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index c40b69bc7a..9680b911e0 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/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) -- cgit v1.2.3