From 84bf5902dba61c88f1b50229bb3afa5a335ded94 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 5 Jun 2015 15:29:15 +0200 Subject: Move deskolemization from TypeComparer to TypeOps It's no longer needed in TypeComparer. We now deskolemize when locally inferring types of vals and defs. --- src/dotty/tools/dotc/core/TypeComparer.scala | 33 +++++----- src/dotty/tools/dotc/core/TypeOps.scala | 97 ++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 64faa6d93..1f80a8a10 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 with ConstraintHandling with Skolemization { +class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { implicit val ctx: Context = initctx val state = ctx.typerState @@ -531,19 +531,16 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling wi * rebase both itself and the member info of `tp` on a freshly created skolem type. */ protected def hasMatchingMember(name: Name, tp1: Type, tp2: RefinedType): Boolean = { - val saved = skolemsState - if (skolemsState == Skolemization.SkolemsDisallowed) skolemsState = Skolemization.SkolemsAllowed - try { - val rebindNeeded = tp2.refinementRefersToThis - val base = if (rebindNeeded) ensureStableSingleton(tp1) else tp1 - val rinfo2 = if (rebindNeeded) tp2.refinedInfo.substRefinedThis(tp2, base) else tp2.refinedInfo - 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 - } - /*>|>*/ ctx.traceIndented(i"hasMatchingMember($base . $name :? ${tp2.refinedInfo}) ${base.member(name).info.show} $rinfo2", subtyping) /*<|<*/ { - memberMatches(base member name) || + val rebindNeeded = tp2.refinementRefersToThis + val base = if (rebindNeeded) ensureStableSingleton(tp1) else tp1 + val rinfo2 = if (rebindNeeded) tp2.refinedInfo.substRefinedThis(tp2, base) else tp2.refinedInfo + 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 + } + /*>|>*/ ctx.traceIndented(i"hasMatchingMember($base . $name :? ${tp2.refinedInfo}) ${base.member(name).info.show} $rinfo2", subtyping) /*<|<*/ { + memberMatches(base member name) || tp1.isInstanceOf[SingletonType] && { // special case for situations like: // class C { type T } @@ -554,9 +551,13 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling wi case _ => false } } - } } - finally skolemsState = saved + } + + final def ensureStableSingleton(tp: Type): SingletonType = tp.stripTypeVar match { + case tp: SingletonType if tp.isStable => tp + case tp: ValueType => SkolemType(tp) + case tp: TypeProxy => ensureStableSingleton(tp.underlying) } /** Skip refinements in `tp2` which match corresponding refinements in `tp1`. diff --git a/src/dotty/tools/dotc/core/TypeOps.scala b/src/dotty/tools/dotc/core/TypeOps.scala index c4673d569..fb641f247 100644 --- a/src/dotty/tools/dotc/core/TypeOps.scala +++ b/src/dotty/tools/dotc/core/TypeOps.scala @@ -76,6 +76,103 @@ trait TypeOps { this: Context => // TODO: Make standalone object. var unstable = false } + /** Approximate a type `tp` with a type that does not contain skolem types. + */ + final def deskolemize(tp: Type): Type = deskolemize(tp, 1, Set()) + + private def deskolemize(tp: Type, variance: Int, seen: Set[SkolemType]): Type = { + def approx(lo: Type = defn.NothingType, hi: Type = defn.AnyType, newSeen: Set[SkolemType] = seen) = + if (variance == 0) NoType + else deskolemize(if (variance < 0) lo else hi, variance, newSeen) + tp match { + case tp: SkolemType => + if (seen contains tp) NoType + else approx(hi = tp.info, newSeen = seen + tp) + case tp: NamedType => + val sym = tp.symbol + if (sym.isStatic) tp + else { + val pre1 = deskolemize(tp.prefix, variance, seen) + if (pre1 eq tp.prefix) tp + else { + val d = tp.prefix.member(tp.name) + d.info match { + case TypeAlias(alias) => deskolemize(alias, variance, seen) + case _ => + 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) + } + } + + object deskolemizeMap extends TypeMap { + private var seen: Set[SkolemType] = _ + def apply(tp: Type) = deskolemize(tp, variance, seen) + def mapOver(tp: Type, variance: Int, seen: Set[SkolemType]) = { + 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 + } + } + } + /** Implementation of Types#simplified */ final def simplify(tp: Type, theMap: SimplifyMap): Type = tp match { case tp: NamedType => -- cgit v1.2.3