From d30f441ae986c144e739223be97b906b3bbd43dc Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 29 Jun 2016 19:01:12 +0200 Subject: Allow general recursion in refined types. Treat parent like refinedInfo. Introduce isBinding convenience method in TypeBounds. --- src/dotty/tools/dotc/core/TypeApplications.scala | 22 +++++------ src/dotty/tools/dotc/core/TypeComparer.scala | 5 +-- src/dotty/tools/dotc/core/TypeErasure.scala | 2 +- src/dotty/tools/dotc/core/TypeOps.scala | 4 +- src/dotty/tools/dotc/core/Types.scala | 43 ++++++++++++++++------ src/dotty/tools/dotc/core/tasty/TreePickler.scala | 2 +- .../dotc/core/unpickleScala2/Scala2Unpickler.scala | 4 +- src/dotty/tools/dotc/printing/PlainPrinter.scala | 2 +- src/dotty/tools/dotc/printing/RefinedPrinter.scala | 2 +- src/dotty/tools/dotc/typer/Applications.scala | 2 +- src/dotty/tools/dotc/typer/Checking.scala | 4 +- src/dotty/tools/dotc/typer/TypeAssigner.scala | 4 +- src/dotty/tools/dotc/typer/Variances.scala | 4 +- 13 files changed, 59 insertions(+), 41 deletions(-) (limited to 'src/dotty') diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index d73181fcb..8ab5fbf02 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -70,19 +70,19 @@ object TypeApplications { } def unapply(tp: Type)(implicit ctx: Context): Option[(List[Int], List[TypeBounds], Type)] = tp match { - case app @ RefinedType(parent, tpnme.hkApply) => + case app @ RefinedType(parent, tpnme.hkApply, refinedInfo) => val cls = parent.typeSymbol val variances = cls.typeParams.map(_.variance) def collectBounds(t: Type, acc: List[TypeBounds]): List[TypeBounds] = t match { - case t @ RefinedType(p, rname) => + case t @ RefinedType(p, rname, rinfo) => assert(rname.isHkArgName) - collectBounds(p, t.refinedInfo.bounds :: acc) + collectBounds(p, rinfo.bounds :: acc) case TypeRef(_, lname) => assert(lname.isLambdaTraitName) acc } val argBounds = collectBounds(parent, Nil) - Some((variances, argBounds, app.refinedInfo.argInfo)) + Some((variances, argBounds, refinedInfo.argInfo)) case _ => None } @@ -153,9 +153,9 @@ object TypeApplications { def stripArgs(tp: Type, n: Int): Type = if (n == 0) tp else tp match { - case tp @ RefinedType(parent, pname) if pname == tparams(n - 1).name => + case tp @ RefinedType(parent, pname, rinfo) if pname == tparams(n - 1).name => val res = stripArgs(parent, n - 1) - if (res.exists) argBuf += tp.refinedInfo.argInfo + if (res.exists) argBuf += rinfo.argInfo res case _ => NoType @@ -335,7 +335,7 @@ class TypeApplications(val self: Type) extends AnyVal { /** The Lambda trait underlying a type lambda */ def LambdaTrait(implicit ctx: Context): Symbol = self.stripTypeVar match { - case RefinedType(parent, tpnme.hkApply) => + case RefinedType(_, tpnme.hkApply, _) => val sym = self.classSymbol if (sym.isLambdaTrait) sym else NoSymbol case TypeBounds(lo, hi) => hi.LambdaTrait @@ -345,7 +345,7 @@ class TypeApplications(val self: Type) extends AnyVal { /** Is receiver type higher-kinded (i.e. of kind != "*")? */ def isHK(implicit ctx: Context): Boolean = self.dealias match { case self: TypeRef => self.info.isHK - case RefinedType(_, name) => name == tpnme.hkApply + case RefinedType(_, tpnme.hkApply, _) => true case TypeBounds(_, hi) => hi.isHK case _ => false } @@ -580,7 +580,7 @@ class TypeApplications(val self: Type) extends AnyVal { } assert(args.nonEmpty) matchParams(self, typParams, args) match { - case refined @ RefinedType(_, pname) if pname.isHkArgName => + case refined @ RefinedType(_, pname, _) if pname.isHkArgName => TypeRef(refined, tpnme.hkApply) case refined => refined @@ -671,7 +671,7 @@ class TypeApplications(val self: Type) extends AnyVal { case TypeBounds(_, hi) => hi.baseTypeWithArgs(base) case _ => default } - case tp @ RefinedType(parent, name) if !tp.member(name).symbol.is(ExpandedTypeParam) => + case tp @ RefinedType(parent, name, _) if !tp.member(name).symbol.is(ExpandedTypeParam) => tp.wrapIfMember(parent.baseTypeWithArgs(base)) case tp: TermRef => tp.underlying.baseTypeWithArgs(base) @@ -731,7 +731,7 @@ class TypeApplications(val self: Type) extends AnyVal { */ final def withoutArgs(typeArgs: List[Type]): Type = typeArgs match { case _ :: typeArgs1 => - val RefinedType(tycon, _) = self + val RefinedType(tycon, _, _) = self tycon.withoutArgs(typeArgs1) case nil => self diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 2523c6b9a..d1dc4069d 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -465,7 +465,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case _ => def isNullable(tp: Type): Boolean = tp.dealias match { case tp: TypeRef => tp.symbol.isNullableClass - case RefinedType(parent, _) => isNullable(parent) + case tp: RefinedType => isNullable(tp.parent) case AndType(tp1, tp2) => isNullable(tp1) && isNullable(tp2) case OrType(tp1, tp2) => isNullable(tp1) || isNullable(tp2) case _ => false @@ -738,9 +738,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { * @return The parent type of `tp2` after skipping the matching refinements. */ private def skipMatching(tp1: Type, tp2: RefinedType): Type = tp1 match { - case tp1 @ RefinedType(parent1, name1) + case tp1 @ RefinedType(parent1, name1, rinfo1: TypeAlias) if name1 == tp2.refinedName && - tp1.refinedInfo.isInstanceOf[TypeAlias] && !tp2.refinementRefersToThis && !tp1.refinementRefersToThis => tp2.parent match { diff --git a/src/dotty/tools/dotc/core/TypeErasure.scala b/src/dotty/tools/dotc/core/TypeErasure.scala index 39d02e069..a5aabe9c4 100644 --- a/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/src/dotty/tools/dotc/core/TypeErasure.scala @@ -430,7 +430,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean // constructor method should not be semi-erased. else if (isConstructor && isDerivedValueClass(sym)) eraseNormalClassRef(tp) else this(tp) - case RefinedType(parent, _) if !(parent isRef defn.ArrayClass) => + case RefinedType(parent, _, _) if !(parent isRef defn.ArrayClass) => eraseResult(parent) case _ => this(tp) diff --git a/src/dotty/tools/dotc/core/TypeOps.scala b/src/dotty/tools/dotc/core/TypeOps.scala index 1288c0b23..72b0c87c4 100644 --- a/src/dotty/tools/dotc/core/TypeOps.scala +++ b/src/dotty/tools/dotc/core/TypeOps.scala @@ -364,8 +364,8 @@ trait TypeOps { this: Context => // TODO: Make standalone object. def normalizeToRef(tp: Type): TypeRef = tp.dealias match { case tp: TypeRef => tp - case tp @ RefinedType(tp1, name: TypeName) => - tp.refinedInfo match { + case tp @ RefinedType(tp1, name: TypeName, rinfo) => + rinfo match { case TypeAlias(TypeRef(pre, name1)) if name1 == name && (pre =:= cls.thisType) => // Don't record refinements of the form X = this.X (These can arise using named parameters). typr.println(s"dropping refinement $tp") diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 632ab823a..b26fd6373 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -1035,7 +1035,7 @@ object Types { /** the self type of the underlying classtype */ def givenSelfType(implicit ctx: Context): Type = this match { - case tp @ RefinedType(parent, name) => tp.wrapIfMember(parent.givenSelfType) + case tp: RefinedType => tp.wrapIfMember(tp.parent.givenSelfType) case tp: ThisType => tp.tref.givenSelfType case tp: TypeProxy => tp.underlying.givenSelfType case _ => NoType @@ -2029,10 +2029,11 @@ object Types { * @param infoFn: A function that produces the info of the refinement declaration, * given the refined type itself. */ - abstract case class RefinedType(parent: Type, refinedName: Name) + abstract case class RefinedType(private var myParent: Type, refinedName: Name, private var myRefinedInfo: Type) extends CachedProxyType with BindingType with ValueType { - val refinedInfo: Type + final def parent = myParent + final def refinedInfo = myRefinedInfo private var refinementRefersToThisCache: Boolean = _ private var refinementRefersToThisKnown: Boolean = false @@ -2053,7 +2054,7 @@ object Types { def checkInst(implicit ctx: Context): this.type = { if (refinedName == tpnme.hkApply) parent.stripTypeVar match { - case RefinedType(_, name) if name.isHkArgName => // ok + case RefinedType(_, name, _) if name.isHkArgName => // ok case _ => badInst } this @@ -2076,16 +2077,18 @@ object Types { case _ => false } - override def computeHash = doHash(refinedName, refinedInfo, parent) + override def computeHash = { + assert(parent.exists) + doHash(refinedName, refinedInfo, parent) + } + override def toString = s"RefinedType($parent, $refinedName, $refinedInfo | $hashCode)" } - class CachedRefinedType(parent: Type, refinedName: Name, infoFn: RefinedType => Type) extends RefinedType(parent, refinedName) { - val refinedInfo = infoFn(this) - } + class CachedRefinedType(refinedName: Name) extends RefinedType(NoType, refinedName, NoType) - class PreHashedRefinedType(parent: Type, refinedName: Name, override val refinedInfo: Type, hc: Int) - extends RefinedType(parent, refinedName) { + class PreHashedRefinedType(parent: Type, refinedName: Name, refinedInfo: Type, hc: Int) + extends RefinedType(parent, refinedName, refinedInfo) { myHash = hc override def computeHash = unsupported("computeHash") } @@ -2095,9 +2098,22 @@ object Types { if (names.isEmpty) parent else make(RefinedType(parent, names.head, infoFns.head), names.tail, infoFns.tail) + def recursive(parentFn: RefinedType => Type, names: List[Name], infoFns: List[RefinedType => Type])(implicit ctx: Context): RefinedType = { + val refinements: List[RefinedType] = names.map(new CachedRefinedType(_)) + val last = refinements.last + (refinements, infoFns).zipped.foreach((rt, infoFn) => rt.myRefinedInfo = infoFn(last)) + (parentFn(last) /: refinements) { (parent, rt) => + rt.myParent = parent + ctx.base.uniqueRefinedTypes.enterIfNew(rt).checkInst + }.asInstanceOf[RefinedType] + } + def apply(parent: Type, name: Name, infoFn: RefinedType => Type)(implicit ctx: Context): RefinedType = { assert(!ctx.erasedTypes || ctx.mode.is(Mode.Printing)) - ctx.base.uniqueRefinedTypes.enterIfNew(new CachedRefinedType(parent, name, infoFn)).checkInst + val res: RefinedType = new CachedRefinedType(name) + res.myParent = parent + res.myRefinedInfo = infoFn(res) + ctx.base.uniqueRefinedTypes.enterIfNew(res).checkInst } def apply(parent: Type, name: Name, info: Type)(implicit ctx: Context): RefinedType = { @@ -2668,8 +2684,8 @@ object Types { } def isOrType(tp: Type): Boolean = tp.stripTypeVar.dealias match { case tp: OrType => true + case tp: RefinedType => isOrType(tp.parent) case AndType(tp1, tp2) => isOrType(tp1) | isOrType(tp2) - case RefinedType(parent, _) => isOrType(parent) case WildcardType(bounds: TypeBounds) => isOrType(bounds.hi) case _ => false } @@ -2860,6 +2876,7 @@ object Types { assert(hi.isInstanceOf[TermType]) def variance: Int = 0 + def isBinding = bindingKind != NoBinding override def underlying(implicit ctx: Context): Type = hi @@ -2876,6 +2893,8 @@ object Types { case _ => this } + def withBindingKind(bk: BindingKind)(implicit ctx: Context) = derivedTypeBounds(lo, hi, bk) + def contains(tp: Type)(implicit ctx: Context): Boolean = tp match { case tp: TypeBounds => lo <:< tp.lo && tp.hi <:< hi case tp: ClassInfo => diff --git a/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 4cfd7727c..0cc08f2d9 100644 --- a/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -236,7 +236,7 @@ class TreePickler(pickler: TastyPickler) { withLength { pickleType(tpe.lo, richTypes) pickleType(tpe.hi, richTypes) - if (tpe.bindingKind != NoBinding) writeNat(tpe.bindingKind.n) + if (tpe.isBinding) writeNat(tpe.bindingKind.n) } case tpe: AnnotatedType => writeByte(ANNOTATED) diff --git a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index 71a919ca3..687e9279b 100644 --- a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -620,9 +620,9 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas def removeSingleton(tp: Type): Type = if (tp isRef defn.SingletonClass) defn.AnyType else tp def elim(tp: Type): Type = tp match { - case tp @ RefinedType(parent, name) => + case tp @ RefinedType(parent, name, rinfo) => val parent1 = elim(tp.parent) - tp.refinedInfo match { + rinfo match { case TypeAlias(info: TypeRef) if isBound(info) => RefinedType(parent1, name, info.symbol.info) case info: TypeRef if isBound(info) => diff --git a/src/dotty/tools/dotc/printing/PlainPrinter.scala b/src/dotty/tools/dotc/printing/PlainPrinter.scala index 1e2ba0b4d..bac180efe 100644 --- a/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -110,7 +110,7 @@ class PlainPrinter(_ctx: Context) extends Printer { */ private def refinementChain(tp: Type): List[Type] = tp :: (tp match { - case RefinedType(parent, _) => refinementChain(parent.stripTypeVar) + case tp: RefinedType => refinementChain(tp.parent.stripTypeVar) case _ => Nil }) diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 614a274b4..e0fd47900 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -123,7 +123,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { def contains(tp1: Type, tp2: Type): Boolean = tp1.eq(tp2) || { tp1.stripTypeVar match { - case RefinedType(parent, _) => contains(parent, tp2) + case tp1: RefinedType => contains(tp1.parent, tp2) case _ => false } } diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index a9184c7e5..14071e27c 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -741,7 +741,7 @@ trait Applications extends Compatibility { self: Typer => def isSubTypeOfParent(subtp: Type, tp: Type)(implicit ctx: Context): Boolean = if (subtp <:< tp) true else tp match { - case RefinedType(parent, _) => isSubTypeOfParent(subtp, parent) + case tp: RefinedType => isSubTypeOfParent(subtp, tp.parent) case _ => false } diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index 22d2407bc..37753fe65 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -172,8 +172,8 @@ object Checking { case tp: TermRef => this(tp.info) mapOver(tp) - case tp @ RefinedType(parent, name) => - tp.derivedRefinedType(this(parent), name, this(tp.refinedInfo, nestedCycleOK, nestedCycleOK)) + case tp @ RefinedType(parent, name, rinfo) => + tp.derivedRefinedType(this(parent), name, this(rinfo, nestedCycleOK, nestedCycleOK)) case tp @ TypeRef(pre, name) => try { // A prefix is interesting if it might contain (transitively) a reference diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index 995fa43ca..f439c4c99 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -98,9 +98,9 @@ trait TypeAssigner { val base = apply(tycon) val args = tp.baseArgInfos(base.typeSymbol) if (base.typeParams.length == args.length) base.appliedTo(args) else base - case tp @ RefinedType(parent, name) if variance > 0 => + case tp @ RefinedType(parent, name, rinfo) if variance > 0 => val parent1 = apply(tp.parent) - val refinedInfo1 = apply(tp.refinedInfo) + val refinedInfo1 = apply(rinfo) if (toAvoid(refinedInfo1)) { typr.println(s"dropping refinement from $tp") parent1 diff --git a/src/dotty/tools/dotc/typer/Variances.scala b/src/dotty/tools/dotc/typer/Variances.scala index 55e6b5232..bc9730140 100644 --- a/src/dotty/tools/dotc/typer/Variances.scala +++ b/src/dotty/tools/dotc/typer/Variances.scala @@ -75,8 +75,8 @@ object Variances { case tp @ TypeBounds(lo, hi) => if (lo eq hi) compose(varianceInType(hi)(tparam), tp.variance) else flip(varianceInType(lo)(tparam)) & varianceInType(hi)(tparam) - case tp @ RefinedType(parent, _) => - varianceInType(parent)(tparam) & varianceInType(tp.refinedInfo)(tparam) + case tp @ RefinedType(parent, _, rinfo) => + varianceInType(parent)(tparam) & varianceInType(rinfo)(tparam) case tp @ MethodType(_, paramTypes) => flip(varianceInTypes(paramTypes)(tparam)) & varianceInType(tp.resultType)(tparam) case ExprType(restpe) => -- cgit v1.2.3