From 468b49972df2eb25cf17be3019d08b25d4057507 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 24 May 2016 15:57:44 +0200 Subject: [PATCH 001/142] Record binding kind in TypeBounds Will be used to encode higher-kinded type parameters. --- src/dotty/tools/dotc/core/Types.scala | 52 ++++++++++++++----- .../tools/dotc/core/tasty/TastyFormat.scala | 3 +- .../tools/dotc/core/tasty/TreePickler.scala | 6 ++- .../tools/dotc/core/tasty/TreeUnpickler.scala | 5 +- 4 files changed, 49 insertions(+), 17 deletions(-) diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index f514a329e1b9..632ab823ac1a 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -2849,8 +2849,12 @@ object Types { unique(new CachedClassInfo(prefix, cls, classParents, decls, selfInfo)) } - /** Type bounds >: lo <: hi */ - abstract case class TypeBounds(lo: Type, hi: Type) extends CachedProxyType with TypeType { + /** Type bounds >: lo <: hi + * @param bindingKind: If != NoBinding, it indicates that this is + * an introduction of a higher-kinded type parameter. + * In that case it also defines the variance of the parameter. + */ + abstract case class TypeBounds(lo: Type, hi: Type)(val bindingKind: BindingKind) extends CachedProxyType with TypeType { assert(lo.isInstanceOf[TermType]) assert(hi.isInstanceOf[TermType]) @@ -2860,9 +2864,9 @@ object Types { override def underlying(implicit ctx: Context): Type = hi /** The non-alias type bounds type with given bounds */ - def derivedTypeBounds(lo: Type, hi: Type)(implicit ctx: Context) = - if ((lo eq this.lo) && (hi eq this.hi) && (variance == 0)) this - else TypeBounds(lo, hi) + def derivedTypeBounds(lo: Type, hi: Type, bk: BindingKind = this.bindingKind)(implicit ctx: Context) = + if ((lo eq this.lo) && (hi eq this.hi) && (bk == this.bindingKind) && (variance == 0)) this + else TypeBounds(lo, hi, bk) /** If this is an alias, a derived alias with the new variance, * Otherwise the type itself. @@ -2884,12 +2888,12 @@ object Types { def & (that: TypeBounds)(implicit ctx: Context): TypeBounds = if ((this.lo frozen_<:< that.lo) && (that.hi frozen_<:< this.hi)) that else if ((that.lo frozen_<:< this.lo) && (this.hi frozen_<:< that.hi)) this - else TypeBounds(this.lo | that.lo, this.hi & that.hi) + else TypeBounds(this.lo | that.lo, this.hi & that.hi, this.bindingKind join that.bindingKind) def | (that: TypeBounds)(implicit ctx: Context): TypeBounds = if ((this.lo frozen_<:< that.lo) && (that.hi frozen_<:< this.hi)) this else if ((that.lo frozen_<:< this.lo) && (this.hi frozen_<:< that.hi)) that - else TypeBounds(this.lo & that.lo, this.hi | that.hi) + else TypeBounds(this.lo & that.lo, this.hi | that.hi, this.bindingKind join that.bindingKind) override def & (that: Type)(implicit ctx: Context) = that match { case that: TypeBounds => this & that @@ -2909,6 +2913,7 @@ object Types { /** If this type and that type have the same variance, this variance, otherwise 0 */ final def commonVariance(that: TypeBounds): Int = (this.variance + that.variance) / 2 + override def computeHash = doHash(variance, lo, hi) override def equals(that: Any): Boolean = that match { case that: TypeBounds => (this.lo eq that.lo) && (this.hi eq that.hi) && this.variance == that.variance @@ -2920,11 +2925,9 @@ object Types { if (lo eq hi) s"TypeAlias($lo, $variance)" else s"TypeBounds($lo, $hi)" } - class RealTypeBounds(lo: Type, hi: Type) extends TypeBounds(lo, hi) { - override def computeHash = doHash(variance, lo, hi) - } + class RealTypeBounds(lo: Type, hi: Type, bk: BindingKind) extends TypeBounds(lo, hi)(bk) - abstract class TypeAlias(val alias: Type, override val variance: Int) extends TypeBounds(alias, alias) { + abstract class TypeAlias(val alias: Type, override val variance: Int) extends TypeBounds(alias, alias)(NoBinding) { /** pre: this is a type alias */ def derivedTypeAlias(alias: Type, variance: Int = this.variance)(implicit ctx: Context) = if ((alias eq this.alias) && (variance == this.variance)) this @@ -2952,12 +2955,11 @@ object Types { class CachedTypeAlias(alias: Type, variance: Int, hc: Int) extends TypeAlias(alias, variance) { myHash = hc - override def computeHash = doHash(variance, lo, hi) } object TypeBounds { - def apply(lo: Type, hi: Type)(implicit ctx: Context): TypeBounds = - unique(new RealTypeBounds(lo, hi)) + def apply(lo: Type, hi: Type, bk: BindingKind = NoBinding)(implicit ctx: Context): TypeBounds = + unique(new RealTypeBounds(lo, hi, bk)) def empty(implicit ctx: Context) = apply(defn.NothingType, defn.AnyType) def upper(hi: Type)(implicit ctx: Context) = apply(defn.NothingType, hi) def lower(lo: Type)(implicit ctx: Context) = apply(lo, defn.AnyType) @@ -2969,6 +2971,28 @@ object Types { def unapply(tp: TypeAlias): Option[Type] = Some(tp.alias) } + /** A value class defining the interpretation of a TypeBounds + * as either a regular type bounds or a binding (i.e. introduction) of a + * higher-kinded type parameter. + */ + class BindingKind(val n: Byte) extends AnyVal { + def join(that: BindingKind) = + if (this == that) this + else if (this == NoBinding) that + else if (that == NoBinding) this + else NonvariantBinding + } + + val NoBinding = new BindingKind(0) // Regular type bounds + val ContravariantBinding = new BindingKind(1) // Bounds for contravariant hk type param + val NonvariantBinding = new BindingKind(2) // Bounds for nonvariant hk type param + val CovariantBinding = new BindingKind(3) // Bounds for covariant hk type param + + object BindingKind { + def fromVariance(v: Int): BindingKind = new BindingKind((v + NonvariantBinding.n).toByte) + def toVariance(bk: BindingKind): Int = bk.n + } + // ----- Annotated and Import types ----------------------------------------------- /** An annotated type tpe @ annot */ diff --git a/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index 2211706225e6..a42958e756ae 100644 --- a/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -129,7 +129,7 @@ Standard-Section: "ASTs" TopLevelStat* SUPERtype Length this_Type underlying_Type REFINEDtype Length underlying_Type refinement_NameRef info_Type APPLIEDtype Length tycon_Type arg_Type* - TYPEBOUNDS Length low_Type high_Type + TYPEBOUNDS Length low_Type high_Type bindingKind_Nat? TYPEALIAS Length alias_Type (COVARIANT | CONTRAVARIANT)? ANNOTATED Length underlying_Type fullAnnotation_Term ANDtype Length left_Type right_Type @@ -494,6 +494,7 @@ object TastyFormat { SELFDEF | REFINEDtype => 1 case RENAMED | PARAMtype => 2 case POLYtype | METHODtype => -1 + case TYPEBOUNDS => -2 case _ => 0 } } diff --git a/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 37b9341eb62f..4cfd7727c6bf 100644 --- a/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -233,7 +233,11 @@ class TreePickler(pickler: TastyPickler) { } case tpe: TypeBounds => writeByte(TYPEBOUNDS) - withLength { pickleType(tpe.lo, richTypes); pickleType(tpe.hi, richTypes) } + withLength { + pickleType(tpe.lo, richTypes) + pickleType(tpe.hi, richTypes) + if (tpe.bindingKind != NoBinding) writeNat(tpe.bindingKind.n) + } case tpe: AnnotatedType => writeByte(ANNOTATED) withLength { pickleType(tpe.tpe, richTypes); pickleTree(tpe.annot.tree) } diff --git a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 91ac4ea3e9fa..2b8e5f019d23 100644 --- a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -266,7 +266,10 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) { case APPLIEDtype => readType().appliedTo(until(end)(readType())) case TYPEBOUNDS => - TypeBounds(readType(), readType()) + val lo = readType() + val hi = readType() + val bk = ifBefore(end)(new BindingKind(readNat().toByte), NoBinding) + TypeBounds(lo, hi, bk) case TYPEALIAS => val alias = readType() val variance = From 2d8d291446168a67d2f6fb40b51e4c18b0ce99ef Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 24 May 2016 15:58:49 +0200 Subject: [PATCH 002/142] Allow refinements of new types Previously a refinement could only apply to a type bound in the parent. This restriction needs to be dropped for the new encoding of hk type parameters. --- src/dotty/tools/dotc/core/TypeApplications.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index 3ed1798ed035..d73181fcb516 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -50,7 +50,7 @@ object TypeApplications { * * [v1 X1: B1, ..., vn Xn: Bn] -> T * ==> - * Lambda$_v1...vn { type $hk_i: B_i, type $Apply = [X_i := this.$Arg_i] T } + * ([X_i := this.$hk_i] T) { type v_i $hk_i: (new)B_i } */ object TypeLambda { def apply(variances: List[Int], From 10ef0046f7bdfea4d8fba31041468c957f6d7a2c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 25 May 2016 10:19:45 +0200 Subject: [PATCH 003/142] Allow general recursion in refined types. --- .../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 | 39 +++++++++++++------ .../core/unpickleScala2/Scala2Unpickler.scala | 4 +- .../tools/dotc/printing/PlainPrinter.scala | 2 +- .../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 +- 12 files changed, 54 insertions(+), 40 deletions(-) diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index d73181fcb516..8ab5fbf0208e 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 2523c6b9a82b..d1dc4069dd72 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 39d02e069ca7..a5aabe9c4188 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 1288c0b23bd6..72b0c87c431b 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 632ab823ac1a..7a82caeed669 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(parent: Type, refinedName: Name) extends RefinedType(parent, 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,21 @@ 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(NoType, _)) + 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(parent, name) + res.myRefinedInfo = infoFn(res) + ctx.base.uniqueRefinedTypes.enterIfNew(res).checkInst } def apply(parent: Type, name: Name, info: Type)(implicit ctx: Context): RefinedType = { @@ -2668,8 +2683,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 } diff --git a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index 71a919ca357e..687e9279b078 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 1e2ba0b4d19b..bac180efee0a 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 614a274b4f6a..e0fd4790019f 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 f743c5784ea5..25385198ded3 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 22d2407bc558..37753fe659d4 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 995fa43ca7b1..f439c4c99ab0 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 55e6b5232347..bc97301405e1 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) => From 3c99610b240bb0b4d71cae09ee540122f02b5d62 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 25 May 2016 11:09:14 +0200 Subject: [PATCH 004/142] Refinements to refinement types Treat parent like refinedInfo. Introduce isBinding convenience method in TypeBounds. --- src/dotty/tools/dotc/core/Types.scala | 12 ++++++++---- src/dotty/tools/dotc/core/tasty/TreePickler.scala | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 7a82caeed669..b26fd63735b4 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -2078,14 +2078,14 @@ object Types { false } override def computeHash = { - assert(parent.exists) // !!! + assert(parent.exists) doHash(refinedName, refinedInfo, parent) } override def toString = s"RefinedType($parent, $refinedName, $refinedInfo | $hashCode)" } - class CachedRefinedType(parent: Type, refinedName: Name) extends RefinedType(parent, refinedName, NoType) + class CachedRefinedType(refinedName: Name) extends RefinedType(NoType, refinedName, NoType) class PreHashedRefinedType(parent: Type, refinedName: Name, refinedInfo: Type, hc: Int) extends RefinedType(parent, refinedName, refinedInfo) { @@ -2099,7 +2099,7 @@ object Types { 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(NoType, _)) + 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) => @@ -2110,7 +2110,8 @@ object Types { def apply(parent: Type, name: Name, infoFn: RefinedType => Type)(implicit ctx: Context): RefinedType = { assert(!ctx.erasedTypes || ctx.mode.is(Mode.Printing)) - val res: RefinedType = new CachedRefinedType(parent, name) + val res: RefinedType = new CachedRefinedType(name) + res.myParent = parent res.myRefinedInfo = infoFn(res) ctx.base.uniqueRefinedTypes.enterIfNew(res).checkInst } @@ -2875,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 @@ -2891,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 4cfd7727c6bf..0cc08f2d9697 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) From 461fe3d829d8d84f192a828f0f5180a77cad1350 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 26 May 2016 08:46:04 +0200 Subject: [PATCH 005/142] Add type lambdas --- docs/SyntaxSummary.txt | 2 +- src/dotty/tools/dotc/ast/Trees.scala | 16 ++++++++++++++++ src/dotty/tools/dotc/ast/untpd.scala | 1 + src/dotty/tools/dotc/core/TypeApplications.scala | 4 ++-- src/dotty/tools/dotc/parsing/Parsers.scala | 11 ++++++++++- src/dotty/tools/dotc/typer/TypeAssigner.scala | 10 ++++++++++ src/dotty/tools/dotc/typer/Typer.scala | 9 +++++++++ 7 files changed, 49 insertions(+), 4 deletions(-) diff --git a/docs/SyntaxSummary.txt b/docs/SyntaxSummary.txt index d4f7ceadec50..45937fb54fa0 100644 --- a/docs/SyntaxSummary.txt +++ b/docs/SyntaxSummary.txt @@ -96,6 +96,7 @@ grammar. ClassQualifier ::= `[' id `]' Type ::= FunArgTypes `=>' Type Function(ts, t) + | HkTypeParamClause `->' Type TypeLambda(ps, t) | InfixType FunArgTypes ::= InfixType | `(' [ FunArgType {`,' FunArgType } ] `)' @@ -125,7 +126,6 @@ grammar. TypeParamBounds ::= TypeBounds {`<%' Type} {`:' Type} ContextBounds(typeBounds, tps) Expr ::= FunParams `=>' Expr Function(args, expr), Function(ValDef([implicit], id, TypeTree(), EmptyTree), expr) - | Expr1 FunParams ::= Bindings | [`implicit'] id | `_' diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala index 7463449c54da..20ae02994bbf 100644 --- a/src/dotty/tools/dotc/ast/Trees.scala +++ b/src/dotty/tools/dotc/ast/Trees.scala @@ -594,6 +594,12 @@ object Trees { def forwardTo = tpt } + /** [typeparams] -> tpt */ + case class TypeLambdaTree[-T >: Untyped] private[ast] (tparams: List[TypeDef[T]], body: Tree[T]) + extends TypTree[T] { + type ThisTree[-T >: Untyped] = TypeLambdaTree[T] + } + /** => T */ case class ByNameTypeTree[-T >: Untyped] private[ast] (result: Tree[T]) extends TypTree[T] { @@ -851,6 +857,7 @@ object Trees { type OrTypeTree = Trees.OrTypeTree[T] type RefinedTypeTree = Trees.RefinedTypeTree[T] type AppliedTypeTree = Trees.AppliedTypeTree[T] + type TypeLambdaTree = Trees.TypeLambdaTree[T] type ByNameTypeTree = Trees.ByNameTypeTree[T] type TypeBoundsTree = Trees.TypeBoundsTree[T] type Bind = Trees.Bind[T] @@ -1028,6 +1035,10 @@ object Trees { case tree: AppliedTypeTree if (tpt eq tree.tpt) && (args eq tree.args) => tree case _ => finalize(tree, untpd.AppliedTypeTree(tpt, args)) } + def TypeLambdaTree(tree: Tree)(tparams: List[TypeDef], body: Tree): TypeLambdaTree = tree match { + case tree: TypeLambdaTree if (tparams eq tree.tparams) && (body eq tree.body) => tree + case _ => finalize(tree, untpd.TypeLambdaTree(tparams, body)) + } def ByNameTypeTree(tree: Tree)(result: Tree): ByNameTypeTree = tree match { case tree: ByNameTypeTree if result eq tree.result => tree case _ => finalize(tree, untpd.ByNameTypeTree(result)) @@ -1160,6 +1171,8 @@ object Trees { cpy.RefinedTypeTree(tree)(transform(tpt), transformSub(refinements)) case AppliedTypeTree(tpt, args) => cpy.AppliedTypeTree(tree)(transform(tpt), transform(args)) + case TypeLambdaTree(tparams, body) => + cpy.TypeLambdaTree(tree)(transformSub(tparams), transform(body)) case ByNameTypeTree(result) => cpy.ByNameTypeTree(tree)(transform(result)) case TypeBoundsTree(lo, hi) => @@ -1264,6 +1277,9 @@ object Trees { this(this(x, tpt), refinements) case AppliedTypeTree(tpt, args) => this(this(x, tpt), args) + case TypeLambdaTree(tparams, body) => + implicit val ctx: Context = localCtx + this(this(x, tparams), body) case ByNameTypeTree(result) => this(x, result) case TypeBoundsTree(lo, hi) => diff --git a/src/dotty/tools/dotc/ast/untpd.scala b/src/dotty/tools/dotc/ast/untpd.scala index c7a7036c3836..b3f8747dcadf 100644 --- a/src/dotty/tools/dotc/ast/untpd.scala +++ b/src/dotty/tools/dotc/ast/untpd.scala @@ -137,6 +137,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def OrTypeTree(left: Tree, right: Tree): OrTypeTree = new OrTypeTree(left, right) def RefinedTypeTree(tpt: Tree, refinements: List[Tree]): RefinedTypeTree = new RefinedTypeTree(tpt, refinements) def AppliedTypeTree(tpt: Tree, args: List[Tree]): AppliedTypeTree = new AppliedTypeTree(tpt, args) + def TypeLambdaTree(tparams: List[TypeDef], body: Tree): TypeLambdaTree = new TypeLambdaTree(tparams, body) def ByNameTypeTree(result: Tree): ByNameTypeTree = new ByNameTypeTree(result) def TypeBoundsTree(lo: Tree, hi: Tree): TypeBoundsTree = new TypeBoundsTree(lo, hi) def Bind(name: Name, body: Tree): Bind = new Bind(name, body) diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index 8ab5fbf0208e..1f40aaf06a33 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -388,9 +388,9 @@ class TypeApplications(val self: Type) extends AnyVal { /** Replace references to type parameters with references to hk arguments `this.$hk_i` * Care is needed not to cause cyclic reference errors, hence `SafeSubstMap`. */ - private[TypeApplications] def internalizeFrom[T <: Type](tparams: List[Symbol])(implicit ctx: Context): RefinedType => T = + def internalizeFrom[T <: Type](tparams: List[Symbol])(implicit ctx: Context): RefinedType => T = (rt: RefinedType) => - new ctx.SafeSubstMap(tparams , argRefs(rt, tparams.length)) + new ctx.SafeSubstMap(tparams, argRefs(rt, tparams.length)) .apply(self).asInstanceOf[T] /** Lambda abstract `self` with given type parameters. Examples: diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala index ded17c67cdf2..0cc392badc36 100644 --- a/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/src/dotty/tools/dotc/parsing/Parsers.scala @@ -223,7 +223,9 @@ object Parsers { } // DEBUG private def expectedMsg(token: Int): String = - showToken(token) + " expected but " + showToken(in.token) + " found." + expectedMessage(showToken(token)) + private def expectedMessage(what: String): String = + s"$what expected but ${showToken(in.token)} found" /** Consume one token of the specified type, or * signal an error if it is not there. @@ -648,6 +650,7 @@ object Parsers { /* ------------- TYPES ------------------------------------------------------ */ /** Type ::= FunArgTypes `=>' Type + * | HkTypeParamClause `->' Type * | InfixType * FunArgTypes ::= InfixType * | `(' [ FunArgType {`,' FunArgType } ] `)' @@ -677,6 +680,12 @@ object Parsers { } } } + else if (in.token == LBRACKET) { + val tparams = typeParamClause(ParamOwner.TypeParam) + if (isIdent && in.name.toString == "->") + atPos(in.skipToken())(TypeLambdaTree(tparams, typ())) + else { syntaxErrorOrIncomplete(expectedMessage("`->'")); typ() } + } else infixType() in.token match { diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index f439c4c99ab0..b686e6eed4d7 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -425,6 +425,16 @@ trait TypeAssigner { tree.withType(ownType) } + def assignType(tree: untpd.TypeLambdaTree, tparamDefs: List[TypeDef], body: Tree)(implicit ctx: Context) = { + val tparams = tparamDefs.map(_.symbol) + val argBindingFns = tparams.map(tparam => + tparam.info.bounds + .withBindingKind(BindingKind.fromVariance(tparam.variance)) + .internalizeFrom(tparams)) + val bodyFn = body.tpe.internalizeFrom(tparams) + tree.withType(TypeApplicationsNewHK.TypeLambda(argBindingFns, bodyFn)) + } + def assignType(tree: untpd.ByNameTypeTree, result: Tree)(implicit ctx: Context) = tree.withType(ExprType(result.tpe)) diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 4f27912f115d..50204d587667 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -954,6 +954,14 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } } + def typedTypeLambdaTree(tree: untpd.TypeLambdaTree)(implicit ctx: Context): Tree = track("typedTypeLambdaTree") { + val TypeLambdaTree(tparams, body) = tree + index(tparams) + val tparams1 = tparams.mapconserve(typed(_).asInstanceOf[TypeDef]) + val body1 = typedType(tree.body) + assignType(cpy.TypeLambdaTree(tree)(tparams1, body1), tparams1, body1) + } + def typedByNameTypeTree(tree: untpd.ByNameTypeTree)(implicit ctx: Context): ByNameTypeTree = track("typedByNameTypeTree") { val result1 = typed(tree.result) assignType(cpy.ByNameTypeTree(tree)(result1), result1) @@ -1268,6 +1276,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case tree: untpd.OrTypeTree => typedOrTypeTree(tree) case tree: untpd.RefinedTypeTree => typedRefinedTypeTree(tree) case tree: untpd.AppliedTypeTree => typedAppliedTypeTree(tree) + case tree: untpd.TypeLambdaTree => typedTypeLambdaTree(tree)(localContext(tree, NoSymbol).setNewScope) case tree: untpd.ByNameTypeTree => typedByNameTypeTree(tree) case tree: untpd.TypeBoundsTree => typedTypeBoundsTree(tree) case tree: untpd.Alternative => typedAlternative(tree, pt) From 39607106e0c23d2244c214c21d8ad21563c8286d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 26 May 2016 08:51:14 +0200 Subject: [PATCH 006/142] Better printing of skolems They not print similar to scalac: "?x" where `x` is a unique number. Todo: An offline explanation what they are, similar to javac. I.e. ... ?3 ... where ?3: T --- src/dotty/tools/dotc/core/Types.scala | 7 +++++++ src/dotty/tools/dotc/printing/PlainPrinter.scala | 3 +-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index b26fd63735b4..8eae84a51b17 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -2596,6 +2596,13 @@ object Types { if (info eq this.info) this else SkolemType(info) override def hashCode: Int = identityHash override def equals(that: Any) = this eq that.asInstanceOf[AnyRef] + + private var myRepr: String = null + def repr(implicit ctx: Context) = { + if (myRepr == null) myRepr = ctx.freshName("?") + myRepr + } + override def toString = s"Skolem($info)" } diff --git a/src/dotty/tools/dotc/printing/PlainPrinter.scala b/src/dotty/tools/dotc/printing/PlainPrinter.scala index bac180efee0a..7053a0ea3c92 100644 --- a/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -235,8 +235,7 @@ class PlainPrinter(_ctx: Context) extends Printer { case tp: RefinedThis => s"${nameString(tp.binder.typeSymbol)}{...}.this" case tp: SkolemType => - if (homogenizedView) toText(tp.info) - else "" + if (homogenizedView) toText(tp.info) else tp.repr } } From a3a610822a2d1f0e0c1aa08d3c62d9e3b1b134e0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 26 May 2016 08:52:47 +0200 Subject: [PATCH 007/142] New type lambda scheme for hk types --- src/dotty/tools/dotc/config/Config.scala | 2 ++ .../tools/dotc/core/TypeApplications.scala | 35 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/dotty/tools/dotc/config/Config.scala b/src/dotty/tools/dotc/config/Config.scala index 3cc3091b5a17..be8a367d71cf 100644 --- a/src/dotty/tools/dotc/config/Config.scala +++ b/src/dotty/tools/dotc/config/Config.scala @@ -8,6 +8,8 @@ object Config { final val cacheMemberNames = true final val cacheImplicitScopes = true + final val newHK = false + final val checkCacheMembersNamed = false /** When updating a constraint bound, check that the constrained parameter diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index 1f40aaf06a33..2411e0bb27e2 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -15,8 +15,35 @@ import StdNames.tpnme import util.Positions.Position import config.Printers._ import collection.mutable + import dotty.tools.dotc.config.Config import java.util.NoSuchElementException +object TypeApplicationsNewHK { + import TypeApplications._ + + object TypeLambda { + def apply(argBindingFns: List[RefinedType => TypeBounds], + bodyFn: RefinedType => Type)(implicit ctx: Context): Type = { + val argNames = argBindingFns.indices.toList.map(tpnme.hkArg) + RefinedType.recursive(bodyFn, argNames, argBindingFns) + } + + def unapply(tp: Type)(implicit ctx: Context): Option[(List[TypeBounds], Type)] = { + def decompose(t: Type, acc: List[TypeBounds]): (List[TypeBounds], Type) = t match { + case t @ RefinedType(p, rname, rinfo: TypeBounds) + if rname.isHkArgName && rinfo.isBinding => + decompose(p, rinfo.bounds :: acc) + case _ => + (acc, t) + } + decompose(tp, Nil) match { + case (Nil, _) => None + case x => Some(x) + } + } + } +} + object TypeApplications { /** Assert type is not a TypeBounds instance and return it unchanged */ @@ -51,6 +78,14 @@ object TypeApplications { * [v1 X1: B1, ..., vn Xn: Bn] -> T * ==> * ([X_i := this.$hk_i] T) { type v_i $hk_i: (new)B_i } + * + * [X] -> List[X] + * + * List { type List$A = this.$hk_0 } { type $hk_0 } + * + * [X] -> X + * + * mu(this) this.$hk_0 & { type $hk_0 } */ object TypeLambda { def apply(variances: List[Int], From e7683311776d7e761f3e66f88e0ac75b66631463 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 27 May 2016 08:52:39 +0200 Subject: [PATCH 008/142] Add mapInfo method to Denotations --- src/dotty/tools/dotc/core/Denotations.scala | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index 5ce8cbcd8b5f..494df7547a87 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -146,6 +146,9 @@ object Denotations { /** Is this denotation different from NoDenotation or an ErrorDenotation? */ def exists: Boolean = true + /** A denotation with the info of this denotation transformed using `f` */ + def mapInfo(f: Type => Type)(implicit ctx: Context): Denotation + /** If this denotation does not exist, fallback to alternative */ final def orElse(that: => Denotation) = if (this.exists) this else that @@ -242,7 +245,7 @@ object Denotations { } else if (exists && !qualifies(symbol)) NoDenotation else asSingleDenotation - } + } /** Form a denotation by conjoining with denotation `that`. * @@ -456,6 +459,8 @@ object Denotations { else if (!d2.exists) d1 else derivedMultiDenotation(d1, d2) } + def mapInfo(f: Type => Type)(implicit ctx: Context): Denotation = + derivedMultiDenotation(denot1.mapInfo(f), denot2.mapInfo(f)) def derivedMultiDenotation(d1: Denotation, d2: Denotation) = if ((d1 eq denot1) && (d2 eq denot2)) this else MultiDenotation(d1, d2) override def toString = alternatives.mkString(" ") @@ -488,6 +493,9 @@ object Denotations { if ((symbol eq this.symbol) && (info eq this.info)) this else newLikeThis(symbol, info) + def mapInfo(f: Type => Type)(implicit ctx: Context): SingleDenotation = + derivedSingleDenotation(symbol, f(info)) + def orElse(that: => SingleDenotation) = if (this.exists) this else that def altsWith(p: Symbol => Boolean): List[SingleDenotation] = From c7fa021ad8b72c8cc8dac9287a8fc40219fdeb89 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 27 May 2016 09:09:40 +0200 Subject: [PATCH 009/142] Introduce recursive types Map self-references in refinements to recursive types. This commit does this for refinement types appearing in source. We still have to do it for unpickled refinements. Test apply-equiv got moved to pending because it simulates the old higher-kinded type encoding in source, which relies on the old representation in terms of self-referential refinement types. The plan is not to adapt this encoding to the new representation, but to replace it with a different encoding that makes critical use of the added power of recursive types. --- src/dotty/tools/dotc/core/Substituters.scala | 22 +++ .../tools/dotc/core/SymDenotations.scala | 3 +- .../tools/dotc/core/TypeApplications.scala | 40 ++++-- src/dotty/tools/dotc/core/TypeComparer.scala | 31 ++++- src/dotty/tools/dotc/core/TypeOps.scala | 23 +++- src/dotty/tools/dotc/core/Types.scala | 129 +++++++++++++++++- .../tools/dotc/core/tasty/TastyFormat.scala | 8 +- .../tools/dotc/core/tasty/TreePickler.scala | 8 ++ .../tools/dotc/core/tasty/TreeUnpickler.scala | 7 +- .../tools/dotc/printing/PlainPrinter.scala | 14 ++ src/dotty/tools/dotc/typer/Checking.scala | 8 +- src/dotty/tools/dotc/typer/TypeAssigner.scala | 4 +- src/dotty/tools/dotc/typer/Typer.scala | 7 +- src/dotty/tools/dotc/typer/Variances.scala | 2 + tests/{ => pending}/pos/apply-equiv.scala | 0 tests/pos/lookuprefined.scala | 6 +- 16 files changed, 281 insertions(+), 31 deletions(-) rename tests/{ => pending}/pos/apply-equiv.scala (100%) diff --git a/src/dotty/tools/dotc/core/Substituters.scala b/src/dotty/tools/dotc/core/Substituters.scala index 0083ac626fae..4598aaa20429 100644 --- a/src/dotty/tools/dotc/core/Substituters.scala +++ b/src/dotty/tools/dotc/core/Substituters.scala @@ -197,6 +197,24 @@ trait Substituters { this: Context => .mapOver(tp) } + final def substRecThis(tp: Type, from: Type, to: Type, theMap: SubstRecThisMap): Type = + tp match { + case tp @ RecThis(binder) => + if (binder eq from) to else tp + case tp: NamedType => + if (tp.currentSymbol.isStatic) tp + else tp.derivedSelect(substRecThis(tp.prefix, from, to, theMap)) + case _: ThisType | _: BoundType | NoPrefix => + tp + case tp: RefinedType => + tp.derivedRefinedType(substRecThis(tp.parent, from, to, theMap), tp.refinedName, substRecThis(tp.refinedInfo, from, to, theMap)) + case tp: TypeAlias => + tp.derivedTypeAlias(substRecThis(tp.alias, from, to, theMap)) + case _ => + (if (theMap != null) theMap else new SubstRecThisMap(from, to)) + .mapOver(tp) + } + final def substParam(tp: Type, from: ParamType, to: Type, theMap: SubstParamMap): Type = tp match { case tp: BoundType => @@ -270,6 +288,10 @@ trait Substituters { this: Context => def apply(tp: Type): Type = substRefinedThis(tp, from, to, this) } + final class SubstRecThisMap(from: Type, to: Type) extends DeepTypeMap { + def apply(tp: Type): Type = substRecThis(tp, from, to, this) + } + final class SubstParamMap(from: ParamType, to: Type) extends DeepTypeMap { def apply(tp: Type) = substParam(tp, from, to, this) } diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index ff99f3b5527e..fb70459d3c4d 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1117,10 +1117,11 @@ object SymDenotations { def debugString = toString + "#" + symbol.id // !!! DEBUG - def hasSkolems(tp: Type): Boolean = tp match { + def hasSkolems(tp: Type): Boolean = tp match { case tp: SkolemType => true case tp: NamedType => hasSkolems(tp.prefix) case tp: RefinedType => hasSkolems(tp.parent) || hasSkolems(tp.refinedInfo) + case tp: RecType => hasSkolems(tp.parent) case tp: PolyType => tp.paramBounds.exists(hasSkolems) || hasSkolems(tp.resType) case tp: MethodType => tp.paramTypes.exists(hasSkolems) || hasSkolems(tp.resType) case tp: ExprType => hasSkolems(tp.resType) diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index 2411e0bb27e2..bd115fefb9de 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -22,10 +22,16 @@ object TypeApplicationsNewHK { import TypeApplications._ object TypeLambda { - def apply(argBindingFns: List[RefinedType => TypeBounds], - bodyFn: RefinedType => Type)(implicit ctx: Context): Type = { + def apply(argBindingFns: List[RecType => TypeBounds], + bodyFn: RecType => Type)(implicit ctx: Context): Type = { val argNames = argBindingFns.indices.toList.map(tpnme.hkArg) - RefinedType.recursive(bodyFn, argNames, argBindingFns) + var idx = 0 + RecType.closeOver(rt => + (bodyFn(rt) /: argBindingFns) { (parent, argBindingFn) => + val res = RefinedType(parent, tpnme.hkArg(idx), argBindingFn(rt)) + idx += 1 + res + }) } def unapply(tp: Type)(implicit ctx: Context): Option[(List[TypeBounds], Type)] = { @@ -33,6 +39,8 @@ object TypeApplicationsNewHK { case t @ RefinedType(p, rname, rinfo: TypeBounds) if rname.isHkArgName && rinfo.isBinding => decompose(p, rinfo.bounds :: acc) + case t: RecType => + decompose(t.parent, acc) case _ => (acc, t) } @@ -78,13 +86,13 @@ object TypeApplications { * [v1 X1: B1, ..., vn Xn: Bn] -> T * ==> * ([X_i := this.$hk_i] T) { type v_i $hk_i: (new)B_i } - * + * * [X] -> List[X] - * + * * List { type List$A = this.$hk_0 } { type $hk_0 } - * + * * [X] -> X - * + * * mu(this) this.$hk_0 & { type $hk_0 } */ object TypeLambda { @@ -212,6 +220,10 @@ object TypeApplications { def argRefs(rt: RefinedType, n: Int)(implicit ctx: Context) = List.range(0, n).map(i => RefinedThis(rt).select(tpnme.hkArg(i))) + /** The references `.this.$hk0, ..., .this.$hk`. */ + def argRefs(rt: RecType, n: Int)(implicit ctx: Context) = + List.range(0, n).map(i => RecThis(rt).select(tpnme.hkArg(i))) + /** Merge `tp1` and `tp2` under a common lambda, combining them with `op`. * @param tparams1 The type parameters of `tp1` * @param tparams2 The type parameters of `tp2` @@ -400,7 +412,7 @@ class TypeApplications(val self: Type) extends AnyVal { * but without forcing anything. */ def classNotLambda(implicit ctx: Context): Boolean = self.stripTypeVar match { - case self: RefinedType => + case self: RefinedOrRecType => self.parent.classNotLambda case self: TypeRef => self.denot.exists && { @@ -428,6 +440,14 @@ class TypeApplications(val self: Type) extends AnyVal { new ctx.SafeSubstMap(tparams, argRefs(rt, tparams.length)) .apply(self).asInstanceOf[T] + /** Replace references to type parameters with references to hk arguments `this.$hk_i` + * Care is needed not to cause cyclic reference errors, hence `SafeSubstMap`. + */ + def recursify[T <: Type](tparams: List[Symbol])(implicit ctx: Context): RecType => T = + (rt: RecType) => + new ctx.SafeSubstMap(tparams, argRefs(rt, tparams.length)) + .apply(self).asInstanceOf[T] + /** Lambda abstract `self` with given type parameters. Examples: * * type T[X] = U becomes type T = [X] -> U @@ -546,6 +566,8 @@ class TypeApplications(val self: Type) extends AnyVal { arg.prefix.select(boundLambda) case arg: RefinedType => arg.derivedRefinedType(adaptArg(arg.parent), arg.refinedName, arg.refinedInfo) + case arg: RecType => + arg.derivedRecType(adaptArg(arg.parent)) case arg @ TypeAlias(alias) => arg.derivedTypeAlias(adaptArg(alias)) case arg @ TypeBounds(lo, hi) => @@ -814,6 +836,8 @@ class TypeApplications(val self: Type) extends AnyVal { } case tp: RefinedType => recur(tp.refinedInfo) || recur(tp.parent) + case tp: RecType => + recur(tp.parent) case tp: TypeBounds => recur(tp.lo) || recur(tp.hi) case tp: AnnotatedType => diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index d1dc4069dd72..9909c9e8ac8c 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -378,6 +378,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { isSubRefinements(tp1w.asInstanceOf[RefinedType], tp2, skipped2) } compareRefined + case tp2: RecType => + val tp1stable = ensureStableSingleton(tp1) + isSubType(fixRecs(tp1stable, tp1stable.widenExpr), tp2.substRecThis(tp2, tp1stable)) case OrType(tp21, tp22) => // Rewrite T1 <: (T211 & T212) | T22 to T1 <: (T211 | T22) and T1 <: (T212 | T22) // and analogously for T1 <: T21 | (T221 & T222) @@ -465,7 +468,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 tp: RefinedType => isNullable(tp.parent) + case tp: RefinedOrRecType => isNullable(tp.parent) case AndType(tp1, tp2) => isNullable(tp1) && isNullable(tp2) case OrType(tp1, tp2) => isNullable(tp1) || isNullable(tp2) case _ => false @@ -494,6 +497,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { isNewSubType(tp1.parent, tp2) || compareHkLambda(tp1, tp2, inOrder = true) || compareAliasedRefined(tp1, tp2, inOrder = true) + case tp1: RecType => + isNewSubType(tp1.parent, tp2) case AndType(tp11, tp12) => // Rewrite (T111 | T112) & T12 <: T2 to (T111 & T12) <: T2 and (T112 | T12) <: T2 // and analogously for T11 & (T121 | T122) & T12 <: T2 @@ -642,6 +647,25 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { } } + /** Replace any top-level recursive type `{ z => T }` in `tp` with + * `[z := anchor]T`. + */ + private def fixRecs(anchor: SingletonType, tp: Type): Type = { + def fix(tp: Type): Type = tp.stripTypeVar match { + case tp: RecType => fix(tp.parent).substRecThis(tp, anchor) + case tp @ RefinedType(parent, rname, rinfo) => tp.derivedRefinedType(fix(parent), rname, rinfo) + case tp: PolyParam => fixOrElse(bounds(tp).hi, tp) + case tp: TypeProxy => fixOrElse(tp.underlying, tp) + case tp: AndOrType => tp.derivedAndOrType(fix(tp.tp1), fix(tp.tp2)) + case tp => tp + } + def fixOrElse(tp: Type, fallback: Type) = { + val tp1 = fix(tp) + if (tp1 ne tp) tp1 else fallback + } + fix(tp) + } + /** The symbol referred to in the refinement of `rt` */ private def refinedSymbol(rt: RefinedType) = rt.parent.member(rt.refinedName).symbol @@ -772,7 +796,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { /** A type has been covered previously in subtype checking if it * is some combination of TypeRefs that point to classes, where the - * combiners are RefinedTypes, AndTypes or AnnotatedTypes. + * combiners are RefinedTypes, RecTypes, AndTypes or AnnotatedTypes. * One exception: Refinements referring to basetype args are never considered * to be already covered. This is necessary because such refined types might * still need to be compared with a compareAliasRefined. @@ -781,6 +805,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case tp: TypeRef => tp.symbol.isClass && tp.symbol != NothingClass && tp.symbol != NullClass case tp: ProtoType => false case tp: RefinedType => isCovered(tp.parent) && !refinedSymbol(tp).is(BaseTypeArg) + case tp: RecType => isCovered(tp.parent) case tp: AnnotatedType => isCovered(tp.underlying) case AndType(tp1, tp2) => isCovered(tp1) && isCovered(tp2) case _ => false @@ -1118,6 +1143,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case _ => NoType } + case tp1: RecType => + tp1.rebind(distributeAnd(tp1.parent, tp2)) case tp1: TypeBounds => tp2 match { case tp2: TypeBounds => tp1 & tp2 diff --git a/src/dotty/tools/dotc/core/TypeOps.scala b/src/dotty/tools/dotc/core/TypeOps.scala index 72b0c87c431b..54846087f186 100644 --- a/src/dotty/tools/dotc/core/TypeOps.scala +++ b/src/dotty/tools/dotc/core/TypeOps.scala @@ -232,11 +232,16 @@ trait TypeOps { this: Context => // TODO: Make standalone object. } case _ => } + tp1 match { + case tp1: RecType => + tp1.rebind(approximateOr(tp1.parent, tp2)) case tp1: TypeProxy if !isClassRef(tp1) => approximateUnion(next(tp1) | tp2) case _ => tp2 match { + case tp2: RecType => + tp2.rebind(approximateOr(tp1, tp2.parent)) case tp2: TypeProxy if !isClassRef(tp2) => approximateUnion(tp1 | next(tp2)) case _ => @@ -252,16 +257,32 @@ trait TypeOps { this: Context => // TODO: Make standalone object. if (ctx.featureEnabled(defn.LanguageModuleClass, nme.keepUnions)) tp else tp match { case tp: OrType => - approximateOr(tp.tp1, tp.tp2) + approximateOr(tp.tp1, tp.tp2) // Maybe refactor using liftToRec? case tp @ AndType(tp1, tp2) => tp derived_& (approximateUnion(tp1), approximateUnion(tp2)) case tp: RefinedType => tp.derivedRefinedType(approximateUnion(tp.parent), tp.refinedName, tp.refinedInfo) + case tp: RecType => + tp.rebind(approximateUnion(tp.parent)) case _ => tp } } + /** Not currently needed: + * + def liftToRec(f: (Type, Type) => Type)(tp1: Type, tp2: Type)(implicit ctx: Context) = { + def f2(tp1: Type, tp2: Type): Type = tp2 match { + case tp2: RecType => tp2.rebind(f(tp1, tp2.parent)) + case _ => f(tp1, tp2) + } + tp1 match { + case tp1: RecType => tp1.rebind(f2(tp1.parent, tp2)) + case _ => f2(tp1, tp2) + } + } + */ + private def enterArgBinding(formal: Symbol, info: Type, cls: ClassSymbol, decls: Scope) = { val lazyInfo = new LazyType { // needed so we do not force `formal`. def complete(denot: SymDenotation)(implicit ctx: Context): Unit = { diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 8eae84a51b17..5252a9149a6c 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -54,7 +54,8 @@ object Types { * | | +----RefinedThis * | | +--- SkolemType * | +- PolyParam - * | +- RefinedType + * | +- RefinedOrRecType -+-- RefinedType + * | | -+-- RecType * | +- TypeBounds * | +- ExprType * | +- AnnotatedType @@ -97,7 +98,7 @@ object Types { final def isStable(implicit ctx: Context): Boolean = stripTypeVar match { case tp: TermRef => tp.termSymbol.isStable && tp.prefix.isStable case _: SingletonType | NoPrefix => true - case tp: RefinedType => tp.parent.isStable + case tp: RefinedOrRecType => tp.parent.isStable case _ => false } @@ -113,7 +114,7 @@ object Types { case TypeAlias(tp) => tp.isRef(sym) case _ => this1.symbol eq sym } - case this1: RefinedType => + case this1: RefinedOrRecType => this1.parent.isRef(sym) case _ => false @@ -447,6 +448,8 @@ object Types { }) case tp: PolyParam => goParam(tp) + case tp: RecType => + goRec(tp) case tp: TypeProxy => go(tp.underlying) case tp: ClassInfo => @@ -462,6 +465,8 @@ object Types { case _ => NoDenotation } + def goRec(tp: RecType) = + go(tp.parent).mapInfo(_.substRecThis(tp, pre)) def goRefined(tp: RefinedType) = { val pdenot = go(tp.parent) val rinfo = @@ -864,6 +869,7 @@ object Types { def isParamName = tp.classSymbol.typeParams.exists(_.name == tp.refinedName) if (refinementOK || isParamName) tp.underlying.underlyingClassRef(refinementOK) else NoType + case tp: RecType if refinementOK => tp.parent case _ => NoType } @@ -958,6 +964,13 @@ object Types { } case _ => loop(pre.parent) } + case pre: RecType => + val candidate = loop(pre.parent) + if (candidate.exists && !pre.isReferredToBy(candidate)) { + //println(s"lookupRefined ${this.toString} . $name, pre: $pre ---> $candidate / ${candidate.toString}") + candidate + } + else NoType case RefinedThis(binder) => binder.lookupRefined(name) case SkolemType(tp) => @@ -1152,6 +1165,10 @@ object Types { final def substRefinedThis(binder: Type, tp: Type)(implicit ctx: Context): Type = ctx.substRefinedThis(this, binder, tp, null) + /** Substitute all occurrences of `RecThis(binder)` by `tp` */ + final def substRecThis(binder: RecType, tp: Type)(implicit ctx: Context): Type = + ctx.substRecThis(this, binder, tp, null) + /** Substitute a bound type by some other type */ final def substParam(from: ParamType, to: Type)(implicit ctx: Context): Type = ctx.substParam(this, from, to, null) @@ -2022,7 +2039,11 @@ object Types { override def hashCode = ref.hashCode + 37 } - // --- Refined Type --------------------------------------------------------- + // --- Refined Type and RecType ------------------------------------------------ + + abstract class RefinedOrRecType extends CachedProxyType with ValueType { + def parent: Type + } /** A refined type parent { refinement } * @param refinedName The name of the refinement declaration @@ -2030,7 +2051,7 @@ object Types { * given the refined type itself. */ abstract case class RefinedType(private var myParent: Type, refinedName: Name, private var myRefinedInfo: Type) - extends CachedProxyType with BindingType with ValueType { + extends RefinedOrRecType with BindingType { final def parent = myParent final def refinedInfo = myRefinedInfo @@ -2082,7 +2103,7 @@ object Types { doHash(refinedName, refinedInfo, parent) } - override def toString = s"RefinedType($parent, $refinedName, $refinedInfo | $hashCode)" + override def toString = s"RefinedType($parent, $refinedName, $refinedInfo)" } class CachedRefinedType(refinedName: Name) extends RefinedType(NoType, refinedName, NoType) @@ -2122,6 +2143,73 @@ object Types { } } + class RecType(parentExp: RecType => Type) extends RefinedOrRecType with BindingType { + + val parent = parentExp(this) + + override def underlying(implicit ctx: Context): Type = parent + + def derivedRecType(parent: Type)(implicit ctx: Context): RecType = + if (parent eq this.parent) this + else RecType(rt => parent.substRecThis(this, RecThis(rt))) + + def rebind(parent: Type)(implicit ctx: Context): Type = + if (parent eq this.parent) this + else RecType.closeOver(rt => parent.substRecThis(this, RecThis(rt))) + + override def equals(other: Any) = other match { + case other: RecType => other.parent == this.parent + case _ => false + } + + def isReferredToBy(tp: Type)(implicit ctx: Context): Boolean = { + val refacc = new TypeAccumulator[Boolean] { + override def apply(x: Boolean, tp: Type) = x || { + tp match { + case tp: TypeRef => apply(x, tp.prefix) + case tp: RecThis => RecType.this eq tp.binder + case _ => foldOver(x, tp) + } + } + } + refacc.apply(false, tp) + } + + override def computeHash = doHash(parent) + + override def toString = s"RecType($parent | $hashCode)" + } + + object RecType { + def checkInst(tp: Type)(implicit ctx: Context): tp.type = { + var binders: List[RecType] = Nil + tp.foreachPart { + case rt: RecType => binders = rt :: binders + case rt: RecThis => assert(binders contains rt.binder, tp) + case _ => + } + tp + } + def apply(parentExp: RecType => Type)(implicit ctx: Context): RecType = checkInst { + var rt = new RecType(parentExp) + rt.parent match { + case rt2: RecType => + rt = rt2.derivedRecType(rt2.parent.substRecThis(rt, RecThis(rt2))) + case _ => + } + unique(rt) + } + def closeOver(parentExp: RecType => Type)(implicit ctx: Context) = checkInst { + val rt = this(parentExp) + //val self = RecThis(rt) + def isSelfRef(t: Type) = t match { + case RecThis(binder) => binder eq rt + case _ => false + } + if (rt.existsPart(isSelfRef)) rt else rt.parent + } + } + // --- AndType/OrType --------------------------------------------------------------- trait AndOrType extends ValueType { // todo: check where we can simplify using AndOrType @@ -2587,6 +2675,22 @@ object Types { override def toString = s"RefinedThis(${binder.hashCode})" } + /** a self-reference to an enclosing recursive type. */ + case class RecThis(binder: RecType) extends BoundType with SingletonType { + type BT = RecType + override def underlying(implicit ctx: Context) = binder + def copyBoundType(bt: BT) = RecThis(bt) + + // need to customize hashCode and equals to prevent infinite recursion + // between RecTypes and RecRefs. + override def computeHash = addDelta(binder.identityHash, 41) + override def equals(that: Any) = that match { + case that: RecThis => this.binder eq that.binder + case _ => false + } + override def toString = s"RecThis(${binder.hashCode})" + } + // ----- Skolem types ----------------------------------------------- /** A skolem type reference with underlying type `binder`. */ @@ -2691,7 +2795,7 @@ object Types { } def isOrType(tp: Type): Boolean = tp.stripTypeVar.dealias match { case tp: OrType => true - case tp: RefinedType => isOrType(tp.parent) + case tp: RefinedOrRecType => isOrType(tp.parent) case AndType(tp1, tp2) => isOrType(tp1) | isOrType(tp2) case WildcardType(bounds: TypeBounds) => isOrType(bounds.hi) case _ => false @@ -3160,6 +3264,8 @@ object Types { tp.derivedSelect(pre) protected def derivedRefinedType(tp: RefinedType, parent: Type, info: Type): Type = tp.derivedRefinedType(parent, tp.refinedName, info) + protected def derivedRecType(tp: RecType, parent: Type): Type = + tp.rebind(parent) protected def derivedTypeAlias(tp: TypeAlias, alias: Type): Type = tp.derivedTypeAlias(alias) protected def derivedTypeBounds(tp: TypeBounds, lo: Type, hi: Type): Type = @@ -3234,6 +3340,9 @@ object Types { } mapOverPoly + case tp: RecType => + derivedRecType(tp, this(tp.parent)) + case tp @ SuperType(thistp, supertp) => derivedSuperType(tp, this(thistp), this(supertp)) @@ -3335,6 +3444,9 @@ object Types { override protected def derivedRefinedType(tp: RefinedType, parent: Type, info: Type) = if (parent.exists && info.exists) tp.derivedRefinedType(parent, tp.refinedName, info) else approx(hi = parent) + override protected def derivedRecType(tp: RecType, parent: Type) = + if (parent.exists) tp.rebind(parent) + else approx() override protected def derivedTypeAlias(tp: TypeAlias, alias: Type) = if (alias.exists) tp.derivedTypeAlias(alias) else approx(NoType, TypeBounds.empty) @@ -3433,6 +3545,9 @@ object Types { variance = -variance this(y, tp.resultType) + case tp: RecType => + this(x, tp.parent) + case SuperType(thistp, supertp) => this(this(x, thistp), supertp) diff --git a/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index a42958e756ae..e9708961a369 100644 --- a/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -103,7 +103,7 @@ Standard-Section: "ASTs" TopLevelStat* TERMREFpkg fullyQualified_NameRef TERMREF possiblySigned_NameRef qual_Type THIS clsRef_Type - REFINEDthis refinedType_ASTRef + RECthis recType_ASTRef SHARED path_ASTRef Constant = UNITconst @@ -126,6 +126,7 @@ Standard-Section: "ASTs" TopLevelStat* TYPEREFsymbol sym_ASTRef qual_Type TYPEREFpkg fullyQualified_NameRef TYPEREF possiblySigned_NameRef qual_Type + RECtype parent_Type SUPERtype Length this_Type underlying_Type REFINEDtype Length underlying_Type refinement_NameRef info_Type APPLIEDtype Length tycon_Type arg_Type* @@ -259,6 +260,7 @@ object TastyFormat { final val TERMREFpkg = 67 final val TYPEREFpkg = 68 final val REFINEDthis = 69 + final val RECthis = REFINEDthis // !!! final val BYTEconst = 70 final val SHORTconst = 71 final val CHARconst = 72 @@ -277,6 +279,7 @@ object TastyFormat { final val IMPLICITarg = 101 final val PRIVATEqualified = 102 final val PROTECTEDqualified = 103 + final val RECtype = 104 final val IDENT = 112 final val SELECT = 113 @@ -417,7 +420,7 @@ object TastyFormat { case TYPEREFdirect => "TYPEREFdirect" case TERMREFpkg => "TERMREFpkg" case TYPEREFpkg => "TYPEREFpkg" - case REFINEDthis => "REFINEDthis" + case RECthis => "RECthis" case BYTEconst => "BYTEconst" case SHORTconst => "SHORTconst" case CHARconst => "CHARconst" @@ -426,6 +429,7 @@ object TastyFormat { case FLOATconst => "FLOATconst" case DOUBLEconst => "DOUBLEconst" case STRINGconst => "STRINGconst" + case RECtype => "RECtype" case IDENT => "IDENT" case SELECT => "SELECT" diff --git a/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 0cc08f2d9697..9f703b5af65f 100644 --- a/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -212,6 +212,11 @@ class TreePickler(pickler: TastyPickler) { val binderAddr = pickledTypes.get(tpe.binder) assert(binderAddr != null, tpe.binder) writeRef(binderAddr.asInstanceOf[Addr]) + case tpe: RecThis => + writeByte(RECthis) + val binderAddr = pickledTypes.get(tpe.binder) + assert(binderAddr != null, tpe.binder) + writeRef(binderAddr.asInstanceOf[Addr]) case tpe: SkolemType => pickleType(tpe.info) case tpe: RefinedType => @@ -221,6 +226,9 @@ class TreePickler(pickler: TastyPickler) { pickleType(tpe.parent) pickleType(tpe.refinedInfo, richTypes = true) } + case tpe: RecType => + writeByte(RECtype) + pickleType(tpe.parent) case tpe: TypeAlias => writeByte(TYPEALIAS) withLength { diff --git a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 2b8e5f019d23..1b4e7845a9ee 100644 --- a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -335,8 +335,13 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) { } case THIS => ThisType.raw(readType().asInstanceOf[TypeRef]) + case RECtype => + RecType(rt => registeringType(rt, readType())) case REFINEDthis => - RefinedThis(readTypeRef().asInstanceOf[RefinedType]) + readTypeRef() match { + case t: RefinedType => RefinedThis(t) + case t: RecType => RecThis(t) + } case SHARED => val ref = readAddr() typeAtAddr.getOrElseUpdate(ref, forkAt(ref).readType()) diff --git a/src/dotty/tools/dotc/printing/PlainPrinter.scala b/src/dotty/tools/dotc/printing/PlainPrinter.scala index 7053a0ea3c92..59f1608dbe39 100644 --- a/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -13,6 +13,8 @@ import scala.annotation.switch class PlainPrinter(_ctx: Context) extends Printer { protected[this] implicit def ctx: Context = _ctx.addMode(Mode.Printing) + private var openRecs: List[RecType] = Nil + protected def maxToTextRecursions = 100 protected final def controlled(op: => Text): Text = @@ -58,6 +60,8 @@ class PlainPrinter(_ctx: Context) extends Printer { } else tp + private def selfRecName(n: Int) = s"z$n" + /** Render elements alternating with `sep` string */ protected def toText(elems: Traversable[Showable], sep: String) = Text(elems map (_ toText this), sep) @@ -130,6 +134,12 @@ class PlainPrinter(_ctx: Context) extends Printer { val parent :: (refined: List[RefinedType @unchecked]) = refinementChain(tp).reverse toTextLocal(parent) ~ "{" ~ Text(refined map toTextRefinement, "; ").close ~ "}" + case tp: RecType => + try { + openRecs = tp :: openRecs + "{" ~ selfRecName(openRecs.length) ~ " => " ~ toTextGlobal(tp.parent) ~ "}" + } + finally openRecs = openRecs.tail case AndType(tp1, tp2) => changePrec(AndPrec) { toText(tp1) ~ " & " ~ toText(tp2) } case OrType(tp1, tp2) => @@ -232,6 +242,10 @@ class PlainPrinter(_ctx: Context) extends Printer { toText(value) case MethodParam(mt, idx) => nameString(mt.paramNames(idx)) + case tp: RecThis => + val idx = openRecs.reverse.indexOf(tp.binder) + if (idx >= 0) selfRecName(idx + 1) + else "{...}.this" // TODO move underlying type to an addendum, e.g. ... z3 ... where z3: ... case tp: RefinedThis => s"${nameString(tp.binder.typeSymbol)}{...}.this" case tp: SkolemType => diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index 37753fe659d4..6944197a15f6 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -174,6 +174,8 @@ object Checking { mapOver(tp) case tp @ RefinedType(parent, name, rinfo) => tp.derivedRefinedType(this(parent), name, this(rinfo, nestedCycleOK, nestedCycleOK)) + case tp: RecType => + tp.rebind(this(tp.parent)) case tp @ TypeRef(pre, name) => try { // A prefix is interesting if it might contain (transitively) a reference @@ -187,7 +189,7 @@ object Checking { case SuperType(thistp, _) => isInteresting(thistp) case AndType(tp1, tp2) => isInteresting(tp1) || isInteresting(tp2) case OrType(tp1, tp2) => isInteresting(tp1) && isInteresting(tp2) - case _: RefinedType => true + case _: RefinedOrRecType => true case _ => false } if (isInteresting(pre)) { @@ -433,12 +435,14 @@ trait Checking { } /** Check that any top-level type arguments in this type are feasible, i.e. that - * their lower bound conforms to their upper cound. If a type argument is + * their lower bound conforms to their upper bound. If a type argument is * infeasible, issue and error and continue with upper bound. */ def checkFeasible(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp match { case tp: RefinedType => tp.derivedRefinedType(tp.parent, tp.refinedName, checkFeasible(tp.refinedInfo, pos, where)) + case tp: RecType => + tp.rebind(tp.parent) case tp @ TypeBounds(lo, hi) if !(lo <:< hi) => ctx.error(d"no type exists between low bound $lo and high bound $hi$where", pos) TypeAlias(hi) diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index b686e6eed4d7..47c3631b8767 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -430,8 +430,8 @@ trait TypeAssigner { val argBindingFns = tparams.map(tparam => tparam.info.bounds .withBindingKind(BindingKind.fromVariance(tparam.variance)) - .internalizeFrom(tparams)) - val bodyFn = body.tpe.internalizeFrom(tparams) + .recursify(tparams)) + val bodyFn = body.tpe.recursify(tparams) tree.withType(TypeApplicationsNewHK.TypeLambda(argBindingFns, bodyFn)) } diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 50204d587667..5f51038a6288 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -913,11 +913,12 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit if ((rsym.is(Method) || rsym.isType) && rsym.allOverriddenSymbols.isEmpty) ctx.error(i"refinement $rsym without matching type in parent $parent", refinement.pos) val rinfo = if (rsym is Accessor) rsym.info.resultType else rsym.info - RefinedType(parent, rsym.name, rt => rinfo.substThis(refineCls, RefinedThis(rt))) + RefinedType(parent, rsym.name, rinfo) // todo later: check that refinement is within bounds } - val res = cpy.RefinedTypeTree(tree)(tpt1, refinements1) withType - (tpt1.tpe /: refinements1)(addRefinement) + val refined = (tpt1.tpe /: refinements1)(addRefinement) + val res = cpy.RefinedTypeTree(tree)(tpt1, refinements1).withType( + RecType.closeOver(rt => refined.substThis(refineCls, RecThis(rt)))) typr.println(i"typed refinement: ${res.tpe}") res } diff --git a/src/dotty/tools/dotc/typer/Variances.scala b/src/dotty/tools/dotc/typer/Variances.scala index bc97301405e1..e88423f98245 100644 --- a/src/dotty/tools/dotc/typer/Variances.scala +++ b/src/dotty/tools/dotc/typer/Variances.scala @@ -77,6 +77,8 @@ object Variances { else flip(varianceInType(lo)(tparam)) & varianceInType(hi)(tparam) case tp @ RefinedType(parent, _, rinfo) => varianceInType(parent)(tparam) & varianceInType(rinfo)(tparam) + case tp: RecType => + varianceInType(tp.parent)(tparam) case tp @ MethodType(_, paramTypes) => flip(varianceInTypes(paramTypes)(tparam)) & varianceInType(tp.resultType)(tparam) case ExprType(restpe) => diff --git a/tests/pos/apply-equiv.scala b/tests/pending/pos/apply-equiv.scala similarity index 100% rename from tests/pos/apply-equiv.scala rename to tests/pending/pos/apply-equiv.scala diff --git a/tests/pos/lookuprefined.scala b/tests/pos/lookuprefined.scala index f7e7f7337f45..9dd2b4abb60d 100644 --- a/tests/pos/lookuprefined.scala +++ b/tests/pos/lookuprefined.scala @@ -2,7 +2,9 @@ class C { type T; type U } trait Test { - val x: (C { type U = T } { type T = String }) # U - val y: String = x + val x1: (C { type U = T; type T = String }) # U + val x2: (C { type U = T } {type T = String }) # U + val y1: String = x1 + val y2: String = x2 } From 7d3b4de8c0b112906d91ba395a7d0b03983f99e0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 27 May 2016 09:54:42 +0200 Subject: [PATCH 010/142] Use recursive types also when unpickling from Scala 2.x --- .../dotc/core/unpickleScala2/Scala2Unpickler.scala | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index 687e9279b078..2663777af870 100644 --- a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -722,13 +722,12 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas val parent = parents.reduceLeft(AndType(_, _)) if (decls.isEmpty) parent else { - def addRefinement(tp: Type, sym: Symbol) = { - def subst(info: Type, rt: RefinedType) = - if (clazz.isClass) info.substThis(clazz.asClass, RefinedThis(rt)) - else info // turns out some symbols read into `clazz` are not classes, not sure why this is the case. - RefinedType(tp, sym.name, subst(sym.info, _)) - } - (parent /: decls.toList)(addRefinement).asInstanceOf[RefinedType] + def subst(info: Type, rt: RecType) = + if (clazz.isClass) info.substThis(clazz.asClass, RecThis(rt)) + else info // turns out some symbols read into `clazz` are not classes, not sure why this is the case. + def addRefinement(tp: Type, sym: Symbol) = RefinedType(tp, sym.name, sym.info) + val refined = (parent /: decls.toList)(addRefinement) + RecType.closeOver(rt => subst(refined, rt)) } case CLASSINFOtpe => val clazz = readSymbolRef() From 96f6bc6f318864e67e9fb7d69100ecb8384c4d8b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 27 May 2016 11:41:56 +0200 Subject: [PATCH 011/142] Abstract type parameters out from type symbols In the new hk scheme, a type parameter can be represented by a refinement without a corresponding symbol. Therefore, we need to disentangle the info inherent in a type parameter from the contents of a type symbol. We achieve this by creating a common super trait "MemerInfo" of Symbol and RefinedType. --- src/dotty/tools/dotc/core/MemberInfo.scala | 21 ++++++++++++ src/dotty/tools/dotc/core/Symbols.scala | 9 +++++- .../tools/dotc/core/TypeApplications.scala | 32 +++++++++++-------- src/dotty/tools/dotc/core/TypeComparer.scala | 8 ++--- src/dotty/tools/dotc/core/Types.scala | 11 +++++-- .../dotc/core/classfile/ClassfileParser.scala | 2 +- src/dotty/tools/dotc/typer/Implicits.scala | 2 +- src/dotty/tools/dotc/typer/Namer.scala | 2 +- src/dotty/tools/dotc/typer/TypeAssigner.scala | 2 +- src/dotty/tools/dotc/typer/Typer.scala | 6 ++-- 10 files changed, 68 insertions(+), 27 deletions(-) create mode 100644 src/dotty/tools/dotc/core/MemberInfo.scala diff --git a/src/dotty/tools/dotc/core/MemberInfo.scala b/src/dotty/tools/dotc/core/MemberInfo.scala new file mode 100644 index 000000000000..3ce74463a826 --- /dev/null +++ b/src/dotty/tools/dotc/core/MemberInfo.scala @@ -0,0 +1,21 @@ +package dotty.tools.dotc.core + +import Names.Name +import Contexts.Context +import Types.Type + +/** The common info associated with a member symbol and a refinement */ +trait MemberInfo { + + def exists(implicit ctx: Context): Boolean + + def memberName(implicit ctx: Context): Name + + def memberInfo(implicit ctx: Context): Type + + def memberInfoAsSeenFrom(pre: Type)(implicit ctx: Context): Type + + def memberVariance(implicit ctx: Context): Int + + +} \ No newline at end of file diff --git a/src/dotty/tools/dotc/core/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala index 1b605e24ffce..1075ee9a1044 100644 --- a/src/dotty/tools/dotc/core/Symbols.scala +++ b/src/dotty/tools/dotc/core/Symbols.scala @@ -367,7 +367,7 @@ object Symbols { * @param coord The coordinates of the symbol (a position or an index) * @param id A unique identifier of the symbol (unique per ContextBase) */ - class Symbol private[Symbols] (val coord: Coord, val id: Int) extends DotClass with printing.Showable { + class Symbol private[Symbols] (val coord: Coord, val id: Int) extends DotClass with MemberInfo with printing.Showable { type ThisName <: Name @@ -489,6 +489,13 @@ object Symbols { */ def pos: Position = if (coord.isPosition) coord.toPosition else NoPosition + // MemberInfo methods + def exists(implicit ctx: Context) = denot.exists + def memberName(implicit ctx: Context): Name = name + def memberInfo(implicit ctx: Context) = denot.info + def memberInfoAsSeenFrom(pre: Type)(implicit ctx: Context) = pre.memberInfo(this) + def memberVariance(implicit ctx: Context) = denot.variance + // -------- Printing -------------------------------------------------------- /** The prefix string to be used when displaying this symbol without denotation */ diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index bd115fefb9de..21be1d4f13c3 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -75,10 +75,10 @@ object TypeApplications { /** Does the variance of `sym1` conform to the variance of `sym2`? * This is the case if the variances are the same or `sym` is nonvariant. */ - def varianceConforms(sym1: TypeSymbol, sym2: TypeSymbol)(implicit ctx: Context) = - sym1.variance == sym2.variance || sym2.variance == 0 + def varianceConforms(sym1: MemberInfo, sym2: MemberInfo)(implicit ctx: Context) = + sym1.memberVariance == sym2.memberVariance || sym2.memberVariance == 0 - def variancesConform(syms1: List[TypeSymbol], syms2: List[TypeSymbol])(implicit ctx: Context) = + def variancesConform(syms1: List[MemberInfo], syms2: List[MemberInfo])(implicit ctx: Context) = syms1.corresponds(syms2)(varianceConforms) /** Extractor for @@ -143,7 +143,7 @@ object TypeApplications { object EtaExpansion { def apply(tycon: TypeRef)(implicit ctx: Context) = { assert(tycon.isEtaExpandable) - tycon.EtaExpand(tycon.typeParams) + tycon.EtaExpand(tycon.typeParamSymbols) } def unapply(tp: Type)(implicit ctx: Context): Option[TypeRef] = { @@ -280,7 +280,7 @@ class TypeApplications(val self: Type) extends AnyVal { * with the bounds on its hk args. See `LambdaAbstract`, where these * types get introduced, and see `isBoundedLambda` below for the test. */ - final def typeParams(implicit ctx: Context): List[TypeSymbol] = /*>|>*/ track("typeParams") /*<|<*/ { + final def typeParams(implicit ctx: Context): List[MemberInfo] = /*>|>*/ track("typeParams") /*<|<*/ { self match { case self: ClassInfo => self.cls.typeParams @@ -309,7 +309,7 @@ class TypeApplications(val self: Type) extends AnyVal { val sym = self.parent.classSymbol if (sym.isLambdaTrait) return sym.typeParams } - self.parent.typeParams.filterNot(_.name == self.refinedName) + self.parent.typeParams.filterNot(_.memberName == self.refinedName) case self: SingletonType => Nil case self: TypeProxy => @@ -319,6 +319,12 @@ class TypeApplications(val self: Type) extends AnyVal { } } + final def typeParamSymbols(implicit ctx: Context): List[TypeSymbol] = { + val tparams = typeParams + assert(tparams.isEmpty || tparams.head.isInstanceOf[Symbol]) + tparams.asInstanceOf[List[TypeSymbol]] + } + /** The named type parameters declared or inherited by this type. * These are all uninstantiated named type parameters of this type or one * of its base types. @@ -498,7 +504,7 @@ class TypeApplications(val self: Type) extends AnyVal { * v1 is compatible with v2, if v1 = v2 or v2 is non-variant. */ def EtaExpand(tparams: List[TypeSymbol])(implicit ctx: Context): Type = { - val tparamsToUse = if (variancesConform(typeParams, tparams)) tparams else typeParams + val tparamsToUse = if (variancesConform(typeParams, tparams)) tparams else typeParamSymbols self.appliedTo(tparams map (_.typeRef)).LambdaAbstract(tparamsToUse) //.ensuring(res => res.EtaReduce =:= self, s"res = $res, core = ${res.EtaReduce}, self = $self, hc = ${res.hashCode}") } @@ -508,7 +514,7 @@ class TypeApplications(val self: Type) extends AnyVal { case self: RefinedType => self.derivedRefinedType(self.parent.EtaExpandCore, self.refinedName, self.refinedInfo) case _ => - self.EtaExpand(self.typeParams) + self.EtaExpand(self.typeParamSymbols) } /** Eta expand if `self` is a (non-lambda) class reference and `bound` is a higher-kinded type */ @@ -621,12 +627,12 @@ class TypeApplications(val self: Type) extends AnyVal { * @param args = `U1, ..., Un` * @param tparams are assumed to be the type parameters of `T`. */ - final def appliedTo(args: List[Type], typParams: List[TypeSymbol])(implicit ctx: Context): Type = { - def matchParams(t: Type, tparams: List[TypeSymbol], args: List[Type])(implicit ctx: Context): Type = args match { + final def appliedTo(args: List[Type], typParams: List[MemberInfo])(implicit ctx: Context): Type = { + def matchParams(t: Type, tparams: List[MemberInfo], args: List[Type])(implicit ctx: Context): Type = args match { case arg :: args1 => try { val tparam :: tparams1 = tparams - matchParams(RefinedType(t, tparam.name, arg.toBounds(tparam)), tparams1, args1) + matchParams(RefinedType(t, tparam.memberName, arg.toBounds(tparam)), tparams1, args1) } catch { case ex: MatchError => println(s"applied type mismatch: $self $args, typeParams = $typParams") // !!! DEBUG @@ -667,11 +673,11 @@ class TypeApplications(val self: Type) extends AnyVal { /** Turn this type, which is used as an argument for * type parameter `tparam`, into a TypeBounds RHS */ - final def toBounds(tparam: Symbol)(implicit ctx: Context): TypeBounds = self match { + final def toBounds(tparam: MemberInfo)(implicit ctx: Context): TypeBounds = self match { case self: TypeBounds => // this can happen for wildcard args self case _ => - val v = tparam.variance + val v = tparam.memberVariance /* Not neeeded. if (v > 0 && !(tparam is Local) && !(tparam is ExpandedTypeParam)) TypeBounds.upper(self) else if (v < 0 && !(tparam is Local) && !(tparam is ExpandedTypeParam)) TypeBounds.lower(self) diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 9909c9e8ac8c..cadeee4f2e5b 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -534,7 +534,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { * - the type parameters of `B` match one-by-one the variances of `tparams`, * - `B` satisfies predicate `p`. */ - private def testLifted(tp1: Type, tp2: Type, tparams: List[TypeSymbol], p: Type => Boolean): Boolean = { + private def testLifted(tp1: Type, tp2: Type, tparams: List[MemberInfo], p: Type => Boolean): Boolean = { val classBounds = tp2.member(tpnme.hkApply).info.classSymbols def recur(bcs: List[ClassSymbol]): Boolean = bcs match { case bc :: bcs1 => @@ -647,7 +647,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { } } - /** Replace any top-level recursive type `{ z => T }` in `tp` with + /** Replace any top-level recursive type `{ z => T }` in `tp` with * `[z := anchor]T`. */ private def fixRecs(anchor: SingletonType, tp: Type): Type = { @@ -1117,8 +1117,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { * in where we allow which interpretation. */ private def liftIfHK(tp1: Type, tp2: Type, op: (Type, Type) => Type) = { - val tparams1 = tp1.typeParams - val tparams2 = tp2.typeParams + val tparams1 = tp1.typeParamSymbols // TODO revise for new hk scheme + val tparams2 = tp2.typeParamSymbols def onlyNamed(tparams: List[TypeSymbol]) = tparams.forall(!_.is(ExpandedName)) if (tparams1.isEmpty || tparams2.isEmpty || onlyNamed(tparams1) && onlyNamed(tparams2)) op(tp1, tp2) diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 5252a9149a6c..15cd3272c79c 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -869,7 +869,7 @@ object Types { def isParamName = tp.classSymbol.typeParams.exists(_.name == tp.refinedName) if (refinementOK || isParamName) tp.underlying.underlyingClassRef(refinementOK) else NoType - case tp: RecType if refinementOK => tp.parent + case tp: RecType if refinementOK => tp.parent case _ => NoType } @@ -2051,7 +2051,7 @@ object Types { * given the refined type itself. */ abstract case class RefinedType(private var myParent: Type, refinedName: Name, private var myRefinedInfo: Type) - extends RefinedOrRecType with BindingType { + extends RefinedOrRecType with BindingType with MemberInfo { final def parent = myParent final def refinedInfo = myRefinedInfo @@ -2090,6 +2090,13 @@ object Types { if (parent.member(refinedName).exists) derivedRefinedType(parent, refinedName, refinedInfo) else parent + // MemberInfo methods + def exists(implicit ctx: Context) = true + def memberName(implicit ctx: Context) = refinedName + def memberInfo(implicit ctx: Context) = refinedInfo + def memberInfoAsSeenFrom(pre: Type)(implicit ctx: Context) = refinedInfo + def memberVariance(implicit ctx: Context) = BindingKind.toVariance(refinedInfo.bounds.bindingKind) + override def equals(that: Any) = that match { case that: RefinedType => this.parent == that.parent && diff --git a/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index f7a69aa53f28..2d7b037b1848 100644 --- a/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -272,7 +272,7 @@ class ClassfileParser( if (sig(index) == '<') { accept('<') var tp1: Type = tp - var formals = tp.typeParams + var formals = tp.typeParamSymbols while (sig(index) != '>') { sig(index) match { case variance @ ('+' | '-' | '*') => diff --git a/src/dotty/tools/dotc/typer/Implicits.scala b/src/dotty/tools/dotc/typer/Implicits.scala index 7de40294dfcb..e21a08fb8a91 100644 --- a/src/dotty/tools/dotc/typer/Implicits.scala +++ b/src/dotty/tools/dotc/typer/Implicits.scala @@ -325,7 +325,7 @@ trait ImplicitRunInfo { self: RunInfo => } def addParentScope(parent: TypeRef): Unit = { iscopeRefs(parent) foreach addRef - for (param <- parent.typeParams) + for (param <- parent.typeParamSymbols) comps ++= iscopeRefs(tp.member(param.name).info) } val companion = cls.companionModule diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index a8f3b89183cf..8437b651c1bf 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -989,7 +989,7 @@ class Namer { typer: Typer => if (args.nonEmpty) { val tycon = tp.withoutArgs(args) val tycon1 = this(tycon) - val tparams = tycon.typeParams + val tparams = tycon.typeParamSymbols val args1 = if (args.length == tparams.length) etaExpandIfHK(tparams, args) else args if ((tycon1 eq tycon) && (args1 eq args)) tp else tycon1.appliedTo(args1) } else mapOver(tp) diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index 47c3631b8767..d41a9dc64893 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -409,7 +409,7 @@ trait TypeAssigner { def refineNamed(tycon: Type, arg: Tree) = arg match { case ast.Trees.NamedArg(name, argtpt) => // Dotty deviation: importing ast.Trees._ and matching on NamedArg gives a cyclic ref error - val tparam = tparams.find(_.name == name) match { + val tparam = tparams.find(_.memberName == name) match { case Some(tparam) => tparam case none => ntparams.find(_.name == name).getOrElse(NoSymbol) } diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 5f51038a6288..1c19049ca8b6 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -939,14 +939,14 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit ctx.error(d"wrong number of type arguments for ${tpt1.tpe}, should be ${tparams.length}", tree.pos) args = args.take(tparams.length) } - def typedArg(arg: untpd.Tree, tparam: Symbol) = { + def typedArg(arg: untpd.Tree, tparam: MemberInfo) = { val (desugaredArg, argPt) = if (ctx.mode is Mode.Pattern) - (if (isVarPattern(arg)) desugar.patternVar(arg) else arg, tparam.info) + (if (isVarPattern(arg)) desugar.patternVar(arg) else arg, tparam.memberInfo) else (arg, WildcardType) val arg1 = typed(desugaredArg, argPt) - adaptTypeArg(arg1, tparam.info) + adaptTypeArg(arg1, tparam.memberInfo) } args.zipWithConserve(tparams)(typedArg(_, _)).asInstanceOf[List[Tree]] } From 4b912dbfb0a47d0136d90f67ba2060efd83bb30e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 27 May 2016 13:16:02 +0200 Subject: [PATCH 012/142] Fix toVariance in MemberBinding --- .../dotc/core/{MemberInfo.scala => MemberBinding.scala} | 0 src/dotty/tools/dotc/core/Types.scala | 5 ++++- 2 files changed, 4 insertions(+), 1 deletion(-) rename src/dotty/tools/dotc/core/{MemberInfo.scala => MemberBinding.scala} (100%) diff --git a/src/dotty/tools/dotc/core/MemberInfo.scala b/src/dotty/tools/dotc/core/MemberBinding.scala similarity index 100% rename from src/dotty/tools/dotc/core/MemberInfo.scala rename to src/dotty/tools/dotc/core/MemberBinding.scala diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 15cd3272c79c..fca1414ff6a8 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -3127,7 +3127,10 @@ object Types { object BindingKind { def fromVariance(v: Int): BindingKind = new BindingKind((v + NonvariantBinding.n).toByte) - def toVariance(bk: BindingKind): Int = bk.n + def toVariance(bk: BindingKind): Int = { + assert(bk.n != 0) + bk.n - NonvariantBinding.n + } } // ----- Annotated and Import types ----------------------------------------------- From 6b9e7d8ccb6dfe7596661e129663cf8ae1001794 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 27 May 2016 13:27:38 +0200 Subject: [PATCH 013/142] Disable checkInst in RecType It can give false negatives. --- src/dotty/tools/dotc/core/Types.scala | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index fca1414ff6a8..6cdda8a46be1 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -2188,16 +2188,20 @@ object Types { } object RecType { + /* Note: this might well fail for nested Recs. + * Failing scenario: Rebind a nest rec, creates a new rec + * but it still has RecThis references to the outer rec. def checkInst(tp: Type)(implicit ctx: Context): tp.type = { var binders: List[RecType] = Nil tp.foreachPart { case rt: RecType => binders = rt :: binders - case rt: RecThis => assert(binders contains rt.binder, tp) + case rt: RecThis => assert(binders contains rt.binder) case _ => } tp } - def apply(parentExp: RecType => Type)(implicit ctx: Context): RecType = checkInst { + */ + def apply(parentExp: RecType => Type)(implicit ctx: Context): RecType = { var rt = new RecType(parentExp) rt.parent match { case rt2: RecType => @@ -2206,7 +2210,7 @@ object Types { } unique(rt) } - def closeOver(parentExp: RecType => Type)(implicit ctx: Context) = checkInst { + def closeOver(parentExp: RecType => Type)(implicit ctx: Context) = { val rt = this(parentExp) //val self = RecThis(rt) def isSelfRef(t: Type) = t match { From 67b4917a72b763546b3f361e0579a692a3b9c987 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 27 May 2016 13:28:12 +0200 Subject: [PATCH 014/142] Simplify RecType.closeOver --- src/dotty/tools/dotc/core/Types.scala | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 6cdda8a46be1..a11bde296836 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -2212,12 +2212,7 @@ object Types { } def closeOver(parentExp: RecType => Type)(implicit ctx: Context) = { val rt = this(parentExp) - //val self = RecThis(rt) - def isSelfRef(t: Type) = t match { - case RecThis(binder) => binder eq rt - case _ => false - } - if (rt.existsPart(isSelfRef)) rt else rt.parent + if (rt.isReferredToBy(rt.parent)) rt else rt.parent } } From 7c59785b07d992f75b9fb51b45ec82b003e4d69b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 27 May 2016 13:29:28 +0200 Subject: [PATCH 015/142] Rename MemberInfo -> MemberBinding Also change MemberInfo.exists to MemberBinding.isTypeParam. --- src/dotty/tools/dotc/core/MemberBinding.scala | 27 ++++++++++---- src/dotty/tools/dotc/core/Symbols.scala | 6 +-- .../tools/dotc/core/TypeApplications.scala | 12 +++--- src/dotty/tools/dotc/core/TypeComparer.scala | 37 +++++++++++-------- src/dotty/tools/dotc/core/Types.scala | 9 +++-- src/dotty/tools/dotc/typer/TypeAssigner.scala | 2 +- src/dotty/tools/dotc/typer/Typer.scala | 2 +- 7 files changed, 59 insertions(+), 36 deletions(-) diff --git a/src/dotty/tools/dotc/core/MemberBinding.scala b/src/dotty/tools/dotc/core/MemberBinding.scala index 3ce74463a826..38a3b071ec8a 100644 --- a/src/dotty/tools/dotc/core/MemberBinding.scala +++ b/src/dotty/tools/dotc/core/MemberBinding.scala @@ -4,18 +4,31 @@ import Names.Name import Contexts.Context import Types.Type -/** The common info associated with a member symbol and a refinement */ -trait MemberInfo { - - def exists(implicit ctx: Context): Boolean - +/** A common super trait of Symbol and Refinement. + * Used to capture the attributes of type parameters + * which can be implemented as either symbols or refinements. + */ +trait MemberBinding { + + /** Does this binding represent a type parameter? + * Only in that case the rest of the binding's methods are significant. + */ + def isTypeParam(implicit ctx: Context): Boolean + + /** The name of the member */ def memberName(implicit ctx: Context): Name + /** The info of the member */ def memberInfo(implicit ctx: Context): Type + /** The info of the member as seen from a prefix type. + * This can be different from `memberInfo` if the binding + * is a type symbol of a class. + */ def memberInfoAsSeenFrom(pre: Type)(implicit ctx: Context): Type + /** The variance of the type parameter + * @pre: isTypeParam = true + */ def memberVariance(implicit ctx: Context): Int - - } \ No newline at end of file diff --git a/src/dotty/tools/dotc/core/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala index 1075ee9a1044..5c41dc4c605e 100644 --- a/src/dotty/tools/dotc/core/Symbols.scala +++ b/src/dotty/tools/dotc/core/Symbols.scala @@ -367,7 +367,7 @@ object Symbols { * @param coord The coordinates of the symbol (a position or an index) * @param id A unique identifier of the symbol (unique per ContextBase) */ - class Symbol private[Symbols] (val coord: Coord, val id: Int) extends DotClass with MemberInfo with printing.Showable { + class Symbol private[Symbols] (val coord: Coord, val id: Int) extends DotClass with MemberBinding with printing.Showable { type ThisName <: Name @@ -489,8 +489,8 @@ object Symbols { */ def pos: Position = if (coord.isPosition) coord.toPosition else NoPosition - // MemberInfo methods - def exists(implicit ctx: Context) = denot.exists + // MemberBinding methods + def isTypeParam(implicit ctx: Context) = denot.is(TypeParam) def memberName(implicit ctx: Context): Name = name def memberInfo(implicit ctx: Context) = denot.info def memberInfoAsSeenFrom(pre: Type)(implicit ctx: Context) = pre.memberInfo(this) diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index 21be1d4f13c3..d9521b3c8a9d 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -75,10 +75,10 @@ object TypeApplications { /** Does the variance of `sym1` conform to the variance of `sym2`? * This is the case if the variances are the same or `sym` is nonvariant. */ - def varianceConforms(sym1: MemberInfo, sym2: MemberInfo)(implicit ctx: Context) = + def varianceConforms(sym1: MemberBinding, sym2: MemberBinding)(implicit ctx: Context) = sym1.memberVariance == sym2.memberVariance || sym2.memberVariance == 0 - def variancesConform(syms1: List[MemberInfo], syms2: List[MemberInfo])(implicit ctx: Context) = + def variancesConform(syms1: List[MemberBinding], syms2: List[MemberBinding])(implicit ctx: Context) = syms1.corresponds(syms2)(varianceConforms) /** Extractor for @@ -280,7 +280,7 @@ class TypeApplications(val self: Type) extends AnyVal { * with the bounds on its hk args. See `LambdaAbstract`, where these * types get introduced, and see `isBoundedLambda` below for the test. */ - final def typeParams(implicit ctx: Context): List[MemberInfo] = /*>|>*/ track("typeParams") /*<|<*/ { + final def typeParams(implicit ctx: Context): List[MemberBinding] = /*>|>*/ track("typeParams") /*<|<*/ { self match { case self: ClassInfo => self.cls.typeParams @@ -627,8 +627,8 @@ class TypeApplications(val self: Type) extends AnyVal { * @param args = `U1, ..., Un` * @param tparams are assumed to be the type parameters of `T`. */ - final def appliedTo(args: List[Type], typParams: List[MemberInfo])(implicit ctx: Context): Type = { - def matchParams(t: Type, tparams: List[MemberInfo], args: List[Type])(implicit ctx: Context): Type = args match { + final def appliedTo(args: List[Type], typParams: List[MemberBinding])(implicit ctx: Context): Type = { + def matchParams(t: Type, tparams: List[MemberBinding], args: List[Type])(implicit ctx: Context): Type = args match { case arg :: args1 => try { val tparam :: tparams1 = tparams @@ -673,7 +673,7 @@ class TypeApplications(val self: Type) extends AnyVal { /** Turn this type, which is used as an argument for * type parameter `tparam`, into a TypeBounds RHS */ - final def toBounds(tparam: MemberInfo)(implicit ctx: Context): TypeBounds = self match { + final def toBounds(tparam: MemberBinding)(implicit ctx: Context): TypeBounds = self match { case self: TypeBounds => // this can happen for wildcard args self case _ => diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index cadeee4f2e5b..58c6bea3a4d3 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -534,7 +534,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { * - the type parameters of `B` match one-by-one the variances of `tparams`, * - `B` satisfies predicate `p`. */ - private def testLifted(tp1: Type, tp2: Type, tparams: List[MemberInfo], p: Type => Boolean): Boolean = { + private def testLifted(tp1: Type, tp2: Type, tparams: List[MemberBinding], p: Type => Boolean): Boolean = { val classBounds = tp2.member(tpnme.hkApply).info.classSymbols def recur(bcs: List[ClassSymbol]): Boolean = bcs match { case bc :: bcs1 => @@ -726,24 +726,31 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { val rebindNeeded = tp2.refinementRefersToThis val base = if (rebindNeeded) ensureStableSingleton(tp1) else tp1 val rinfo2 = if (rebindNeeded) tp2.refinedInfo.substRefinedThis(tp2, base) else tp2.refinedInfo + val mbr = base.member(name) + def qualifies(m: SingleDenotation) = isSubType(m.info, rinfo2) - def memberMatches(mbr: Denotation): Boolean = mbr match { // inlined hasAltWith for performance + + def memberMatches: 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 } - // val foo: C - // foo.type <: C { type T = foo.T } - rinfo2 match { - case rinfo2: TypeAlias => - !defn.isBottomType(base.widen) && (base select name) =:= rinfo2.alias - case _ => false - } - } + + // special case for situations like: + // class C { type T } + // val foo: C + // foo.type <: C { type T = foo.T } + def selfReferentialMatch = tp1.isInstanceOf[SingletonType] && { + rinfo2 match { + case rinfo2: TypeAlias => + !defn.isBottomType(base.widen) && (base select name) =:= rinfo2.alias + case _ => false + } + } + + def varianceMatches = true // TODO: fill in + + /*>|>*/ ctx.traceIndented(i"hasMatchingMember($base . $name :? ${tp2.refinedInfo}) ${mbr.info.show} $rinfo2", subtyping) /*<|<*/ { + (memberMatches || selfReferentialMatch) && varianceMatches } } diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index a11bde296836..277bc11f93e5 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -2051,7 +2051,7 @@ object Types { * given the refined type itself. */ abstract case class RefinedType(private var myParent: Type, refinedName: Name, private var myRefinedInfo: Type) - extends RefinedOrRecType with BindingType with MemberInfo { + extends RefinedOrRecType with BindingType with MemberBinding { final def parent = myParent final def refinedInfo = myRefinedInfo @@ -2090,8 +2090,11 @@ object Types { if (parent.member(refinedName).exists) derivedRefinedType(parent, refinedName, refinedInfo) else parent - // MemberInfo methods - def exists(implicit ctx: Context) = true + // MemberBinding methods + def isTypeParam(implicit ctx: Context) = refinedInfo match { + case tp: TypeBounds => tp.isBinding + case _ => false + } def memberName(implicit ctx: Context) = refinedName def memberInfo(implicit ctx: Context) = refinedInfo def memberInfoAsSeenFrom(pre: Type)(implicit ctx: Context) = refinedInfo diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index d41a9dc64893..b7e2fd83271f 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -413,7 +413,7 @@ trait TypeAssigner { case Some(tparam) => tparam case none => ntparams.find(_.name == name).getOrElse(NoSymbol) } - if (tparam.exists) RefinedType(tycon, name, argtpt.tpe.toBounds(tparam)) + if (tparam.isTypeParam) RefinedType(tycon, name, argtpt.tpe.toBounds(tparam)) else errorType(i"$tycon does not have a parameter or abstract type member named $name", arg.pos) case _ => errorType(s"named and positional type arguments may not be mixed", arg.pos) diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 1c19049ca8b6..7d1f229fe035 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -939,7 +939,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit ctx.error(d"wrong number of type arguments for ${tpt1.tpe}, should be ${tparams.length}", tree.pos) args = args.take(tparams.length) } - def typedArg(arg: untpd.Tree, tparam: MemberInfo) = { + def typedArg(arg: untpd.Tree, tparam: MemberBinding) = { val (desugaredArg, argPt) = if (ctx.mode is Mode.Pattern) (if (isVarPattern(arg)) desugar.patternVar(arg) else arg, tparam.memberInfo) From 7a92cd55c03a91ff8f39853a74c3f5a3fbf8bc73 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 27 May 2016 15:19:01 +0200 Subject: [PATCH 016/142] Add printing of type lambda trees Also fix printing of variances in typedefs and params; they were suppressed before. --- src/dotty/tools/dotc/core/Flags.scala | 4 +++- src/dotty/tools/dotc/printing/RefinedPrinter.scala | 11 +++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index cd660aa46216..a078c9fd0b48 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -436,7 +436,9 @@ object Flags { /** Flags representing modifiers that can appear in trees */ final val ModifierFlags = - SourceModifierFlags | Module | Param | Synthetic | Package | Local | commonFlags(Mutable) + SourceModifierFlags | Module | Param | Synthetic | Package | Local | + CovariantOrOuter | ContravariantOrLabel | + commonFlags(Mutable) // | Trait is subsumed by commonFlags(Lazy) from SourceModifierFlags assert(ModifierFlags.isTermFlags && ModifierFlags.isTypeFlags) diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index e0fd4790019f..87f5c23c6067 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -207,7 +207,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { import untpd.{modsDeco => _, _} - /** Print modifiers form symbols if tree has type, overriding the untpd behavior. */ + /** Print modifiers from symbols if tree has type, overriding the untpd behavior. */ implicit def modsDeco(mdef: untpd.MemberDef)(implicit ctx: Context): untpd.ModsDeco = tpd.modsDeco(mdef.asInstanceOf[tpd.MemberDef]).asInstanceOf[untpd.ModsDeco] @@ -264,6 +264,11 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { Text(mods.annotations.map(annotText), " ") ~~ flagsText ~~ (kw provided !suppressKw) } + def varianceText(mods: untpd.Modifiers) = + if (mods is Covariant) "+" + else if (mods is Contravariant) "-" + else "" + def argText(arg: Tree): Text = arg match { case arg: TypeBoundsTree => "_" ~ toTextGlobal(arg) case arg: TypeTree => @@ -398,6 +403,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { toTextLocal(tpt) ~ " " ~ blockText(refines) case AppliedTypeTree(tpt, args) => toTextLocal(tpt) ~ "[" ~ Text(args map argText, ", ") ~ "]" + case TypeLambdaTree(tparams, body) => + tparamsText(tparams) ~ " -> " ~ toText(body) case ByNameTypeTree(tpt) => "=> " ~ toTextLocal(tpt) case TypeBoundsTree(lo, hi) => @@ -431,7 +438,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case tree @ TypeDef(name, rhs) => def typeDefText(rhsText: Text) = dclTextOr { - modText(tree.mods, "type") ~~ nameIdText(tree) ~ + modText(tree.mods, "type") ~~ (varianceText(tree.mods) ~ nameIdText(tree)) ~ withEnclosingDef(tree) { val rhsText1 = if (tree.hasType) toText(tree.symbol.info) else rhsText tparamsText(tree.tparams) ~ rhsText1 From c9daca05337ab2852c4dd2ca36dc7c4d6eb2c32e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 27 May 2016 18:49:06 +0200 Subject: [PATCH 017/142] Replace MemberBinding # memberInfo by memberBounds --- src/dotty/tools/dotc/core/MemberBinding.scala | 6 +++--- src/dotty/tools/dotc/core/Symbols.scala | 4 ++-- src/dotty/tools/dotc/core/Types.scala | 4 ++-- src/dotty/tools/dotc/typer/Typer.scala | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/dotty/tools/dotc/core/MemberBinding.scala b/src/dotty/tools/dotc/core/MemberBinding.scala index 38a3b071ec8a..6f081c542508 100644 --- a/src/dotty/tools/dotc/core/MemberBinding.scala +++ b/src/dotty/tools/dotc/core/MemberBinding.scala @@ -2,7 +2,7 @@ package dotty.tools.dotc.core import Names.Name import Contexts.Context -import Types.Type +import Types.{Type, TypeBounds} /** A common super trait of Symbol and Refinement. * Used to capture the attributes of type parameters @@ -19,13 +19,13 @@ trait MemberBinding { def memberName(implicit ctx: Context): Name /** The info of the member */ - def memberInfo(implicit ctx: Context): Type + def memberBounds(implicit ctx: Context): TypeBounds /** The info of the member as seen from a prefix type. * This can be different from `memberInfo` if the binding * is a type symbol of a class. */ - def memberInfoAsSeenFrom(pre: Type)(implicit ctx: Context): Type + def memberBoundsAsSeenFrom(pre: Type)(implicit ctx: Context): TypeBounds /** The variance of the type parameter * @pre: isTypeParam = true diff --git a/src/dotty/tools/dotc/core/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala index 5c41dc4c605e..c7eb548124a3 100644 --- a/src/dotty/tools/dotc/core/Symbols.scala +++ b/src/dotty/tools/dotc/core/Symbols.scala @@ -492,8 +492,8 @@ object Symbols { // MemberBinding methods def isTypeParam(implicit ctx: Context) = denot.is(TypeParam) def memberName(implicit ctx: Context): Name = name - def memberInfo(implicit ctx: Context) = denot.info - def memberInfoAsSeenFrom(pre: Type)(implicit ctx: Context) = pre.memberInfo(this) + def memberBounds(implicit ctx: Context) = denot.info.bounds + def memberBoundsAsSeenFrom(pre: Type)(implicit ctx: Context) = pre.memberInfo(this).bounds def memberVariance(implicit ctx: Context) = denot.variance // -------- Printing -------------------------------------------------------- diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 277bc11f93e5..42abc425110b 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -2096,8 +2096,8 @@ object Types { case _ => false } def memberName(implicit ctx: Context) = refinedName - def memberInfo(implicit ctx: Context) = refinedInfo - def memberInfoAsSeenFrom(pre: Type)(implicit ctx: Context) = refinedInfo + def memberBounds(implicit ctx: Context) = refinedInfo.bounds + def memberBoundsAsSeenFrom(pre: Type)(implicit ctx: Context) = memberBounds def memberVariance(implicit ctx: Context) = BindingKind.toVariance(refinedInfo.bounds.bindingKind) override def equals(that: Any) = that match { diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 7d1f229fe035..e7ee21a05428 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -942,11 +942,11 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedArg(arg: untpd.Tree, tparam: MemberBinding) = { val (desugaredArg, argPt) = if (ctx.mode is Mode.Pattern) - (if (isVarPattern(arg)) desugar.patternVar(arg) else arg, tparam.memberInfo) + (if (isVarPattern(arg)) desugar.patternVar(arg) else arg, tparam.memberBounds) else (arg, WildcardType) val arg1 = typed(desugaredArg, argPt) - adaptTypeArg(arg1, tparam.memberInfo) + adaptTypeArg(arg1, tparam.memberBounds) } args.zipWithConserve(tparams)(typedArg(_, _)).asInstanceOf[List[Tree]] } From ddc3a8107e60fd0cc40d3840ee8777e013a42c34 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 27 May 2016 23:15:14 +0200 Subject: [PATCH 018/142] Fix printing of type lambdas The previous scheme accidentally printed