From 860fd2eb286c48d36655ad99a068a2e61bdb1381 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 30 Aug 2013 11:10:37 +0200 Subject: More tests and fixes related to inference. Main blooper: TypeVars got dereferenced in all Typemaps, so disappeared too quickly from types during inference. --- src/dotty/tools/dotc/core/TypeComparer.scala | 10 +++--- src/dotty/tools/dotc/core/TyperState.scala | 10 ++++-- src/dotty/tools/dotc/core/Types.scala | 45 ++++++++++++------------ src/dotty/tools/dotc/printing/PlainPrinter.scala | 5 +-- src/dotty/tools/dotc/typer/ErrorReporting.scala | 2 +- src/dotty/tools/dotc/typer/Inferencing.scala | 17 ++++++--- src/dotty/tools/dotc/typer/Namer.scala | 2 +- 7 files changed, 55 insertions(+), 36 deletions(-) (limited to 'src/dotty/tools') diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index b7ae92b46..bdfe21561 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -60,6 +60,7 @@ class TypeComparer(implicit val ctx: Context) extends DotClass { val bounds = constraint(param).asInstanceOf[TypeBounds] val bound = if (fromBelow) bounds.lo else bounds.hi val inst = removeParam(bound) + println(s"approx ${param.show}, from below = $fromBelow, bound = ${bound.show}, inst = ${inst.show}") constraint = constraint.replace(param, inst) inst } @@ -130,7 +131,7 @@ class TypeComparer(implicit val ctx: Context) extends DotClass { case _ => secondTry(tp1, tp2) } case tp2: TypeVar => - firstTry(tp1, tp2.thisInstance) + firstTry(tp1, tp2.underlying) case tp2: WildcardType => tp2.optBounds match { case TypeBounds(_, hi) => isSubType(tp1, hi) @@ -152,7 +153,7 @@ class TypeComparer(implicit val ctx: Context) extends DotClass { case _ => thirdTry(tp1, tp2) } case tp1: TypeVar => - secondTry(tp1.thisInstance, tp2) + secondTry(tp1.underlying, tp2) case tp1: WildcardType => tp1.optBounds match { case TypeBounds(lo, _) => isSubType(lo, tp2) @@ -168,8 +169,9 @@ class TypeComparer(implicit val ctx: Context) extends DotClass { def thirdTryNamed(tp1: Type, tp2: NamedType): Boolean = tp2.info match { case TypeBounds(lo2, hi2) => - isSubType(tp1, lo2) || - (tp2.symbol is GADTFlexType) && trySetType(tp2, TypeBounds(lo2 | tp1, hi2)) + (isSubType(tp1, lo2) + || (tp2.symbol is GADTFlexType) && trySetType(tp2, TypeBounds(lo2 | tp1, hi2)) + || fourthTry(tp1, tp2)) case _ => val cls2 = tp2.symbol (cls2 == defn.SingletonClass && tp1.isStable diff --git a/src/dotty/tools/dotc/core/TyperState.scala b/src/dotty/tools/dotc/core/TyperState.scala index b00e55e29..7f161d994 100644 --- a/src/dotty/tools/dotc/core/TyperState.scala +++ b/src/dotty/tools/dotc/core/TyperState.scala @@ -18,7 +18,7 @@ class TyperState(val reporter: Reporter = ThrowingReporter) extends DotClass { /** A map that records for instantiated type vars their instance type. * Used only in a temporary way for contexts that may be retracted - * without also retracting the type var. + * without also retracting the type var. */ def instType: SimpleMap[TypeVar, Type] = SimpleMap.Empty @@ -34,6 +34,9 @@ class TyperState(val reporter: Reporter = ThrowingReporter) extends DotClass { class MutableTyperState(previous: TyperState, reporter: Reporter) extends TyperState(reporter) { + def checkConsistent() = + for (tvar <- undetVars) assert(constraint(tvar.origin) != NoType, tvar) + private var myConstraint: Constraint = previous.constraint private var myUndetVars: Set[TypeVar] = previous.undetVars private var myInstType: SimpleMap[TypeVar, Type] = previous.instType @@ -43,7 +46,10 @@ extends TyperState(reporter) { override def instType = myInstType override def constraint_=(c: Constraint) = myConstraint = c - override def undetVars_=(vs: Set[TypeVar]) = myUndetVars = vs + override def undetVars_=(vs: Set[TypeVar]) = { + myUndetVars = vs + //checkConsistent() + } override def instType_=(m: SimpleMap[TypeVar, Type]): Unit = myInstType = m override def fresh: TyperState = new MutableTyperState(this, new StoreReporter) diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index fc2592b46..19c0e3128 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -117,7 +117,7 @@ object Types { classSymbol is ModuleClass /** Is this type produced as a repair for an error? */ - final def isError(implicit ctx: Context): Boolean = thisInstance match { + final def isError(implicit ctx: Context): Boolean = stripTypeVar match { case ErrorType => true case tp => (tp.typeSymbol is Erroneous) || (tp.termSymbol is Erroneous) } @@ -144,7 +144,7 @@ object Types { ctx.isVolatile(this) /** Does the type carry an annotation that is an instance of `cls`? */ - final def hasAnnotation(cls: ClassSymbol)(implicit ctx: Context): Boolean = thisInstance match { + final def hasAnnotation(cls: ClassSymbol)(implicit ctx: Context): Boolean = stripTypeVar match { case AnnotatedType(annot, tp) => annot.symbol == cls || tp.hasAnnotation(cls) case _ => false } @@ -177,15 +177,15 @@ object Types { final def foreach(f: Type => Unit): Unit = ??? /** Map function over elements of an AndType, rebuilding with & */ - def mapAnd(f: Type => Type)(implicit ctx: Context): Type = thisInstance match { + def mapAnd(f: Type => Type)(implicit ctx: Context): Type = stripTypeVar match { case AndType(tp1, tp2) => tp1.mapAnd(f) & tp2.mapAnd(f) - case tp => f(tp) + case _ => f(this) } /** Map function over elements of an OrType, rebuilding with | */ - final def mapOr(f: Type => Type)(implicit ctx: Context): Type = thisInstance match { + final def mapOr(f: Type => Type)(implicit ctx: Context): Type = stripTypeVar match { case OrType(tp1, tp2) => tp1.mapOr(f) | tp2.mapOr(f) - case tp => f(tp) + case _ => f(this) } // ----- Associated symbols ---------------------------------------------- @@ -514,7 +514,7 @@ object Types { /** Map a TypeVar to either its instance if it is instantiated, or its origin, * if not. Identity on all other types. */ - def thisInstance(implicit ctx: Context): Type = this + def stripTypeVar(implicit ctx: Context): Type = this /** Widen from singleton type to its underlying non-singleton * base type by applying one or more `underlying` dereferences, @@ -535,9 +535,9 @@ object Types { } /** If this is an alias type, its alias, otherwise the type itself */ - final def dealias(implicit ctx: Context): Type = thisInstance match { + final def dealias(implicit ctx: Context): Type = stripTypeVar match { case tp: TypeRef if tp.symbol.isAliasType => tp.info.bounds.hi - case tp => tp + case _ => this } /** Widen from constant type to its underlying non-constant @@ -551,9 +551,9 @@ object Types { /** If this is a refinement type, the unrefined parent, * else the type itself. */ - final def unrefine(implicit ctx: Context): Type = thisInstance match { + final def unrefine(implicit ctx: Context): Type = stripTypeVar match { case tp @ RefinedType(tycon, _) => tycon.unrefine - case tp => tp + case _ => this } /** Map references to Object to references to Any; needed for Java interop */ @@ -801,7 +801,7 @@ object Types { */ final def typeArgs(implicit ctx: Context): List[Type] = { var tparams: List[TypeSymbol] = null - def recur(tp: Type, refineCount: Int): mutable.ListBuffer[Type] = tp match { + def recur(tp: Type, refineCount: Int): mutable.ListBuffer[Type] = tp.stripTypeVar match { case tp @ RefinedType(tycon, name) => val buf = recur(tycon, refineCount + 1) if (buf == null) null @@ -817,7 +817,7 @@ object Types { if (refineCount == 0) null else new mutable.ListBuffer[Type] } - val buf = recur(thisInstance, 0) + val buf = recur(this, 0) if (buf == null) Nil else buf.toList } @@ -840,7 +840,7 @@ object Types { * Otherwise return 0 and the type itself */ final def splitArray(implicit ctx: Context): (Int, Type) = { - def recur(n: Int, tp: Type): (Int, Type) = tp match { + def recur(n: Int, tp: Type): (Int, Type) = tp.stripTypeVar match { case RefinedType(tycon, _) if tycon.isArray => tp.typeArgs match { case arg :: Nil => recur(n + 1, arg) @@ -849,7 +849,7 @@ object Types { case _ => (n, tp) } - recur(0, thisInstance) + recur(0, this) } /** Given a type alias @@ -1759,7 +1759,7 @@ object Types { * is also a singleton type. */ def instantiate(fromBelow: Boolean)(implicit ctx: Context): Type = { - def upperBound = ctx.typerState.constraint(origin).bounds.hi + val upperBound = ctx.typerState.constraint(origin).bounds.hi def isSingleton(tp: Type): Boolean = tp match { case tp: SingletonType => true case AndType(tp1, tp2) => isSingleton(tp1) | isSingleton(tp2) @@ -1773,13 +1773,13 @@ object Types { } /** If the variable is instantiated, its instance, otherwise its origin */ - override def thisInstance(implicit ctx: Context) = { + override def stripTypeVar(implicit ctx: Context) = { val inst = instanceOpt if (inst.exists) inst else origin } - /** Same as `thisInstance` */ - override def underlying(implicit ctx: Context): Type = thisInstance + /** Same as `stripTypeVar` */ + override def underlying(implicit ctx: Context): Type = stripTypeVar override def hashCode: Int = System.identityHashCode(this) override def equals(that: Any) = this eq that.asInstanceOf[AnyRef] @@ -2027,7 +2027,7 @@ object Types { case tp: RefinedType => isInstantiatable(tp.underlying) case tp: TypeVar => - isInstantiatable(tp.thisInstance) + isInstantiatable(tp.underlying) case _ => false } @@ -2088,7 +2088,8 @@ object Types { tp.derivedAnnotatedType(mapOver(annot), this(underlying)) case tp: TypeVar => - apply(tp.thisInstance) + val inst = tp.instanceOpt + if (inst.exists) apply(inst) else tp case tp @ WildcardType => tp.derivedWildcardType(mapOver(tp.optBounds)) @@ -2192,7 +2193,7 @@ object Types { this(this(x, annot), underlying) case tp: TypeVar => - foldOver(x, tp.thisInstance) + foldOver(x, tp.underlying) case tp: WildcardType => foldOver(x, tp.optBounds) diff --git a/src/dotty/tools/dotc/printing/PlainPrinter.scala b/src/dotty/tools/dotc/printing/PlainPrinter.scala index b4e2e3770..f86075d83 100644 --- a/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -125,7 +125,8 @@ class PlainPrinter(_ctx: Context) extends Printer { case tp: ExprType => changePrec(GlobalPrec) { "=> " ~ toText(tp.resultType) } case tp: PolyType => - def paramText(name: TypeName, bounds: TypeBounds) = toText(polyParamName(name)) ~ ": " ~ toText(bounds) + def paramText(name: TypeName, bounds: TypeBounds) = + toText(polyParamName(name)) ~ toText(bounds) changePrec(GlobalPrec) { "[" ~ Text((tp.paramNames, tp.paramBounds).zipped map paramText, ", ") ~ @@ -136,7 +137,7 @@ class PlainPrinter(_ctx: Context) extends Printer { case AnnotatedType(annot, tpe) => toTextLocal(tpe) ~ " " ~ toText(annot) case tp: TypeVar => - toTextLocal(tp.underlying) + toTextLocal(tp.underlying) ~ "'" // debug for now, so that we can see where the TypeVars are. case _ => tp.fallbackToText(this) } diff --git a/src/dotty/tools/dotc/typer/ErrorReporting.scala b/src/dotty/tools/dotc/typer/ErrorReporting.scala index 177882688..cda37f486 100644 --- a/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -93,7 +93,7 @@ object ErrorReporting { def treatArg(arg: Any, suffix: String): (Any, String) = arg match { case arg: Showable => (arg.show, suffix) - case arg: List[_] if suffix.head == '%' => + case arg: List[_] if suffix.nonEmpty && suffix.head == '%' => val (sep, rest) = suffix.tail.span(_ != '%') if (rest.nonEmpty) (arg mkString sep, rest.tail) else (arg, suffix) diff --git a/src/dotty/tools/dotc/typer/Inferencing.scala b/src/dotty/tools/dotc/typer/Inferencing.scala index 06b6e1487..1083b4f2e 100644 --- a/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/src/dotty/tools/dotc/typer/Inferencing.scala @@ -124,6 +124,7 @@ object Inferencing { false case tvar: TypeVar if forceIt && !tvar.isInstantiated => val inst = tvar.instantiate(fromBelow = true) + println(i"forced instantiation of ${tvar.origin} = $inst") inst != defn.NothingType && inst != defn.NullType case _ => true @@ -193,10 +194,18 @@ object Inferencing { val vs = tp.variances(tvar => (ctx.typerState.undetVars contains tvar) && (pos contains tvar.pos)) for ((tvar, v) <- vs) - if (v == 1) tvar.instantiate(fromBelow = true) - else if (v == -1) tvar.instantiate(fromBelow = false) - for (tvar <- ctx.typerState.undetVars if !(vs contains tvar)) - tvar.instantiate(fromBelow = false) + if (v == 1) { + println(i"interpolate covariant $tvar in $tp") + tvar.instantiate(fromBelow = true) + } + else if (v == -1) { + println(i"interpolate contrvariant $tvar in $tp") + tvar.instantiate(fromBelow = false) + } + for (tvar <- ctx.typerState.undetVars if (pos contains tvar.pos) && !(vs contains tvar)) { + println(i"interpolate non-occurring $tvar in $tp") + tvar.instantiate(fromBelow = true) + } } /** Instantiate undetermined type variables to that type `tp` is diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 97cc37cc3..265326480 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -339,7 +339,7 @@ class Namer { typer: Typer => } /** Typecheck tree during completion, and remember result in typedtree map */ - private def typedAheadImpl(tree: Tree, pt: Type)(implicit ctx: Context): tpd.Tree = + private def typedAheadImpl(tree: Tree, pt: Type)(implicit ctx: Context): tpd.Tree = typedTree.getOrElseUpdate(expanded(tree), typer.typedUnadapted(tree, pt)) def typedAheadType(tree: Tree, pt: Type = WildcardType)(implicit ctx: Context): tpd.Tree = -- cgit v1.2.3