diff options
author | Martin Odersky <odersky@gmail.com> | 2013-09-16 17:56:17 +0200 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2013-09-16 17:56:17 +0200 |
commit | dba4f1ec3dc5f489056284aab63c771cdd32561c (patch) | |
tree | ab6af09b871c2a01741fd5bbe874a1e19a3fddea /src/dotty/tools/dotc/core | |
parent | 682cf32cbe5cebeccf9ea10caf43e74c9cecc7c0 (diff) | |
download | dotty-dba4f1ec3dc5f489056284aab63c771cdd32561c.tar.gz dotty-dba4f1ec3dc5f489056284aab63c771cdd32561c.tar.bz2 dotty-dba4f1ec3dc5f489056284aab63c771cdd32561c.zip |
Bug fixes and improvements in error diagnostics.
Main refactoring: lub/glb moves to type comparers.
Diffstat (limited to 'src/dotty/tools/dotc/core')
-rw-r--r-- | src/dotty/tools/dotc/core/Constraint.scala | 15 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Decorators.scala | 1 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/SymDenotations.scala | 6 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/TypeComparer.scala | 223 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/TypeOps.scala | 108 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/TyperState.scala | 15 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Types.scala | 8 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/pickling/UnPickler.scala | 2 |
8 files changed, 227 insertions, 151 deletions
diff --git a/src/dotty/tools/dotc/core/Constraint.scala b/src/dotty/tools/dotc/core/Constraint.scala index c74428be5..af797c09b 100644 --- a/src/dotty/tools/dotc/core/Constraint.scala +++ b/src/dotty/tools/dotc/core/Constraint.scala @@ -43,7 +43,7 @@ class Constraint(val map: SimpleMap[PolyType, Array[Type]]) extends AnyVal with /** A new constraint which is derived from this constraint by removing * the type parameter `param` from the domain. */ - def - (param: PolyParam) = { + def - (param: PolyParam)(implicit ctx: Context) = { val pt = param.binder val pnum = param.paramNum val entries = map(pt) @@ -107,11 +107,16 @@ class Constraint(val map: SimpleMap[PolyType, Array[Type]]) extends AnyVal with if entries(n).exists } yield PolyParam(poly, n) - override def toText(printer: Printer): Text = { - val dom = domainPolys map (_.toText(printer)) + def constrainedTypesText(printer: Printer): Text = + Text(domainPolys map (_.toText(printer)), ", ") + + def constraintText(indent: Int, printer: Printer): Text = { val assocs = for (param <- domainParams) - yield " " ~ param.toText(printer) ~ this(param).toText(printer) - "Constraint(" ~ Text(dom, ", ") ~ ") {" ~ Text(assocs, "\n") ~ "}" + yield (" " * indent) ~ param.toText(printer) ~ this(param).toText(printer) + Text(assocs, "\n") } + + override def toText(printer: Printer): Text = + "Constraint(" ~ constrainedTypesText(printer) ~ ") {" ~ constraintText(2, printer) ~ "}" } diff --git a/src/dotty/tools/dotc/core/Decorators.scala b/src/dotty/tools/dotc/core/Decorators.scala index 476b97ec2..7888d2f8c 100644 --- a/src/dotty/tools/dotc/core/Decorators.scala +++ b/src/dotty/tools/dotc/core/Decorators.scala @@ -6,6 +6,7 @@ import Symbols._ import Contexts._, Names._, Phases._, printing.Texts._, printing.Printer import util.Positions.Position, util.SourcePosition import collection.mutable.ListBuffer +import scala.language.implicitConversions /** This object provides useful implicit decorators for types defined elsewhere */ object Decorators { diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 135dfe148..0d1c9b597 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -739,13 +739,11 @@ object SymDenotations { myThisType } - private def computeThisType(implicit ctx: Context): Type = ThisType(classSymbol) - /* was: + private def computeThisType(implicit ctx: Context): Type = ThisType(classSymbol) /* if ((this is PackageClass) && !isRoot) TermRef(owner.thisType, name.toTermName) else - ThisType(classSymbol) - */ + ThisType(classSymbol) */ private[this] var myTypeConstructor: TypeRef = null diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index b3039c730..cf7b18f88 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -21,23 +21,47 @@ class TypeComparer(initctx: Context) extends DotClass { private var pendingSubTypes: mutable.Set[(Type, Type)] = null private var recCount = 0 + private var frozenConstraint = false + + private var myAnyType: Type = null + private var myNothingType: Type = null + private var myNullType: Type = null + private var myObjectType: Type = null + def AnyType = { + if (myAnyType == null) myAnyType = defn.AnyType + myAnyType + } + def NothingType = { + if (myNothingType == null) myNothingType = defn.NothingType + myNothingType + } + def NullType = { + if (myNullType == null) myNullType = defn.NullType + myNullType + } + def ObjectType = { + if (myObjectType == null) myObjectType = defn.ObjectType + myObjectType + } + /** Add the constraint `<bounds.lo <: param <: bounds.hi>` * to `constraint`. * @pre `param` is in the constraint's domain */ - def addConstraint(param: PolyParam, bounds: TypeBounds): Boolean = { - val pt = param.binder - val pnum = param.paramNum - val oldEntries = constraint(pt) - val oldBounds = oldEntries(pnum).asInstanceOf[TypeBounds] - val newBounds = oldBounds & bounds - if (oldBounds ne newBounds) { - val newEntries = oldEntries.clone - newEntries(pnum) = newBounds - constraint = constraint.updated(pt, newEntries) + def addConstraint(param: PolyParam, bounds: TypeBounds): Boolean = + !frozenConstraint && { + val pt = param.binder + val pnum = param.paramNum + val oldEntries = constraint(pt) + val oldBounds = oldEntries(pnum).asInstanceOf[TypeBounds] + val newBounds = oldBounds & bounds + if (oldBounds ne newBounds) { + val newEntries = oldEntries.clone + newEntries(pnum) = newBounds + constraint = constraint.updated(pt, newEntries) + } + isSubType(newBounds.lo, newBounds.hi) } - isSubType(newBounds.lo, newBounds.hi) - } /** Solve constraint for given type parameter `param`. * If `fromBelow` is true the parameter is approximated by its lower bound, @@ -66,6 +90,12 @@ class TypeComparer(initctx: Context) extends DotClass { inst } + def isSubTypeWhenFrozen(tp1: Type, tp2: Type): Boolean = { + frozenConstraint = true + try isSubType(tp1, tp2) + finally frozenConstraint = false + } + def isSubType(tp1: Type, tp2: Type): Boolean = if (tp1 == NoType || tp2 == NoType) false else if (tp1 eq tp2) true @@ -120,7 +150,7 @@ class TypeComparer(initctx: Context) extends DotClass { tp1.name == tp2.name && isSubType(pre1, pre2) || sym2.isClass && { val base = tp1.baseType(sym2) - (base ne tp1) && isSubType(base, tp2) + base.exists && (base ne tp1) && isSubType(base, tp2) } || thirdTryNamed(tp1, tp2)) case _ => @@ -189,9 +219,11 @@ class TypeComparer(initctx: Context) extends DotClass { thirdTryNamed(tp1, tp2) case tp2: RefinedType => isSubType(tp1, tp2.parent) && ( - tp2.refinedName == nme.WILDCARD || - tp1.member(tp2.refinedName).hasAltWith(alt => - isSubType(alt.info, tp2.refinedInfo))) + tp2.refinedName == nme.WILDCARD + || (tp1 eq NothingType) + || (tp1 eq NullType) + || tp1.member(tp2.refinedName).hasAltWith(alt => + isSubType(alt.info, tp2.refinedInfo))) case AndType(tp21, tp22) => isSubType(tp1, tp21) && isSubType(tp1, tp22) case OrType(tp21, tp22) => @@ -226,7 +258,8 @@ class TypeComparer(initctx: Context) extends DotClass { case TypeBounds(lo2, hi2) => tp1 match { case TypeBounds(lo1, hi1) => - isSubType(lo2, lo1) && isSubType(hi1, hi2) + ((lo2 eq NothingType) || isSubType(lo2, lo1)) && + ((hi2 eq AnyType) || isSubType(hi1, hi2)) case tp1: ClassInfo => val tt = tp1.typeConstructor // was typeTemplate isSubType(lo2, tt) && isSubType(tt, hi2) @@ -246,8 +279,8 @@ class TypeComparer(initctx: Context) extends DotClass { def fourthTry(tp1: Type, tp2: Type): Boolean = tp1 match { case tp1: TypeRef => - ((tp1 eq defn.NothingType) - || (tp1 eq defn.NullType) && tp2.dealias.typeSymbol.isNonValueClass + ((tp1 eq NothingType) + || (tp1 eq NullType) && tp2.dealias.typeSymbol.isNonValueClass || (tp1.info match { case TypeBounds(lo1, hi1) => isSubType(hi1, tp2) || @@ -353,8 +386,8 @@ class TypeComparer(initctx: Context) extends DotClass { formals2 match { case formal2 :: rest2 => (isSameType(formal1, formal2) - || isJava1 && formal2 == defn.ObjectType && formal1 == defn.AnyType - || isJava2 && formal1 == defn.ObjectType && formal2 == defn.AnyType) && matchingParams(rest1, rest2, isJava1, isJava2) + || isJava1 && formal2 == ObjectType && formal1 == AnyType + || isJava2 && formal1 == ObjectType && formal2 == AnyType) && matchingParams(rest1, rest2, isJava1, isJava2) case nil => false } @@ -367,12 +400,156 @@ class TypeComparer(initctx: Context) extends DotClass { else if (tp1 eq tp2) true else isSubType(tp1, tp2) && isSubType(tp2, tp1) + def glb(tp1: Type, tp2: Type): Type = + if (tp1 eq tp2) tp1 + else if (!tp1.exists || (tp1 eq AnyType) || (tp2 eq NothingType)) tp2 + else if (!tp2.exists || (tp2 eq AnyType) || (tp1 eq NothingType)) tp1 + else tp2 match { // normalize to disjunctive normal form if possible. + case OrType(tp21, tp22) => + tp1 & tp21 | tp1 & tp22 + case _ => + tp1 match { + case OrType(tp11, tp12) => + tp11 & tp2 | tp12 & tp2 + case _ => + val t1 = mergeIfSub(tp1, tp2) + if (t1.exists) t1 + else { + val t2 = mergeIfSub(tp2, tp1) + if (t2.exists) t2 + else { + tp1 match { + case tp1: TypeType => + tp2 match { + case tp2: TypeType => + val b1 = tp1.bounds + val b2 = tp2.bounds + return TypeBounds(b1.lo | b2.lo, b1.hi & b2.hi) + case _ => + } + case _ => + } + AndType(tp1, tp2) + } + } + } + } + + final def glb(tps: List[Type]): Type = + (AnyType /: tps)(glb) + + def lub(tp1: Type, tp2: Type): Type = + if (tp1 eq tp2) tp1 + else if (!tp1.exists || (tp1 eq AnyType) || (tp2 eq NothingType)) tp1 + else if (!tp2.exists || (tp2 eq AnyType) || (tp1 eq NothingType)) tp2 + else { + val t1 = mergeIfSuper(tp1, tp2) + if (t1.exists) t1 + else { + val t2 = mergeIfSuper(tp2, tp1) + if (t2.exists) t2 + else { + tp1 match { + case tp1: TypeType => + tp2 match { + case tp2: TypeType => + val b1 = t1.bounds + val b2 = t2.bounds + return TypeBounds(b1.lo & b2.lo, b1.hi | b2.hi) + case _ => + } + case _ => + } + OrType(tp1, tp2) + } + } + } + + final def lub(tps: List[Type]): Type = + (NothingType /: tps)(lub) + + /** Merge `t1` into `tp2` if t1 is a subtype of some &-summand of tp2. + */ + private def mergeIfSub(tp1: Type, tp2: Type): Type = + if (isSubTypeWhenFrozen(tp1, tp2)) + if (isSubTypeWhenFrozen(tp2, tp1)) tp2 else tp1 // keep existing type if possible + else tp2 match { + case tp2 @ AndType(tp21, tp22) => + val lower1 = mergeIfSub(tp1, tp21) + if (lower1 eq tp21) tp2 + else if (lower1.exists) lower1 & tp22 + else { + val lower2 = mergeIfSub(tp1, tp22) + if (lower2 eq tp22) tp2 + else if (lower2.exists) tp21 & lower2 + else NoType + } + case _ => + NoType + } + + /** Merge `tp1` into `tp2` if tp1 is a supertype of some |-summand of tp2. + */ + private def mergeIfSuper(tp1: Type, tp2: Type): Type = + if (isSubTypeWhenFrozen(tp2, tp1)) + if (isSubTypeWhenFrozen(tp1, tp2)) tp2 else tp1 // keep existing type if possible + else tp2 match { + case tp2 @ OrType(tp21, tp22) => + val higher1 = mergeIfSuper(tp1, tp21) + if (higher1 eq tp21) tp2 + else if (higher1.exists) higher1 | tp22 + else { + val higher2 = mergeIfSuper(tp1, tp22) + if (higher2 eq tp22) tp2 + else if (higher2.exists) tp21 | higher2 + else NoType + } + case _ => + NoType + } + def copyIn(ctx: Context) = new TypeComparer(ctx) } class ExplainingTypeComparer(initctx: Context) extends TypeComparer(initctx) { - override def isSubType(tp1: Type, tp2: Type) = { - ctx.traceIndented(s"${tp1} <:< ${tp2}")(super.isSubType(tp1, tp2)) + private var indent = 0 + private val b = new StringBuilder + + def traceIndented[T](str: String)(op: => T): T = { + assert(str != "<notype> <:< Int") + indent += 2 + b append "\n" append (" " * indent) append "==> " append str + val res = op + b append "\n" append (" " * indent) append "<== " append str append " = " append show(res) + indent -= 2 + res } + + private def show(res: Any) = res match { + case res: printing.Showable if !ctx.settings.Yexplainlowlevel.value => res.show + case _ => String.valueOf(res) + } + + override def isSubType(tp1: Type, tp2: Type) = + traceIndented(s"${show(tp1)} <:< ${show(tp2)} ${tp1.getClass} ${defn.NothingType.getClass} ${tp1.normalizedPrefix} ${defn.NothingType.normalizedPrefix} ${tp1 eq defn.NothingType} ${tp1.typeSymbol eq defn.NothingClass}") { + super.isSubType(tp1, tp2) + } + + override def lub(tp1: Type, tp2: Type) = + traceIndented(s"lub(${show(tp1)}, ${show(tp2)})") { + super.lub(tp1, tp2) + } + + override def glb(tp1: Type, tp2: Type) = + traceIndented(s"glb(${show(tp1)}, ${show(tp2)})") { + super.glb(tp1, tp2) + } + override def copyIn(ctx: Context) = new ExplainingTypeComparer(ctx) + + override def toString = + "Subtype trace:" + { + try b.toString + finally b.clear() + } }
\ No newline at end of file diff --git a/src/dotty/tools/dotc/core/TypeOps.scala b/src/dotty/tools/dotc/core/TypeOps.scala index 0af244054..b4418642d 100644 --- a/src/dotty/tools/dotc/core/TypeOps.scala +++ b/src/dotty/tools/dotc/core/TypeOps.scala @@ -97,114 +97,6 @@ trait TypeOps { this: Context => } } - final def glb(tp1: Type, tp2: Type): Type = - if (tp1 eq tp2) tp1 - else if (!tp1.exists) tp2 - else if (!tp2.exists) tp1 - else tp2 match { // normalize to disjunctive normal form if possible. - case OrType(tp21, tp22) => - tp1 & tp21 | tp1 & tp22 - case _ => - tp1 match { - case OrType(tp11, tp12) => - tp11 & tp2 | tp12 & tp2 - case _ => - val t1 = mergeIfSub(tp1, tp2) - if (t1.exists) t1 - else { - val t2 = mergeIfSub(tp2, tp1) - if (t2.exists) t2 - else { - tp1 match { - case tp1: TypeType => - tp2 match { - case tp2: TypeType => - val b1 = tp1.bounds - val b2 = tp2.bounds - return TypeBounds(b1.lo | b2.lo, b1.hi & b2.hi) - case _ => - } - case _ => - } - AndType(tp1, tp2) - } - } - } - } - - final def glb(tps: List[Type]): Type = - (defn.AnyType /: tps)(glb) - - def lub(tp1: Type, tp2: Type): Type = - if (tp1 eq tp2) tp1 - else if (!tp1.exists) tp1 - else if (!tp2.exists) tp2 - else { - val t1 = mergeIfSuper(tp1, tp2) - if (t1.exists) t1 - else { - val t2 = mergeIfSuper(tp2, tp1) - if (t2.exists) t2 - else { - tp1 match { - case tp1: TypeType => - tp2 match { - case tp2: TypeType => - val b1 = t1.bounds - val b2 = t2.bounds - return TypeBounds(b1.lo & b2.lo, b1.hi | b2.hi) - case _ => - } - case _ => - } - OrType(tp1, tp2) - } - } - } - - final def lub(tps: List[Type]): Type = - (defn.NothingType /: tps)(lub) - - /** Merge `t1` into `tp2` if t1 is a subtype of some &-summand of tp2. - */ - private def mergeIfSub(tp1: Type, tp2: Type)(implicit ctx: Context): Type = - if (tp1 <:< tp2) - if (tp2 <:< tp1) tp2 else tp1 // keep existing type if possible - else tp2 match { - case tp2 @ AndType(tp21, tp22) => - val lower1 = mergeIfSub(tp1, tp21) - if (lower1 eq tp21) tp2 - else if (lower1.exists) lower1 & tp22 - else { - val lower2 = mergeIfSub(tp1, tp22) - if (lower2 eq tp22) tp2 - else if (lower2.exists) tp21 & lower2 - else NoType - } - case _ => - NoType - } - - /** Merge `tp1` into `tp2` if tp1 is a supertype of some |-summand of tp2. - */ - private def mergeIfSuper(tp1: Type, tp2: Type)(implicit ctx: Context): Type = - if (tp2 <:< tp1) - if (tp1 <:< tp2) tp2 else tp1 // keep existing type if possible - else tp2 match { - case tp2 @ OrType(tp21, tp22) => - val higher1 = mergeIfSuper(tp1, tp21) - if (higher1 eq tp21) tp2 - else if (higher1.exists) higher1 | tp22 - else { - val higher2 = mergeIfSuper(tp1, tp22) - if (higher2 eq tp22) tp2 - else if (higher2.exists) tp21 | higher2 - else NoType - } - case _ => - NoType - } - /** Normalize a list of parent types of class `cls` that may contain refinements * to a list of typerefs, by converting all refinements to member * definitions in scope `decls`. Can add members to `decls` as a side-effect. diff --git a/src/dotty/tools/dotc/core/TyperState.scala b/src/dotty/tools/dotc/core/TyperState.scala index 21dc73579..d14f6c24b 100644 --- a/src/dotty/tools/dotc/core/TyperState.scala +++ b/src/dotty/tools/dotc/core/TyperState.scala @@ -96,14 +96,17 @@ extends TyperState(reporter) { } override def toText(printer: Printer): Text = { + val header: Text = "Typer state:" val undetVarsText = - " undetVars = (" ~ - Text(undetVars map (_.toText(printer)), ", ") ~ ")" + " undetVars: " ~ + Text(undetVars map (_.toText(printer)), ", ") ~ "." + val constrainedText = + " constrained types: " ~ constraint.constrainedTypesText(printer) ~ "." val constraintText = - " constraint = " ~ constraint.toText(printer) + " constraint: " ~ constraint.constraintText(3, printer) val instTypeText = - " instType = (" ~ - Text(instType.map2((k, v) => s"${k.toText(printer)} -> ${v.toText(printer)}"), ", ") ~ ")" - Text.lines(List(undetVarsText, constraintText, instTypeText)) + " instType: " ~ + Text(instType.map2((k, v) => s"${k.toText(printer)} -> ${v.toText(printer)}"), ", ") ~ "." + Text.lines(List(header, undetVarsText, constrainedText, constraintText, instTypeText)) } } diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 9c0a0ff49..36a4986b4 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -504,10 +504,10 @@ object Types { }} def & (that: Type)(implicit ctx: Context): Type = - ctx.glb(this, that) + ctx.typeComparer.glb(this, that) def | (that: Type)(implicit ctx: Context): Type = - ctx.lub(this, that) + ctx.typeComparer.lub(this, that) // ----- Unwrapping types ----------------------------------------------- @@ -1284,6 +1284,7 @@ object Types { false } override def computeHash = doHash(fixedSym, prefix) + override def toString = super.toString + "(fixed sym)" } final class TermRefBySym(prefix: Type, name: TermName, val fixedSym: TermSymbol) @@ -1762,7 +1763,6 @@ object Types { /** Instantiate variable with given type */ def instantiateWith(tp: Type)(implicit ctx: Context): Type = { assert(owningState.undetVars contains this) - owningState.constraint -= origin owningState.undetVars -= this if (ctx.typerState eq creatorState) inst = tp else ctx.typerState.instType = ctx.typerState.instType.updated(this, tp) @@ -2010,7 +2010,7 @@ object Types { object ErrorType extends ErrorType /** Wildcard type, possibly with bounds */ - abstract case class WildcardType(optBounds: Type) extends CachedGroundType { + abstract case class WildcardType(optBounds: Type) extends CachedGroundType with TermType { def derivedWildcardType(optBounds: Type)(implicit ctx: Context) = if (optBounds eq this.optBounds) this else WildcardType(optBounds.asInstanceOf[TypeBounds]) override def computeHash = doHash(optBounds) diff --git a/src/dotty/tools/dotc/core/pickling/UnPickler.scala b/src/dotty/tools/dotc/core/pickling/UnPickler.scala index e2f1af5f9..f0dcb7336 100644 --- a/src/dotty/tools/dotc/core/pickling/UnPickler.scala +++ b/src/dotty/tools/dotc/core/pickling/UnPickler.scala @@ -592,7 +592,7 @@ class UnPickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClassRoot: val pre = readTypeRef() val sym = readDisambiguatedSymbolRef(_.info.isParameterless) if (isLocal(sym) || (pre == NoPrefix)) TermRef.withSym(pre, sym.asTerm) - else TermRef.withSig(pre, sym.name.asTermName, NotAMethod) + else TermRef.withSig(pre, sym.name.asTermName, NotAMethod) // !!! should become redundant case SUPERtpe => val thistpe = readTypeRef() val supertpe = readTypeRef() |