diff options
author | Martin Odersky <odersky@gmail.com> | 2015-01-08 17:52:00 +0100 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2015-01-08 17:52:00 +0100 |
commit | 9f745338242524c6607fa7b2930157b0c71be939 (patch) | |
tree | 47538e766d412760b2298eaf16ab56589eb772be /src/dotty/tools | |
parent | 0bc9f680af7670985ff10b1e5fa472655745c90b (diff) | |
download | dotty-9f745338242524c6607fa7b2930157b0c71be939.tar.gz dotty-9f745338242524c6607fa7b2930157b0c71be939.tar.bz2 dotty-9f745338242524c6607fa7b2930157b0c71be939.zip |
Refacttored skolemization logic
... into a new trait "Skolemization".
Diffstat (limited to 'src/dotty/tools')
-rw-r--r-- | src/dotty/tools/dotc/core/NameOps.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Skolemization.scala | 122 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/TypeComparer.scala | 51 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/TypeOps.scala | 102 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Types.scala | 6 |
5 files changed, 150 insertions, 133 deletions
diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala index 240447e6d..c15c4c4c2 100644 --- a/src/dotty/tools/dotc/core/NameOps.scala +++ b/src/dotty/tools/dotc/core/NameOps.scala @@ -355,5 +355,5 @@ object NameOps { case NO_NAME => primitivePostfixMethodName case name => name } - } + } }
\ No newline at end of file diff --git a/src/dotty/tools/dotc/core/Skolemization.scala b/src/dotty/tools/dotc/core/Skolemization.scala new file mode 100644 index 000000000..592ec860b --- /dev/null +++ b/src/dotty/tools/dotc/core/Skolemization.scala @@ -0,0 +1,122 @@ +package dotty.tools.dotc +package core + +import Symbols._, Types._, Contexts._, Decorators._, NameOps._, StdNames._ +import collection.mutable + +trait Skolemization { + + protected var skolemsOutstanding = false + + def ensureSingleton(tp: Type)(implicit ctx: Context): SingletonType = tp.stripTypeVar match { + case tp: SingletonType => + tp + case tp: ValueType => + skolemsOutstanding = true + tp.narrow + case tp: TypeProxy => + ensureSingleton(tp.underlying) + } + + /** Approximate a type `tp` with a type that does not contain skolem termrefs. + * @param toSuper if true, return the smallest supertype of `tp` with this property + * e;se return the largest subtype. + */ + final def deSkolemize(tp: Type, toSuper: Boolean)(implicit ctx: Context): Type = + if (skolemsOutstanding) deSkolemize(tp, if (toSuper) 1 else -1, Set()) else tp + + private def deSkolemize(tp: Type, variance: Int, seen: Set[Symbol])(implicit ctx: Context): Type = + ctx.traceIndented(s"deskolemize $tp, variance = $variance, seen = $seen = ") { + def approx(lo: Type = defn.NothingType, hi: Type = defn.AnyType, newSeen: Set[Symbol] = seen) = + if (variance == 0) NoType + else deSkolemize(if (variance < 0) lo else hi, variance, newSeen) + tp match { + case tp: NamedType => + val sym = tp.symbol + if (sym.isSkolem) + if (seen contains sym) NoType + else approx(hi = sym.info, newSeen = seen + sym) + else if (sym.isStatic) tp + else { + val pre1 = deSkolemize(tp.prefix, variance, seen) + if (pre1.exists && !pre1.isRef(defn.NothingClass)) tp.derivedSelect(pre1) + else { + ctx.log(s"deskolem: $tp: ${tp.info}") + tp.info match { + case TypeBounds(lo, hi) => approx(lo, hi) + case info => approx(defn.NothingType, info) + } + } + } + case _: ThisType | _: BoundType | _: SuperType | NoType | NoPrefix => + tp + case tp: RefinedType => + val parent1 = deSkolemize(tp.parent, variance, seen) + if (parent1.exists) { + val refinedInfo1 = deSkolemize(tp.refinedInfo, variance, seen) + if (refinedInfo1.exists) + tp.derivedRefinedType(parent1, tp.refinedName, refinedInfo1) + else + approx(hi = parent1) + } + else approx() + case tp: TypeAlias => + val alias1 = deSkolemize(tp.alias, variance * tp.variance, seen) + if (alias1.exists) tp.derivedTypeAlias(alias1) + else approx(hi = TypeBounds.empty) + case tp: TypeBounds => + val lo1 = deSkolemize(tp.lo, -variance, seen) + val hi1 = deSkolemize(tp.hi, variance, seen) + if (lo1.exists && hi1.exists) tp.derivedTypeBounds(lo1, hi1) + else approx(hi = + if (lo1.exists) TypeBounds.lower(lo1) + else if (hi1.exists) TypeBounds.upper(hi1) + else TypeBounds.empty) + case tp: ClassInfo => + val pre1 = deSkolemize(tp.prefix, variance, seen) + if (pre1.exists) tp.derivedClassInfo(pre1) + else NoType + case tp: AndOrType => + val tp1d = deSkolemize(tp.tp1, variance, seen) + val tp2d = deSkolemize(tp.tp2, variance, seen) + if (tp1d.exists && tp2d.exists) + tp.derivedAndOrType(tp1d, tp2d) + else if (tp.isAnd) + approx(hi = tp1d & tp2d) // if one of tp1d, tp2d exists, it is the result of tp1d & tp2d + else + approx(lo = tp1d & tp2d) + case tp: WildcardType => + val bounds1 = deSkolemize(tp.optBounds, variance, seen) + if (bounds1.exists) tp.derivedWildcardType(bounds1) + else WildcardType + case _ => + if (tp.isInstanceOf[MethodicType]) assert(variance != 0, tp) + deSkolemizeMap.mapOver(tp, variance, seen) + } + } + + private var deSkolemizeMapCache: DeSkolemizeMap = null + + private def deSkolemizeMap(implicit ctx: Context) = { + if (deSkolemizeMapCache == null || deSkolemizeMapCache.definingCtx != ctx) + deSkolemizeMapCache = new DeSkolemizeMap + deSkolemizeMapCache + } + + private class DeSkolemizeMap(implicit ctx: Context) extends TypeMap { + def definingCtx = ctx + private var seen: Set[Symbol] = _ + def apply(tp: Type) = deSkolemize(tp, variance, seen) + def mapOver(tp: Type, variance: Int, seen: Set[Symbol]) = { + val savedVariance = this.variance + val savedSeen = this.seen + this.variance = variance + this.seen = seen + try super.mapOver(tp) + finally { + this.variance = savedVariance + this.seen = savedSeen + } + } + } +}
\ No newline at end of file diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 98a0d665c..8ce1ad354 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -16,7 +16,7 @@ import scala.util.control.NonFatal /** Provides methods to compare types. */ -class TypeComparer(initctx: Context) extends DotClass { +class TypeComparer(initctx: Context) extends DotClass with Skolemization { implicit val ctx: Context = initctx val state = ctx.typerState @@ -267,8 +267,7 @@ class TypeComparer(initctx: Context) extends DotClass { } } - val bound = (if (false) ctx.deSkolemize(bound0, toSuper = fromBelow) else bound0) - .dealias.stripTypeVar + val bound = deSkolemize(bound0, toSuper = fromBelow).dealias.stripTypeVar def description = s"${param.show} ${if (fromBelow) ">:>" else "<:<"} ${bound.show} to ${constraint.show}" constr.println(s"adding $description") val res = addBi(param, bound, fromBelow) @@ -794,24 +793,28 @@ class TypeComparer(initctx: Context) extends DotClass { ctdSubType(orig1, orig2) } - def hasMatchingMember(name: Name, orig1: Type, tp2: RefinedType): Boolean = /*>|>*/ ctx.traceIndented(s"hasMatchingMember($orig1 . $name, ${tp2.refinedInfo}) ${orig1.member(name).info.show}", subtyping) /*<|<*/ { - val base = orig1.ensureSingleton - var rinfo2 = tp2.refinedInfo.substRefinedThis(0, base) - def qualifies(m: SingleDenotation) = isSubType(m.info, rinfo2) - def memberMatches(mbr: Denotation): Boolean = mbr match { // inlined hasAltWith for performance - case mbr: SingleDenotation => qualifies(mbr) - case _ => mbr hasAltWith qualifies - } - memberMatches(base member name) || - orig1.isInstanceOf[SingletonType] && - { // special case for situations like: - // foo <: C { type T = foo.T } - rinfo2 match { - case rinfo2: TypeAlias => - !ctx.phase.erasedTypes && (base select name) =:= rinfo2.alias - case _ => false - } + def hasMatchingMember(name: Name, tp1: Type, tp2: RefinedType): Boolean = /*>|>*/ ctx.traceIndented(s"hasMatchingMember($tp1 . $name, ${tp2.refinedInfo}) ${tp1.member(name).info.show}", subtyping) /*<|<*/ { + val saved = skolemsOutstanding + try { + val base = ensureSingleton(tp1) + var rinfo2 = tp2.refinedInfo.substRefinedThis(0, base) + def qualifies(m: SingleDenotation) = isSubType(m.info, rinfo2) + def memberMatches(mbr: Denotation): Boolean = mbr match { // inlined hasAltWith for performance + case mbr: SingleDenotation => qualifies(mbr) + case _ => mbr hasAltWith qualifies } + memberMatches(base member name) || + tp1.isInstanceOf[SingletonType] && + { // special case for situations like: + // foo <: C { type T = foo.T } + rinfo2 match { + case rinfo2: TypeAlias => + !ctx.phase.erasedTypes && (base select name) =:= rinfo2.alias + case _ => false + } + } + } + finally skolemsOutstanding = saved } /** A type has been covered previously in subtype checking if it @@ -864,7 +867,7 @@ class TypeComparer(initctx: Context) extends DotClass { def narrowGADTBounds(tr: NamedType, bound: Type, fromBelow: Boolean): Boolean = ctx.mode.is(Mode.GADTflexible) && { val tparam = tr.symbol - val bound1 = bound // ctx.deSkolemize(bound, toSuper = fromBelow) + val bound1 = deSkolemize(bound, toSuper = fromBelow) println(s"narrow gadt bound of $tparam: ${tparam.info} from ${if (fromBelow) "below" else "above"} to $bound1 ${bound1.isRef(tparam)}") !bound1.isRef(tparam) && { val oldBounds = ctx.gadt.bounds(tparam) @@ -1419,9 +1422,9 @@ class ExplainingTypeComparer(initctx: Context) extends TypeComparer(initctx) { super.isSubType(tp1, tp2) } - override def hasMatchingMember(name: Name, orig1: Type, tp2: RefinedType): Boolean = - traceIndented(s"hasMatchingMember(${show(orig1)} . $name, ${show(tp2.refinedInfo)}), member = ${show(orig1.member(name).info)}") { - super.hasMatchingMember(name, orig1, tp2) + override def hasMatchingMember(name: Name, tp1: Type, tp2: RefinedType): Boolean = + traceIndented(s"hasMatchingMember(${show(tp1)} . $name, ${show(tp2.refinedInfo)}), member = ${show(tp1.member(name).info)}") { + super.hasMatchingMember(name, tp1, tp2) } override def lub(tp1: Type, tp2: Type) = diff --git a/src/dotty/tools/dotc/core/TypeOps.scala b/src/dotty/tools/dotc/core/TypeOps.scala index fb6ef6c09..6fc7314c2 100644 --- a/src/dotty/tools/dotc/core/TypeOps.scala +++ b/src/dotty/tools/dotc/core/TypeOps.scala @@ -87,108 +87,6 @@ trait TypeOps { this: Context => def apply(tp: Type) = simplify(tp, this) } - /** Approximate a type `tp` with a type that does not contain skolem termrefs. - * @param toSuper if true, return the smallest supertype of `tp` with this property - * e;se return the largest subtype. - */ - final def deSkolemize(tp: Type, toSuper: Boolean): Type = - deSkolemize(tp, if (toSuper) 1 else -1, Set()) - - private def deSkolemize(tp: Type, variance: Int, seen: Set[Symbol]): Type = - traceIndented(s"deskolemize $tp, variance = $variance, seen = $seen = ") { - def approx(lo: Type = defn.NothingType, hi: Type = defn.AnyType, newSeen: Set[Symbol] = seen) = - if (variance == 0) NoType - else deSkolemize(if (variance < 0) lo else hi, variance, newSeen) - tp match { - case tp: NamedType => - val sym = tp.symbol - if (sym.isSkolem) - if (seen contains sym) NoType - else approx(hi = sym.info, newSeen = seen + sym) - else if (sym.isStatic) tp - else { - val pre1 = deSkolemize(tp.prefix, variance, seen) - if (pre1.exists && !pre1.isRef(defn.NothingClass)) tp.derivedSelect(pre1) - else { - ctx.log(s"deskolem: $tp: ${tp.info}") - tp.info match { - case TypeBounds(lo, hi) => approx(lo, hi) - case info => approx(defn.NothingType, info) - } - } - } - case _: ThisType | _: BoundType | _: SuperType | NoType | NoPrefix => - tp - case tp: RefinedType => - val parent1 = deSkolemize(tp.parent, variance, seen) - if (parent1.exists) { - val refinedInfo1 = deSkolemize(tp.refinedInfo, variance, seen) - if (refinedInfo1.exists) - tp.derivedRefinedType(parent1, tp.refinedName, refinedInfo1) - else - approx(hi = parent1) - } - else approx() - case tp: TypeAlias => - val alias1 = deSkolemize(tp.alias, variance * tp.variance, seen) - if (alias1.exists) tp.derivedTypeAlias(alias1) - else approx(hi = TypeBounds.empty) - case tp: TypeBounds => - val lo1 = deSkolemize(tp.lo, -variance, seen) - val hi1 = deSkolemize(tp.hi, variance, seen) - if (lo1.exists && hi1.exists) tp.derivedTypeBounds(lo1, hi1) - else approx(hi = - if (lo1.exists) TypeBounds.lower(lo1) - else if (hi1.exists) TypeBounds.upper(hi1) - else TypeBounds.empty) - case tp: ClassInfo => - val pre1 = deSkolemize(tp.prefix, variance, seen) - if (pre1.exists) tp.derivedClassInfo(pre1) - else NoType - case tp: AndOrType => - val tp1d = deSkolemize(tp.tp1, variance, seen) - val tp2d = deSkolemize(tp.tp2, variance, seen) - if (tp1d.exists && tp2d.exists) - tp.derivedAndOrType(tp1d, tp2d) - else if (tp.isAnd) - approx(hi = tp1d & tp2d) // if one of tp1d, tp2d exists, it is the result of tp1d & tp2d - else - approx(lo = tp1d & tp2d) - case tp: WildcardType => - val bounds1 = deSkolemize(tp.optBounds, variance, seen) - if (bounds1.exists) tp.derivedWildcardType(bounds1) - else WildcardType - case _ => - if (tp.isInstanceOf[MethodicType]) assert(variance != 0, tp) - deSkolemizeMap.mapOver(tp, variance, seen) - } - } - - private var deSkolemizeMapCache: DeSkolemizeMap = null - - private def deSkolemizeMap = { - if (deSkolemizeMapCache == null || deSkolemizeMapCache.definingCtx != this) - deSkolemizeMapCache = new DeSkolemizeMap - deSkolemizeMapCache - } - - private class DeSkolemizeMap(implicit ctx: Context) extends TypeMap { - def definingCtx = ctx - private var seen: Set[Symbol] = _ - def apply(tp: Type) = deSkolemize(tp, variance, seen) - def mapOver(tp: Type, variance: Int, seen: Set[Symbol]) = { - val savedVariance = this.variance - val savedSeen = this.seen - this.variance = variance - this.seen = seen - try super.mapOver(tp) - finally { - this.variance = savedVariance - this.seen = savedSeen - } - } - } - /** Approximate union type by intersection of its dominators. * See Type#approximateUnion for an explanation. */ diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index c6913eb0f..920c9c6f8 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -755,12 +755,6 @@ object Types { def narrow(implicit ctx: Context): TermRef = TermRef(NoPrefix, ctx.newSkolem(this)) - def ensureSingleton(implicit ctx: Context): SingletonType = stripTypeVar match { - case tp: SingletonType => tp - case tp: ValueType => narrow - case tp: TypeProxy => tp.underlying.ensureSingleton - } - // ----- Normalizing typerefs over refined types ---------------------------- /** If this is a refinement type that has a refinement for `name` (which might be followed |