From 07693204b167d745a3d18f7c4ac9b4cc0ee2551d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 29 Jul 2017 11:49:11 +0200 Subject: [PATCH 01/18] Refine variance for NamedTypes in TypeMaps --- compiler/src/dotty/tools/dotc/core/Types.scala | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 40fbc80f85f0..52a374e63afb 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3713,8 +3713,19 @@ object Types { tp match { case tp: NamedType => if (stopAtStatic && tp.symbol.isStatic) tp - else derivedSelect(tp, this(tp.prefix)) - + else { + val saved = variance + variance = variance max 0 + // A prefix is never contravariant. Even if say `p.A` is used in a contravariant + // context, we cannot assume contravariance for `p` because `p`'s lower + // bound might not have a binding for `A` (e.g. the lower bound could be `Nothing`). + // By contrast, covariance does translate to the prefix, since we have that + // if `p <: q` then `p.A <: q.A`, and well-formedness requires that `A` is a member + // of `p`'s upper bound. + val prefix1 = this(tp.prefix) + variance = saved + derivedSelect(tp, prefix1) + } case _: ThisType | _: BoundType | NoPrefix => tp From c6c4a174729f6ea6512862a3cf08d317d9406a79 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 29 Jul 2017 12:38:00 +0200 Subject: [PATCH 02/18] Treat variances in type accumulators and type maps the same Bonus point: No need to override applyPrefix in variances accumulator. This means we have reduced three different ways to compute the current variance to one. --- compiler/src/dotty/tools/dotc/core/Types.scala | 4 ++-- compiler/src/dotty/tools/dotc/typer/Inferencing.scala | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 52a374e63afb..15d1f675205b 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3924,9 +3924,9 @@ object Types { protected var variance = 1 - protected def applyToPrefix(x: T, tp: NamedType) = { + protected final def applyToPrefix(x: T, tp: NamedType) = { val saved = variance - variance = 0 + variance = variance max 0 // see remark on NamedType case in TypeMap val result = this(x, tp.prefix) variance = saved result diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index b2c8fd0595a0..99a6e5373fa9 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -319,8 +319,6 @@ object Inferencing { case _ => foldOver(vmap, t) } - override def applyToPrefix(vmap: VarianceMap, t: NamedType) = - apply(vmap, t.prefix) } /** Include in `vmap` type variables occurring in the constraints of type variables From 9e7e96222c9a82bb79c325d8a747e4c45d7e41d7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 29 Jul 2017 19:10:52 +0200 Subject: [PATCH 03/18] Refine determination of prefix in typedIdent --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 76bc9360f6aa..a389cf77e98c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -268,7 +268,20 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit if (qualifies(defDenot)) { val found = if (isSelfDenot(defDenot)) curOwner.enclosingClass.thisType - else curOwner.thisType.select(name, defDenot) + else { + val effectiveOwner = + if (curOwner.isTerm && defDenot.symbol.isType) + // Don't mix NoPrefix and thisType prefixes, since type comparer + // would not detect types to be compatible. Note: If we replace the + // 2nd condition by `defDenot.symbol.maybeOwner.isType` we get lots + // of failures in the `tastyBootstrap` test. Trying to compile these + // files in isolation works though. + // TODO: Investigate why that happens. + defDenot.symbol.owner + else + curOwner + effectiveOwner.thisType.select(name, defDenot) + } if (!(curOwner is Package) || isDefinedInCurrentUnit(defDenot)) result = checkNewOrShadowed(found, definition) // no need to go further out, we found highest prec entry else { From c924cdd38b8067306391c2c4fb32efea575e7aa7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 25 Jul 2017 20:18:42 +0200 Subject: [PATCH 04/18] Pickle LazyRefs Need to keep cycle breakers --- compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala | 5 ++++- compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala | 1 + compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala | 5 +++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index cd0d427f3510..3b04d6d67eb7 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -164,6 +164,7 @@ Standard-Section: "ASTs" TopLevelStat* BIND Length boundName_NameRef bounds_Type // for type-variables defined in a type pattern BYNAMEtype underlying_Type + LAZYref underlying_Type POLYtype Length result_Type NamesTypes METHODtype Length result_Type NamesTypes // needed for refinements TYPELAMBDAtype Length result_Type NamesTypes // variance encoded in front of name: +/-/(nothing) @@ -256,7 +257,7 @@ object TastyFormat { final val OBJECTCLASS = 40 final val SIGNED = 63 - + final val firstInternalTag = 64 final val IMPLMETH = 64 @@ -322,6 +323,7 @@ object TastyFormat { final val PROTECTEDqualified = 105 final val RECtype = 106 final val SINGLETONtpt = 107 + final val LAZYref = 108 final val IDENT = 112 final val IDENTtpt = 113 @@ -512,6 +514,7 @@ object TastyFormat { case DOUBLEconst => "DOUBLEconst" case STRINGconst => "STRINGconst" case RECtype => "RECtype" + case LAZYref => "LAZYref" case IDENT => "IDENT" case IDENTtpt => "IDENTtpt" diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 338a395ab685..885a76e3f763 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -246,6 +246,7 @@ class TreePickler(pickler: TastyPickler) { case tpe: ParamRef => assert(pickleParamRef(tpe), s"orphan parameter reference: $tpe") case tpe: LazyRef => + writeByte(LAZYref) pickleType(tpe.ref) } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index d1f800c9926f..8b6fa72529f3 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -293,6 +293,11 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi RecType(rt => registeringType(rt, readType())) case RECthis => RecThis(readTypeRef().asInstanceOf[RecType]) + case LAZYref => + val rdr = fork + def readUnderlying() = rdr.readType() + skipTree() + LazyRef(readUnderlying) case SHARED => val ref = readAddr() typeAtAddr.getOrElseUpdate(ref, forkAt(ref).readType()) From 1f42b150ef39b36c8f64a26330894fcadbdf256c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 25 Jul 2017 20:22:09 +0200 Subject: [PATCH 05/18] Be more careful reading applied types from Tasty Same principle as reading from Scala2 pickles applied = in the face of F-bounded polymorphism we cannot assume that the type parameters are known when we unpickle a type application. --- .../src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 8b6fa72529f3..5eea9b769aa7 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -996,7 +996,12 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi val refinements = readStats(refineCls, end)(localContext(refineCls)) RefinedTypeTree(parent, refinements, refineCls) case APPLIEDtpt => - AppliedTypeTree(readTpt(), until(end)(readTpt())) + // If we do directly a tpd.AppliedType tree we might get a + // wrong number of arguments in some scenarios reading F-bounded + // types. This came up in #137 of collection strawman. + val tycon = readTpt() + val args = until(end)(readTpt()) + untpd.AppliedTypeTree(tycon, args).withType(tycon.tpe.safeAppliedTo(args.tpes)) case ANDtpt => val tpt1 = readTpt() val tpt2 = readTpt() From 31ef20646bf05d0f0d492fbd7c8b46d426463e74 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 25 Jul 2017 20:22:49 +0200 Subject: [PATCH 06/18] Survive cyclic reference errors when printing an error message. --- .../tools/dotc/reporting/diagnostic/messages.scala | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index 3b398d23661c..757da5d83bc7 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -23,6 +23,7 @@ import dotty.tools.dotc.ast.Trees import dotty.tools.dotc.ast.untpd.Modifiers import dotty.tools.dotc.core.Flags.{FlagSet, Mutable} import dotty.tools.dotc.core.SymDenotations.SymDenotation +import scala.util.control.NonFatal object messages { @@ -708,10 +709,15 @@ object messages { private val actualArgString = actual.map(_.show).mkString("[", ", ", "]") - private val prettyName = fntpe.termSymbol match { - case NoSymbol => fntpe.show - case symbol => symbol.showFullName - } + private val prettyName = + try + fntpe.termSymbol match { + case NoSymbol => fntpe.show + case symbol => symbol.showFullName + } + catch { + case NonFatal(ex) => fntpe.show + } val msg = hl"""|${NoColor(msgPrefix)} type arguments for $prettyName$expectedArgString From 01cc2986a3ac9dcca5c922dc8a4cc5b3724ccf1f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 26 Jul 2017 09:53:36 +0200 Subject: [PATCH 07/18] Make LazyRef completers take a context This avoids memory leaks in unpicklers, where lazy refs don't capture the context anymore. Other lazyrefs still need to capture context, though. --- .../tools/dotc/core/TypeApplications.scala | 2 +- compiler/src/dotty/tools/dotc/core/Types.scala | 18 ++++++++---------- .../tools/dotc/core/tasty/TreeUnpickler.scala | 3 +-- .../src/dotty/tools/dotc/typer/Checking.scala | 2 +- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index fb2300ff1829..9397eeb09d7b 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -443,7 +443,7 @@ class TypeApplications(val self: Type) extends AnyVal { case dealiased: TypeBounds => dealiased.derivedTypeBounds(dealiased.lo.appliedTo(args), dealiased.hi.appliedTo(args)) case dealiased: LazyRef => - LazyRef(() => dealiased.ref.appliedTo(args)) + LazyRef(c => dealiased.ref(c).appliedTo(args)) case dealiased: WildcardType => WildcardType(dealiased.optBounds.appliedTo(args).bounds) case dealiased: TypeRef if dealiased.symbol == defn.NothingClass => diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 15d1f675205b..3dd56aa1cebf 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -2149,25 +2149,23 @@ object Types { } } - case class LazyRef(private var refFn: () => Type) extends UncachedProxyType with ValueType { + case class LazyRef(private var refFn: Context => Type) extends UncachedProxyType with ValueType { private var myRef: Type = null private var computed = false - def ref = { + def ref(implicit ctx: Context) = { if (computed) assert(myRef != null) else { computed = true - myRef = refFn() + myRef = refFn(ctx) + refFn = null } myRef } def evaluating = computed && myRef == null override def underlying(implicit ctx: Context) = ref - override def toString = s"LazyRef($ref)" - override def equals(other: Any) = other match { - case other: LazyRef => this.ref.equals(other.ref) - case _ => false - } - override def hashCode = ref.hashCode + 37 + override def toString = s"LazyRef(...)" + override def equals(other: Any) = this eq other.asInstanceOf[AnyRef] + override def hashCode = System.identityHashCode(this) } // --- Refined Type and RecType ------------------------------------------------ @@ -3779,7 +3777,7 @@ object Types { derivedSuperType(tp, this(thistp), this(supertp)) case tp: LazyRef => - LazyRef(() => this(tp.ref)) + LazyRef(_ => this(tp.ref)) case tp: ClassInfo => mapClassInfo(tp) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 5eea9b769aa7..3015bef6d8e7 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -295,9 +295,8 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi RecThis(readTypeRef().asInstanceOf[RecType]) case LAZYref => val rdr = fork - def readUnderlying() = rdr.readType() skipTree() - LazyRef(readUnderlying) + LazyRef(implicit ctx => rdr.readType()) case SHARED => val ref = readAddr() typeAtAddr.getOrElseUpdate(ref, forkAt(ref).readType()) diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 72ce89a33942..130eb8d36754 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -249,7 +249,7 @@ object Checking { } catch { case ex: CyclicReference => ctx.debuglog(i"cycle detected for $tp, $nestedCycleOK, $cycleOK") - if (cycleOK) LazyRef(() => tp) + if (cycleOK) LazyRef(_ => tp) else if (reportErrors) throw ex else tp } From 8a50886f163e0d499f3732e2337360371e46a4d7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 21 Jul 2017 13:44:35 +0200 Subject: [PATCH 08/18] Introduce AppliedType AppliedType will be used for all type applications, higher-kinded or not. That is, eventually, it will replace HKApply and also the encodings of type applications as refined types. This commit introduces AppliedType and adapts baseType computations to take it into account. AppliedType is not yet constructed anywhere, however. --- .../tools/dotc/core/SymDenotations.scala | 64 +++++------ .../tools/dotc/core/TypeApplications.scala | 14 ++- .../dotty/tools/dotc/core/TypeComparer.scala | 8 +- .../src/dotty/tools/dotc/core/TypeOps.scala | 6 +- .../src/dotty/tools/dotc/core/Types.scala | 107 +++++++++++++++++- .../tools/dotc/core/tasty/TreePickler.scala | 2 +- .../tools/dotc/printing/PlainPrinter.scala | 6 +- .../tools/dotc/printing/RefinedPrinter.scala | 4 +- .../src/dotty/tools/dotc/sbt/ExtractAPI.scala | 2 +- .../tools/dotc/transform/ExplicitOuter.scala | 2 +- .../dotty/tools/dotc/typer/Applications.scala | 10 +- .../src/dotty/tools/dotc/typer/Namer.scala | 2 +- .../dotty/tools/dotc/typer/TypeAssigner.scala | 4 +- .../tools/dottydoc/model/factories.scala | 2 +- 14 files changed, 168 insertions(+), 65 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index bfaa12729090..6c3204eaacfb 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -699,7 +699,7 @@ object SymDenotations { | is not a subclass of ${owner.showLocated} where target is defined""") else if ( !( isType // allow accesses to types from arbitrary subclasses fixes #4737 - || pre.baseTypeRef(cls).exists // ??? why not use derivesFrom ??? + || pre.derivesFrom(cls) || isConstructor || (owner is ModuleClass) // don't perform this check for static members )) @@ -1266,10 +1266,10 @@ object SymDenotations { private[this] var myMemberCache: LRUCache[Name, PreDenotation] = null private[this] var myMemberCachePeriod: Period = Nowhere - /** A cache from types T to baseTypeRef(T, C) */ - type BaseTypeRefMap = java.util.HashMap[CachedType, Type] - private[this] var myBaseTypeRefCache: BaseTypeRefMap = null - private[this] var myBaseTypeRefCachePeriod: Period = Nowhere + /** A cache from types T to baseType(T, C) */ + type BaseTypeMap = java.util.HashMap[CachedType, Type] + private[this] var myBaseTypeCache: BaseTypeMap = null + private[this] var myBaseTypeCachePeriod: Period = Nowhere private var baseDataCache: BaseData = BaseData.None private var memberNamesCache: MemberNames = MemberNames.None @@ -1282,14 +1282,14 @@ object SymDenotations { myMemberCache } - private def baseTypeRefCache(implicit ctx: Context): BaseTypeRefMap = { - if (myBaseTypeRefCachePeriod != ctx.period && - (myBaseTypeRefCachePeriod.runId != ctx.runId || - ctx.phases(myBaseTypeRefCachePeriod.phaseId).sameParentsStartId != ctx.phase.sameParentsStartId)) { - myBaseTypeRefCache = new BaseTypeRefMap - myBaseTypeRefCachePeriod = ctx.period + private def baseTypeCache(implicit ctx: Context): BaseTypeMap = { + if (myBaseTypeCachePeriod != ctx.period && + (myBaseTypeCachePeriod.runId != ctx.runId || + ctx.phases(myBaseTypeCachePeriod.phaseId).sameParentsStartId != ctx.phase.sameParentsStartId)) { + myBaseTypeCache = new BaseTypeMap + myBaseTypeCachePeriod = ctx.period } - myBaseTypeRefCache + myBaseTypeCache } private def invalidateBaseDataCache() = { @@ -1302,9 +1302,9 @@ object SymDenotations { memberNamesCache = MemberNames.None } - def invalidateBaseTypeRefCache() = { - myBaseTypeRefCache = null - myBaseTypeRefCachePeriod = Nowhere + def invalidateBaseTypeCache() = { + myBaseTypeCache = null + myBaseTypeCachePeriod = Nowhere } override def copyCaches(from: SymDenotation, phase: Phase)(implicit ctx: Context): this.type = { @@ -1313,7 +1313,7 @@ object SymDenotations { if (from.memberNamesCache.isValidAt(phase)) memberNamesCache = from.memberNamesCache if (from.baseDataCache.isValidAt(phase)) { baseDataCache = from.baseDataCache - myBaseTypeRefCache = from.baseTypeRefCache + myBaseTypeCache = from.baseTypeCache } case _ => } @@ -1581,11 +1581,11 @@ object SymDenotations { raw.filterExcluded(excluded).asSeenFrom(pre).toDenot(pre) } - /** Compute tp.baseTypeRef(this) */ - final def baseTypeRefOf(tp: Type)(implicit ctx: Context): Type = { + /** Compute tp.baseType(this) */ + final def baseTypeOf(tp: Type)(implicit ctx: Context): Type = { def foldGlb(bt: Type, ps: List[Type]): Type = ps match { - case p :: ps1 => foldGlb(bt & baseTypeRefOf(p), ps1) + case p :: ps1 => foldGlb(bt & baseTypeOf(p), ps1) case _ => bt } @@ -1597,7 +1597,7 @@ object SymDenotations { * and this changes subtyping relations. As a shortcut, we do not * cache ErasedValueType at all. */ - def isCachable(tp: Type, btrCache: BaseTypeRefMap): Boolean = { + def isCachable(tp: Type, btrCache: BaseTypeMap): Boolean = { def inCache(tp: Type) = btrCache.containsKey(tp) tp match { case _: TypeErasure.ErasedValueType => false @@ -1609,12 +1609,12 @@ object SymDenotations { } } - def computeBaseTypeRefOf(tp: Type): Type = { + def computeBaseTypeOf(tp: Type): Type = { Stats.record("computeBaseTypeOf") - if (symbol.isStatic && tp.derivesFrom(symbol)) + if (symbol.isStatic && tp.derivesFrom(symbol) && symbol.typeParams.isEmpty) symbol.typeRef else tp match { - case tp: TypeRef => + case tp: RefType => val subcls = tp.symbol if (subcls eq symbol) tp @@ -1623,14 +1623,14 @@ object SymDenotations { if (cdenot.baseClassSet contains symbol) foldGlb(NoType, tp.parents) else NoType case _ => - baseTypeRefOf(tp.superType) + baseTypeOf(tp.superType) } case tp: TypeProxy => - baseTypeRefOf(tp.superType) + baseTypeOf(tp.superType) case AndType(tp1, tp2) => - baseTypeRefOf(tp1) & baseTypeRefOf(tp2) + baseTypeOf(tp1) & baseTypeOf(tp2) case OrType(tp1, tp2) => - baseTypeRefOf(tp1) | baseTypeRefOf(tp2) + baseTypeOf(tp1) | baseTypeOf(tp2) case JavaArrayType(_) if symbol == defn.ObjectClass => this.typeRef case _ => @@ -1638,16 +1638,16 @@ object SymDenotations { } } - /*>|>*/ ctx.debugTraceIndented(s"$tp.baseTypeRef($this)") /*<|<*/ { + /*>|>*/ ctx.debugTraceIndented(s"$tp.baseType($this)") /*<|<*/ { tp match { case tp: CachedType => - val btrCache = baseTypeRefCache + val btrCache = baseTypeCache try { var basetp = btrCache get tp if (basetp == null) { btrCache.put(tp, NoPrefix) - basetp = computeBaseTypeRefOf(tp) - if (isCachable(tp, baseTypeRefCache)) btrCache.put(tp, basetp) + basetp = computeBaseTypeOf(tp) + if (isCachable(tp, baseTypeCache)) btrCache.put(tp, basetp) else btrCache.remove(tp) } else if (basetp == NoPrefix) throw CyclicReference(this) @@ -1659,7 +1659,7 @@ object SymDenotations { throw ex } case _ => - computeBaseTypeRefOf(tp) + computeBaseTypeOf(tp) } } } diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index 9397eeb09d7b..f68d24e36d94 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -76,7 +76,7 @@ object TypeApplications { } def unapply(tp: Type)(implicit ctx: Context): Option[TypeRef] = tp match { - case tp @ HKTypeLambda(tparams, AppliedType(fn: TypeRef, args)) if (args == tparams.map(_.toArg)) => Some(fn) + case tp @ HKTypeLambda(tparams, AnyAppliedType(fn: TypeRef, args)) if (args == tparams.map(_.toArg)) => Some(fn) case _ => None } } @@ -87,7 +87,7 @@ object TypeApplications { * * where v_i, p_i are the variances and names of the type parameters of T. */ - object AppliedType { + object AnyAppliedType { def apply(tp: Type, args: List[Type])(implicit ctx: Context): Type = tp.appliedTo(args) def unapply(tp: Type)(implicit ctx: Context): Option[(Type, List[Type])] = tp match { @@ -111,6 +111,8 @@ object TypeApplications { None } collectArgs(tycon.typeParams, refinements, new mutable.ListBuffer[Type]) + case AppliedType(tycon, args) => + Some((tycon, args)) case HKApply(tycon, args) => Some((tycon, args)) case _ => @@ -408,7 +410,7 @@ class TypeApplications(val self: Type) extends AnyVal { if (!args.exists(_.isInstanceOf[TypeBounds])) { val followAlias = Config.simplifyApplications && { dealiased.resType match { - case AppliedType(tyconBody, dealiasedArgs) => + case AnyAppliedType(tyconBody, dealiasedArgs) => // Reduction should not affect type inference when it's // just eta-reduction (ignoring variance annotations). // See i2201*.scala for examples where more aggressive @@ -421,7 +423,7 @@ class TypeApplications(val self: Type) extends AnyVal { else HKApply(self, args) } else dealiased.resType match { - case AppliedType(tycon, args1) if tycon.safeDealias ne tycon => + case AnyAppliedType(tycon, args1) if tycon.safeDealias ne tycon => // In this case we should always dealias since we cannot handle // higher-kinded applications to wildcard arguments. dealiased @@ -521,7 +523,7 @@ class TypeApplications(val self: Type) extends AnyVal { * Existential types in arguments are returned as TypeBounds instances. */ final def baseTypeWithArgs(base: Symbol)(implicit ctx: Context): Type = ctx.traceIndented(s"btwa ${self.show} wrt $base", core, show = true) { - def default = self.baseTypeRef(base).appliedTo(baseArgInfos(base)) + def default = self.baseTypeTycon(base).appliedTo(baseArgInfos(base)) def isExpandedTypeParam(sym: Symbol) = sym.is(TypeParam) && sym.name.is(ExpandedName) self match { case tp: TypeRef => @@ -573,7 +575,7 @@ class TypeApplications(val self: Type) extends AnyVal { * Existential types in arguments are returned as TypeBounds instances. */ final def argInfos(implicit ctx: Context): List[Type] = self match { - case AppliedType(tycon, args) => args + case AnyAppliedType(tycon, args) => args case _ => Nil } diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 4e911eed5bf4..80bf3be04aad 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -368,7 +368,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case _ => val cls2 = tp2.symbol if (cls2.isClass) { - val base = tp1.baseTypeRef(cls2) + val base = tp1.baseType(cls2) if (base.exists && (base ne tp1)) return isSubType(base, tp2) if (cls2 == defn.SingletonClass && tp1.isStable) return true } @@ -713,7 +713,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { def liftToBase(bcs: List[ClassSymbol]): Boolean = bcs match { case bc :: bcs1 => classBounds.exists(bc.derivesFrom) && - tyconOK(tp1w.baseTypeRef(bc), tp1w.baseArgInfos(bc)) || + tyconOK(tp1w.baseTypeTycon(bc), tp1w.baseArgInfos(bc)) || liftToBase(bcs1) case _ => false @@ -771,7 +771,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { tycon1 match { case param1: TypeParamRef => def canInstantiate = tp2 match { - case AppliedType(tycon2, args2) => + case AnyAppliedType(tycon2, args2) => tryInstantiate(param1, tycon2.ensureHK) && isSubArgs(args1, args2, tycon2.typeParams) case _ => false @@ -810,7 +810,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { val classBounds = tp2.classSymbols def recur(bcs: List[ClassSymbol]): Boolean = bcs match { case bc :: bcs1 => - val baseRef = tp1.baseTypeRef(bc) + val baseRef = tp1.baseTypeTycon(bc) (classBounds.exists(bc.derivesFrom) && variancesConform(baseRef.typeParams, tparams) && p(baseRef.appliedTo(tp1.baseArgInfos(bc))) diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index 829b5acec00c..73f57131646f 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -66,7 +66,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object. else pre match { case pre: SuperType => toPrefix(pre.thistpe, cls, thiscls) case _ => - if (thiscls.derivesFrom(cls) && pre.baseTypeRef(thiscls).exists) { + if (thiscls.derivesFrom(cls) && pre.baseType(thiscls).exists) { // ??? why not derivesFrom ??? if (theMap != null && theMap.currentVariance <= 0 && !isLegalPrefix(pre)) { ctx.base.unsafeNonvariant = ctx.runId pre match { @@ -79,7 +79,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object. else if ((pre.termSymbol is Package) && !(thiscls is Package)) toPrefix(pre.select(nme.PACKAGE), cls, thiscls) else - toPrefix(pre.baseTypeRef(cls).normalizedPrefix, cls.owner, thiscls) + toPrefix(pre.baseType(cls).normalizedPrefix, cls.owner, thiscls) } } @@ -256,7 +256,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object. val doms = dominators(commonBaseClasses, Nil) def baseTp(cls: ClassSymbol): Type = { val base = - if (tp1.typeParams.nonEmpty) tp.baseTypeRef(cls) + if (tp1.typeParams.nonEmpty) tp.baseTypeTycon(cls) else tp.baseTypeWithArgs(cls) base.mapReduceOr(identity)(mergeRefined) } diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 3dd56aa1cebf..9e959a91cb98 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -566,6 +566,7 @@ object Types { try pdenot.info & rinfo catch { case ex: CyclicReference => + // ??? can this still happen? ??? // happens for tests/pos/sets.scala. findMember is called from baseTypeRef. // The & causes a subtype check which calls baseTypeRef again with the same // superclass. In the observed case, the superclass was Any, and @@ -826,13 +827,17 @@ object Types { /** The basetype TypeRef of this type with given class symbol, * but without including any type arguments */ - final def baseTypeRef(base: Symbol)(implicit ctx: Context): Type = /*ctx.traceIndented(s"$this baseTypeRef $base")*/ /*>|>*/ track("baseTypeRef") /*<|<*/ { + final def baseType(base: Symbol)(implicit ctx: Context): Type = /*ctx.traceIndented(s"$this baseType $base")*/ /*>|>*/ track("baseType") /*<|<*/ { base.denot match { - case classd: ClassDenotation => classd.baseTypeRefOf(this) + case classd: ClassDenotation => classd.baseTypeOf(this) case _ => NoType } } + /** Temporary replacement for baseTypeRef */ + final def baseTypeTycon(base: Symbol)(implicit ctx: Context): Type = + baseType(base).typeConstructor + def & (that: Type)(implicit ctx: Context): Type = track("&") { ctx.typeComparer.glb(this, that) } @@ -1001,6 +1006,12 @@ object Types { case _ => this } + /** The type constructor of an applied type, otherwise the type itself */ + final def typeConstructor(implicit ctx: Context): Type = this match { + case AppliedType(tycon, _) => tycon + case _ => this + } + /** If this is a (possibly aliased, annotated, and/or parameterized) reference to * a class, the class type ref, otherwise NoType. * @param refinementOK If `true` we also skip non-parameter refinements. @@ -1435,6 +1446,11 @@ object Types { /** A marker trait for types that can be types of values or that are higher-kinded */ trait ValueType extends ValueTypeOrProto with ValueTypeOrWildcard + /** A common base trait of NamedType and AppliedType */ + trait RefType extends CachedProxyType with ValueType { + def symbol(implicit ctx: Context): Symbol + } + /** A marker trait for types that are guaranteed to contain only a * single non-null value (they might contain null in addition). */ @@ -1466,7 +1482,7 @@ object Types { // --- NamedTypes ------------------------------------------------------------------ /** A NamedType of the form Prefix # name */ - abstract class NamedType extends CachedProxyType with ValueType { + abstract class NamedType extends CachedProxyType with RefType { val prefix: Type val name: Name @@ -2992,6 +3008,91 @@ object Types { def paramRef(implicit ctx: Context): Type = TypeParamRef(tl, n) } + /** A type application `C[T_1, ..., T_n]` */ + abstract case class AppliedType(tycon: Type, args: List[Type]) + extends CachedProxyType with RefType { + + private var validSuper: Period = Nowhere + private var cachedSuper: Type = _ + + override def underlying(implicit ctx: Context): Type = tycon + + override def superType(implicit ctx: Context): Type = { + def reapply(tp: Type) = tp match { + case tp: TypeRef if tp.symbol.isClass => tp + case _ => tp.applyIfParameterized(args) + } + if (ctx.period != validSuper) { + validSuper = ctx.period + cachedSuper = tycon match { + case tp: HKTypeLambda => defn.AnyType + case tp: TypeVar if !tp.inst.exists => + // supertype not stable, since underlying might change + validSuper = Nowhere + reapply(tp.underlying) + case tp: TypeProxy => + if (tp.typeSymbol.is(Provisional)) validSuper = Nowhere + reapply(tp.superType) + case _ => defn.AnyType + } + } + cachedSuper + } + + override def symbol(implicit ctx: Context) = tycon.typeSymbol + + def lowerBound(implicit ctx: Context) = tycon.stripTypeVar match { + case tycon: TypeRef => + tycon.info match { + case TypeBounds(lo, hi) => + if (lo eq hi) superType // optimization, can profit from caching in this case + else lo.applyIfParameterized(args) + case _ => NoType + } + case _ => + NoType + } + + def typeParams(implicit ctx: Context): List[ParamInfo] = { + val tparams = tycon.typeParams + if (tparams.isEmpty) HKTypeLambda.any(args.length).typeParams else tparams + } + + def derivedAppliedType(tycon: Type, args: List[Type])(implicit ctx: Context): Type = + if ((tycon eq this.tycon) && (args eq this.args)) this + else tycon.appliedTo(args) + + override def computeHash = doHash(tycon, args) + + protected def checkInst(implicit ctx: Context): this.type = { + def check(tycon: Type): Unit = tycon.stripTypeVar match { + case tycon: TypeRef => + case _: TypeParamRef | _: ErrorType | _: WildcardType => + case _: TypeLambda => + assert(!args.exists(_.isInstanceOf[TypeBounds]), s"unreduced type apply: $this") + case tycon: AnnotatedType => + check(tycon.underlying) + case _ => + assert(false, s"illegal type constructor in $this") + } + if (Config.checkHKApplications) check(tycon) + this + } + } + + final class CachedAppliedType(tycon: Type, args: List[Type]) extends AppliedType(tycon, args) + + object AppliedType { + def apply(tycon: Type, args: List[Type])(implicit ctx: Context) = + unique(new CachedAppliedType(tycon, args)).checkInst + } + + object ClassRef { + def unapply(tp: RefType)(implicit ctx: Context): Option[RefType] = { // after bootstrap, drop the Option + if (tp.symbol.isClass) Some(tp) else None + } + } + /** A higher kinded type application `C[T_1, ..., T_n]` */ abstract case class HKApply(tycon: Type, args: List[Type]) extends CachedProxyType with ValueType { diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 885a76e3f763..052be7a366ab 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -139,7 +139,7 @@ class TreePickler(pickler: TastyPickler) { } private def pickleNewType(tpe: Type, richTypes: Boolean)(implicit ctx: Context): Unit = tpe match { - case AppliedType(tycon, args) => + case AnyAppliedType(tycon, args) => writeByte(APPLIEDtype) withLength { pickleType(tycon); args.foreach(pickleType(_)) } case ConstantType(value) => diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 8a10ef60ebbe..91eabe742839 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -4,7 +4,7 @@ package printing import core._ import Texts._, Types._, Flags._, Names._, Symbols._, NameOps._, Constants._, Denotations._ import Contexts.Context, Scopes.Scope, Denotations.Denotation, Annotations.Annotation -import TypeApplications.AppliedType +import TypeApplications.AnyAppliedType import StdNames.{nme, tpnme} import ast.Trees._, ast._ import typer.Implicits._ @@ -123,7 +123,7 @@ class PlainPrinter(_ctx: Context) extends Printer { */ private def refinementChain(tp: Type): List[Type] = tp :: (tp match { - case AppliedType(_, _) => Nil + case AnyAppliedType(_, _) => Nil case tp: RefinedType => refinementChain(tp.parent.stripTypeVar) case _ => Nil }) @@ -142,7 +142,7 @@ class PlainPrinter(_ctx: Context) extends Printer { toTextLocal(tp.underlying) ~ "(" ~ toTextRef(tp) ~ ")" case tp: TypeRef => toTextPrefix(tp.prefix) ~ selectionString(tp) - case AppliedType(tycon, args) => + case AnyAppliedType(tycon, args) => (toTextLocal(tycon) ~ "[" ~ Text(args map argText, ", ") ~ "]").close case tp: RefinedType => val parent :: (refined: List[RefinedType @unchecked]) = diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index c494110201e4..482d8c651cce 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -126,7 +126,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { } def isInfixType(tp: Type): Boolean = tp match { - case AppliedType(tycon, args) => + case AnyAppliedType(tycon, args) => args.length == 2 && !Character.isUnicodeIdentifierStart(tycon.typeSymbol.name.toString.head) // TODO: Once we use the 2.12 stdlib, also check the @showAsInfix annotation @@ -149,7 +149,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { homogenize(tp) match { case x: ConstantType if homogenizedView => return toText(x.widen) - case AppliedType(tycon, args) => + case AnyAppliedType(tycon, args) => val cls = tycon.typeSymbol if (tycon.isRepeatedParam) return toTextLocal(args.head) ~ "*" if (defn.isFunctionClass(cls)) return toTextFunction(args, cls.name.isImplicitFunction) diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala index 5488d1979649..b499427d5009 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala @@ -370,7 +370,7 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder else tp.prefix new api.Projection(simpleType(prefix), sym.name.toString) - case TypeApplications.AppliedType(tycon, args) => + case TypeApplications.AnyAppliedType(tycon, args) => def processArg(arg: Type): api.Type = arg match { case arg @ TypeBounds(lo, hi) => // Handle wildcard parameters if (lo.isDirectRef(defn.NothingClass) && hi.isDirectRef(defn.AnyClass)) diff --git a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala index 937490ab060a..3ad2d69305c9 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -105,7 +105,7 @@ class ExplicitOuter extends MiniPhaseTransform with InfoTransformer { thisTransf for (parentTrait <- cls.mixins) { if (needsOuterIfReferenced(parentTrait)) { - val parentTp = cls.denot.thisType.baseTypeRef(parentTrait) + val parentTp = cls.denot.thisType.baseType(parentTrait) val outerAccImpl = newOuterAccessor(cls, parentTrait).enteredAfter(thisTransformer) newDefs += DefDef(outerAccImpl, singleton(fixThis(outerPrefix(parentTp)))) } diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 5e56a9bbea40..1d481556475c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -378,12 +378,12 @@ trait Applications extends Compatibility { self: Typer with Dynamic => // default getters for class constructors are found in the companion object val cls = meth.owner val companion = cls.companionModule - receiver.tpe.baseTypeRef(cls) match { - case tp: TypeRef if companion.isTerm => - selectGetter(ref(TermRef(tp.prefix, companion.asTerm))) - case _ => - EmptyTree + if (companion.isTerm) { + val prefix = receiver.tpe.baseType(cls).normalizedPrefix + if (prefix.exists) selectGetter(ref(TermRef(prefix, companion.asTerm))) + else EmptyTree } + else EmptyTree } } } diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 7dd741666cdb..93a2b3eda707 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -922,7 +922,7 @@ class Namer { typer: Typer => Checking.checkWellFormed(cls) if (isDerivedValueClass(cls)) cls.setFlag(Final) cls.info = avoidPrivateLeaks(cls, cls.pos) - cls.baseClasses.foreach(_.invalidateBaseTypeRefCache()) // we might have looked before and found nothing + cls.baseClasses.foreach(_.invalidateBaseTypeCache()) // we might have looked before and found nothing } } diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 3a330b3f041d..ea115a07a3a1 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -6,7 +6,7 @@ import core._ import ast._ import Scopes._, Contexts._, Constants._, Types._, Symbols._, Names._, Flags._, Decorators._ import ErrorReporting._, Annotations._, Denotations._, SymDenotations._, StdNames._, TypeErasure._ -import TypeApplications.AppliedType +import TypeApplications.AnyAppliedType import util.Positions._ import config.Printers.typr import ast.Trees._ @@ -104,7 +104,7 @@ trait TypeAssigner { } case tp @ HKApply(tycon, args) if toAvoid(tycon) => apply(tp.superType) - case tp @ AppliedType(tycon, args) if toAvoid(tycon) => + case tp @ AnyAppliedType(tycon, args) if toAvoid(tycon) => val base = apply(tycon) var args = tp.baseArgInfos(base.typeSymbol) if (base.typeParams.length != args.length) diff --git a/doc-tool/src/dotty/tools/dottydoc/model/factories.scala b/doc-tool/src/dotty/tools/dottydoc/model/factories.scala index 03f11335ead1..563a20938372 100644 --- a/doc-tool/src/dotty/tools/dottydoc/model/factories.scala +++ b/doc-tool/src/dotty/tools/dottydoc/model/factories.scala @@ -57,7 +57,7 @@ object factories { } def expandTpe(t: Type, params: List[Reference] = Nil): Reference = t match { - case AppliedType(tycon, args) => { + case AnyAppliedType(tycon, args) => { val cls = tycon.typeSymbol if (defn.isFunctionClass(cls)) From 91509881f83d21b6c3a398854a793d5d9f8c1780 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 21 Jul 2017 16:34:45 +0200 Subject: [PATCH 09/18] Adapt operations in Types So far, everything up to parents is adapted to new scheme. Parents is half done; needs more work once we change layour of ClassInfo. --- compiler/src/dotty/tools/dotc/ast/tpd.scala | 4 +- .../tools/dotc/core/SymDenotations.scala | 2 +- .../src/dotty/tools/dotc/core/Types.scala | 76 +++++++++++++------ .../tools/dotc/printing/PlainPrinter.scala | 4 +- .../dotc/reporting/diagnostic/messages.scala | 2 +- .../dotty/tools/dotc/transform/Erasure.scala | 2 +- .../tools/dotc/transform/ExplicitOuter.scala | 2 +- .../dotc/transform/OverridingPairs.scala | 2 +- .../dotty/tools/dotc/typer/Applications.scala | 2 +- .../dotty/tools/dotc/typer/RefChecks.scala | 2 +- .../dotty/tools/dotc/typer/TypeAssigner.scala | 4 +- 11 files changed, 66 insertions(+), 36 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 016db8e2c01b..4ee30af5ede1 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -213,7 +213,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { ta.assignType(untpd.TypeDef(sym.name, TypeTree(sym.info)), sym) def ClassDef(cls: ClassSymbol, constr: DefDef, body: List[Tree], superArgs: List[Tree] = Nil)(implicit ctx: Context): TypeDef = { - val firstParentRef :: otherParentRefs = cls.info.parents + val firstParentRef :: otherParentRefs = cls.info.parentRefs val firstParent = cls.typeRef.baseTypeWithArgs(firstParentRef.symbol) val superRef = if (cls is Trait) TypeTree(firstParent) @@ -261,7 +261,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def AnonClass(parents: List[Type], fns: List[TermSymbol], methNames: List[TermName])(implicit ctx: Context): Block = { val owner = fns.head.owner val parents1 = - if (parents.head.classSymbol.is(Trait)) parents.head.parents.head :: parents + if (parents.head.classSymbol.is(Trait)) parents.head.parentRefs.head :: parents else parents val cls = ctx.newNormalizedClassSymbol(owner, tpnme.ANON_FUN, Synthetic, parents1, coord = fns.map(_.pos).reduceLeft(_ union _)) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 6c3204eaacfb..3bd592a9b181 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1620,7 +1620,7 @@ object SymDenotations { tp else subcls.denot match { case cdenot: ClassDenotation => - if (cdenot.baseClassSet contains symbol) foldGlb(NoType, tp.parents) + if (cdenot.baseClassSet contains symbol) foldGlb(NoType, tp.parentsNEW) // !!! change to parents else NoType case _ => baseTypeOf(tp.superType) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 9e959a91cb98..6ace9d94e2e3 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -129,9 +129,10 @@ object Types { case TypeAlias(tp) => assert((tp ne this) && (tp ne this1), s"$tp / $this") tp.isRef(sym) - case _ => this1.symbol eq sym + case _ => this1.symbol eq sym } case this1: RefinedOrRecType => this1.parent.isRef(sym) + case this1: AppliedType => this1.underlying.isRef(sym) case this1: HKApply => this1.superType.isRef(sym) case _ => false } @@ -210,7 +211,7 @@ object Types { */ private final def phantomLatticeType(implicit ctx: Context): Type = widen match { case tp: ClassInfo if defn.isPhantomTerminalClass(tp.classSymbol) => tp.prefix - case tp: TypeProxy if tp.superType ne this => tp.underlying.phantomLatticeType + case tp: TypeProxy if tp.superType ne this => tp.underlying.phantomLatticeType // ??? guard needed ??? case tp: AndOrType => tp.tp1.phantomLatticeType case _ => NoType } @@ -483,6 +484,8 @@ object Types { }) case tp: TypeRef => tp.denot.findMember(name, pre, excluded) + case tp: AppliedType => + goApplied(tp) case tp: ThisType => goThis(tp) case tp: RefinedType => @@ -494,7 +497,7 @@ object Types { case tp: SuperType => goSuper(tp) case tp: HKApply => - goApply(tp) + goHKApply(tp) case tp: TypeProxy => go(tp.underlying) case tp: ClassInfo => @@ -584,7 +587,15 @@ object Types { } } - def goApply(tp: HKApply) = tp.tycon match { + def goApplied(tp: AppliedType) = tp.tycon match { + case tl: HKTypeLambda => + go(tl.resType).mapInfo(info => + tl.derivedLambdaAbstraction(tl.paramNames, tl.paramInfos, info).appliedTo(tp.args)) + case _ => + go(tp.superType) + } + + def goHKApply(tp: HKApply) = tp.tycon match { case tl: HKTypeLambda => go(tl.resType).mapInfo(info => tl.derivedLambdaAbstraction(tl.paramNames, tl.paramInfos, info).appliedTo(tp.args)) @@ -963,6 +974,14 @@ object Types { case TypeAlias(tp) => tp.dealias(keepAnnots): @tailrec case _ => tp } + case app @ AppliedType(tycon, args) => + val tycon1 = tycon.dealias(keepAnnots) + if (tycon1 ne tycon) app.superType.dealias(keepAnnots): @tailrec + else this + case app @ HKApply(tycon, args) => + val tycon1 = tycon.dealias(keepAnnots) + if (tycon1 ne tycon) app.superType.dealias(keepAnnots): @tailrec + else this case tp: TypeVar => val tp1 = tp.instanceOpt if (tp1.exists) tp1.dealias(keepAnnots): @tailrec else tp @@ -971,10 +990,6 @@ object Types { if (keepAnnots) tp.derivedAnnotatedType(tp1, tp.annot) else tp1 case tp: LazyRef => tp.ref.dealias(keepAnnots): @tailrec - case app @ HKApply(tycon, args) => - val tycon1 = tycon.dealias(keepAnnots) - if (tycon1 ne tycon) app.superType.dealias(keepAnnots): @tailrec - else this case _ => this } @@ -1021,6 +1036,8 @@ object Types { if (tp.symbol.isClass) tp else if (tp.symbol.isAliasType) tp.underlying.underlyingClassRef(refinementOK) else NoType + case tp: AppliedType => + tp.superType.underlyingClassRef(refinementOK) case tp: AnnotatedType => tp.underlying.underlyingClassRef(refinementOK) case tp: RefinedType => @@ -1158,19 +1175,33 @@ object Types { * Inherited by all type proxies. Empty for all other types. * Overwritten in ClassInfo, where parents is cached. */ - def parents(implicit ctx: Context): List[TypeRef] = this match { - case tp: TypeProxy => tp.underlying.parents - case _ => List() + def parentRefs(implicit ctx: Context): List[TypeRef] = this match { + case tp: TypeProxy => tp.underlying.parentRefs + case _ => Nil } /** The full parent types, including all type arguments */ def parentsWithArgs(implicit ctx: Context): List[Type] = this match { case tp: TypeProxy => tp.superType.parentsWithArgs - case _ => List() + case _ => Nil + } + + /** The full parent types, including (in new scheme) all type arguments */ + def parentsNEW(implicit ctx: Context): List[Type] = this match { + case AppliedType(tycon: HKTypeLambda, args) => // TODO: can be eliminated once ClassInfo is changed, also: cache? + tycon.resType.parentsWithArgs.map(_.substParams(tycon, args)) + case tp: TypeProxy => tp.superType.parentsNEW + case _ => Nil + } + + /** The first parent of this type, AnyRef if list of parents is empty */ + def firstParentRef(implicit ctx: Context): TypeRef = parentRefs match { + case p :: _ => p + case _ => defn.AnyType } /** The first parent of this type, AnyRef if list of parents is empty */ - def firstParent(implicit ctx: Context): TypeRef = parents match { + def firstParentNEW(implicit ctx: Context): Type = parentsNEW match { case p :: _ => p case _ => defn.AnyType } @@ -3018,10 +3049,6 @@ object Types { override def underlying(implicit ctx: Context): Type = tycon override def superType(implicit ctx: Context): Type = { - def reapply(tp: Type) = tp match { - case tp: TypeRef if tp.symbol.isClass => tp - case _ => tp.applyIfParameterized(args) - } if (ctx.period != validSuper) { validSuper = ctx.period cachedSuper = tycon match { @@ -3029,10 +3056,10 @@ object Types { case tp: TypeVar if !tp.inst.exists => // supertype not stable, since underlying might change validSuper = Nowhere - reapply(tp.underlying) + tp.underlying.applyIfParameterized(args) case tp: TypeProxy => if (tp.typeSymbol.is(Provisional)) validSuper = Nowhere - reapply(tp.superType) + tp.superType.applyIfParameterized(args) case _ => defn.AnyType } } @@ -3439,7 +3466,7 @@ object Types { private var parentsCache: List[TypeRef] = null /** The parent type refs as seen from the given prefix */ - override def parents(implicit ctx: Context): List[TypeRef] = { + override def parentRefs(implicit ctx: Context): List[TypeRef] = { if (parentsCache == null) parentsCache = cls.classParents.mapConserve(_.asSeenFrom(prefix, cls.owner).asInstanceOf[TypeRef]) parentsCache @@ -3447,7 +3474,7 @@ object Types { /** The parent types with all type arguments */ override def parentsWithArgs(implicit ctx: Context): List[Type] = - parents mapConserve { pref => + parentRefs mapConserve { pref => ((pref: Type) /: pref.classSymbol.typeParams) { (parent, tparam) => val targSym = decls.lookup(tparam.name) if (targSym.exists) RefinedType(parent, targSym.name, targSym.info) @@ -3455,6 +3482,9 @@ object Types { } } + override def parentsNEW(implicit ctx: Context): List[Type] = + parentRefs // !!! TODO: change + def derivedClassInfo(prefix: Type)(implicit ctx: Context) = if (prefix eq this.prefix) this else ClassInfo(prefix, cls, classParents, decls, selfInfo) @@ -3938,12 +3968,12 @@ object Types { abstract class DeepTypeMap(implicit ctx: Context) extends TypeMap { override def mapClassInfo(tp: ClassInfo) = { val prefix1 = this(tp.prefix) - val parents1 = (tp.parents mapConserve this).asInstanceOf[List[TypeRef]] + val parentRefs1 = (tp.parentRefs mapConserve this).asInstanceOf[List[TypeRef]] val selfInfo1 = tp.selfInfo match { case selfInfo: Type => this(selfInfo) case selfInfo => selfInfo } - tp.derivedClassInfo(prefix1, parents1, tp.decls, selfInfo1) + tp.derivedClassInfo(prefix1, parentRefs1, tp.decls, selfInfo1) } } diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 91eabe742839..cf409a18103b 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -323,7 +323,7 @@ class PlainPrinter(_ctx: Context) extends Printer { val declsText = if (trueDecls.isEmpty || !ctx.settings.debug.value) Text() else dclsText(trueDecls) - tparamsText ~ " extends " ~ toTextParents(tp.parents) ~ "{" ~ selfText ~ declsText ~ + tparamsText ~ " extends " ~ toTextParents(tp.parentsNEW) ~ "{" ~ selfText ~ declsText ~ "} at " ~ preText case tp => ": " ~ toTextGlobal(tp) @@ -401,7 +401,7 @@ class PlainPrinter(_ctx: Context) extends Printer { def toText(sym: Symbol): Text = (kindString(sym) ~~ { - if (sym.isAnonymousClass) toText(sym.info.parents, " with ") ~ "{...}" + if (sym.isAnonymousClass) toText(sym.info.parentsNEW, " with ") ~ "{...}" else if (hasMeaninglessName(sym)) simpleNameString(sym.owner) + idString(sym) else nameString(sym) }).close diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index 757da5d83bc7..2526fe9d9c54 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -1264,7 +1264,7 @@ object messages { val msg = hl"""|$qual does not name a parent of $cls""" val kind = "Reference" - private val parents: Seq[String] = (cls.info.parents map (_.name.show)).sorted + private val parents: Seq[String] = (cls.info.parentRefs map (_.name.show)).sorted val explanation = hl"""|When a qualifier ${"T"} is used in a ${"super"} prefix of the form ${"C.super[T]"}, diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 5837b2b6904a..25c8c53a37a1 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -391,7 +391,7 @@ object Erasure { cpy.Super(qual)(thisQual, untpd.Ident(sym.owner.asClass.name)) .withType(SuperType(thisType, sym.owner.typeRef)) else - qual.withType(SuperType(thisType, thisType.firstParent)) + qual.withType(SuperType(thisType, thisType.firstParentRef)) case _ => qual } diff --git a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala index 3ad2d69305c9..8573e1e930d9 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -194,7 +194,7 @@ object ExplicitOuter { needsOuterIfReferenced(cls) && (!hasLocalInstantiation(cls) || // needs outer because we might not know whether outer is referenced or not cls.mixins.exists(needsOuterIfReferenced) || // needs outer for parent traits - cls.classInfo.parents.exists(parent => // needs outer to potentially pass along to parent + cls.info.parentsNEW.exists(parent => // needs outer to potentially pass along to parent needsOuterIfReferenced(parent.classSymbol.asClass))) /** Class is always instantiated in the compilation unit where it is defined */ diff --git a/compiler/src/dotty/tools/dotc/transform/OverridingPairs.scala b/compiler/src/dotty/tools/dotc/transform/OverridingPairs.scala index cbd79d5c50d6..44c17d15178c 100644 --- a/compiler/src/dotty/tools/dotc/transform/OverridingPairs.scala +++ b/compiler/src/dotty/tools/dotc/transform/OverridingPairs.scala @@ -35,7 +35,7 @@ object OverridingPairs { * pair has already been treated in a parent class. * This may be refined in subclasses. @see Bridges for a use case. */ - protected def parents: Array[Symbol] = base.info.parents.toArray map (_.typeSymbol) + protected def parents: Array[Symbol] = base.info.parentsNEW.toArray map (_.typeSymbol) /** Does `sym1` match `sym2` so that it qualifies as overriding. * Types always match. Term symbols match if their membertypes diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 1d481556475c..9468f43daa79 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -894,7 +894,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => def isSubTypeOfParent(subtp: Type, tp: Type)(implicit ctx: Context): Boolean = if (subtp <:< tp) true else tp match { - case tp: TypeRef if tp.symbol.isClass => isSubTypeOfParent(subtp, tp.firstParent) + case tp: TypeRef if tp.symbol.isClass => isSubTypeOfParent(subtp, tp.firstParentNEW) case tp: TypeProxy => isSubTypeOfParent(subtp, tp.superType) case _ => false } diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 97d50b07e34e..7a7f8d9df998 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -285,7 +285,7 @@ object RefChecks { //Console.println(infoString(member) + " shadows1 " + infoString(other) " in " + clazz);//DEBUG return } - val parentSymbols = clazz.info.parents.map(_.typeSymbol) + val parentSymbols = clazz.info.parentsNEW.map(_.typeSymbol) if (parentSymbols exists (p => subOther(p) && subMember(p) && deferredCheck)) { //Console.println(infoString(member) + " shadows2 " + infoString(other) + " in " + clazz);//DEBUG return diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index ea115a07a3a1..2e5cdfb55acf 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -293,7 +293,7 @@ trait TypeAssigner { case err: ErrorType => untpd.cpy.Super(tree)(qual, mix).withType(err) case qtype @ ThisType(_) => val cls = qtype.cls - def findMixinSuper(site: Type): Type = site.parents filter (_.name == mix.name) match { + def findMixinSuper(site: Type): Type = site.parentRefs filter (_.name == mix.name) match { case p :: Nil => p case Nil => @@ -304,7 +304,7 @@ trait TypeAssigner { val owntype = if (mixinClass.exists) mixinClass.typeRef else if (!mix.isEmpty) findMixinSuper(cls.info) - else if (inConstrCall || ctx.erasedTypes) cls.info.firstParent + else if (inConstrCall || ctx.erasedTypes) cls.info.firstParentRef else { val ps = cls.classInfo.parentsWithArgs if (ps.isEmpty) defn.AnyType else ps.reduceLeft((x: Type, y: Type) => x & y) From aa809031342d475de8db0459ae5485a530138b94 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 22 Jul 2017 14:29:02 +0200 Subject: [PATCH 10/18] Move givenSelfType to ClassSymbol Simplifies usage and removes some obscure code in Types --- .../backend/jvm/DottyBackendInterface.scala | 2 +- .../tools/dotc/core/SymDenotations.scala | 8 ++++++ .../src/dotty/tools/dotc/core/Types.scala | 28 ++++--------------- .../src/dotty/tools/dotc/sbt/ExtractAPI.scala | 2 +- .../tools/dotc/transform/ExplicitSelf.scala | 5 ++-- .../src/dotty/tools/dotc/typer/Checking.scala | 2 +- .../dotty/tools/dotc/typer/RefChecks.scala | 8 +++--- .../dotty/tools/dotc/typer/TypeAssigner.scala | 20 ++++++------- 8 files changed, 31 insertions(+), 44 deletions(-) diff --git a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index 297f7e0f8934..aacee60668ff 100644 --- a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -889,7 +889,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma // to inner symbols of DefDef // todo: somehow handle. - def parents: List[Type] = tp.parents + def parents: List[Type] = tp.parentsNEW } diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 3bd592a9b181..54d7d41d3206 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1379,6 +1379,14 @@ object SymDenotations { NoSymbol } + /** The explicitly given self type (self types of modules are assumed to be + * explcitly given here). + */ + def givenSelfType(implicit ctx: Context) = classInfo.selfInfo match { + case tp: Type => tp + case self: Symbol => self.info + } + // ------ class-specific operations ----------------------------------- private[this] var myThisType: Type = null diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 6ace9d94e2e3..382cf0fddfb9 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1206,14 +1206,6 @@ object Types { case _ => defn.AnyType } - /** the self type of the underlying classtype */ - def givenSelfType(implicit ctx: Context): Type = this match { - case tp: RefinedType => tp.wrapIfMember(tp.parent.givenSelfType) - case tp: ThisType => tp.tref.givenSelfType - case tp: TypeProxy => tp.superType.givenSelfType - case _ => NoType - } - /** The parameter types of a PolyType or MethodType, Empty list for others */ final def paramInfoss(implicit ctx: Context): List[List[Type]] = stripPoly match { case mt: MethodType => mt.paramInfos :: mt.resultType.paramInfoss @@ -1642,10 +1634,8 @@ object Types { } private def checkSymAssign(sym: Symbol)(implicit ctx: Context) = { - def selfTypeOf(sym: Symbol) = sym.owner.info match { - case info: ClassInfo => info.givenSelfType - case _ => NoType - } + def selfTypeOf(sym: Symbol) = + if (sym.isClass) sym.asClass.givenSelfType else NoType assert( (lastSymbol eq sym) || @@ -2242,7 +2232,7 @@ object Types { if ((parent eq this.parent) && (refinedName eq this.refinedName) && (refinedInfo eq this.refinedInfo)) this else RefinedType(parent, refinedName, refinedInfo) - /** Add this refinement to `parent`, provided If `refinedName` is a member of `parent`. */ + /** Add this refinement to `parent`, provided `refinedName` is a member of `parent`. */ def wrapIfMember(parent: Type)(implicit ctx: Context): Type = if (parent.member(refinedName).exists) derivedRefinedType(parent, refinedName, refinedInfo) else parent @@ -3416,7 +3406,7 @@ object Types { if (selfTypeCache == null) selfTypeCache = { def fullRef = fullyAppliedRef - val given = givenSelfType + val given = cls.givenSelfType val raw = if (!given.exists) fullRef else if (cls is Module) given @@ -3427,14 +3417,6 @@ object Types { selfTypeCache } - /** The explicitly given self type (self types of modules are assumed to be - * explcitly given here). - */ - override def givenSelfType(implicit ctx: Context): Type = selfInfo match { - case tp: Type => tp - case self: Symbol => self.info - } - private var selfTypeCache: Type = null private def fullyAppliedRef(base: Type, tparams: List[TypeSymbol])(implicit ctx: Context): Type = tparams match { @@ -4284,7 +4266,7 @@ object Types { } private def otherReason(pre: Type)(implicit ctx: Context): String = pre match { - case pre: ThisType if pre.givenSelfType.exists => + case pre: ThisType if pre.cls.givenSelfType.exists => i"\nor the self type of $pre might not contain all transitive dependencies" case _ => "" } diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala index b499427d5009..b34eade299ee 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala @@ -179,7 +179,7 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder else dt.Module } else dt.ClassDef - val selfType = apiType(sym.classInfo.givenSelfType) + val selfType = apiType(sym.givenSelfType) val name = if (sym.is(ModuleClass)) sym.fullName.sourceModuleName else sym.fullName diff --git a/compiler/src/dotty/tools/dotc/transform/ExplicitSelf.scala b/compiler/src/dotty/tools/dotc/transform/ExplicitSelf.scala index 7bb65e5755b2..3592471823ff 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExplicitSelf.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExplicitSelf.scala @@ -38,9 +38,8 @@ class ExplicitSelf extends MiniPhaseTransform { thisTransform => override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo): Tree = tree match { case Select(thiz: This, name) if name.isTermName => val cls = thiz.symbol.asClass - val cinfo = cls.classInfo - if (cinfo.givenSelfType.exists && !cls.derivesFrom(tree.symbol.owner)) - cpy.Select(tree)(thiz.asInstance(AndType(cinfo.selfType, thiz.tpe)), name) + if (cls.givenSelfType.exists && !cls.derivesFrom(tree.symbol.owner)) + cpy.Select(tree)(thiz.asInstance(AndType(cls.classInfo.selfType, thiz.tpe)), name) else tree case _ => tree } diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 130eb8d36754..90586f249670 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -133,7 +133,7 @@ object Checking { // Create a synthetic singleton type instance, and check whether // it conforms to the self type of the class as seen from that instance. val stp = SkolemType(tp) - val selfType = tref.givenSelfType.asSeenFrom(stp, cls) + val selfType = cls.asClass.givenSelfType.asSeenFrom(stp, cls) if (selfType.exists && !(stp <:< selfType)) ctx.error(DoesNotConformToSelfTypeCantBeInstantiated(tp, selfType), pos) } diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 7a7f8d9df998..741e174d3ec2 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -94,16 +94,16 @@ object RefChecks { */ private def checkParents(cls: Symbol)(implicit ctx: Context): Unit = cls.info match { case cinfo: ClassInfo => - def checkSelfConforms(other: TypeRef, category: String, relation: String) = { + def checkSelfConforms(other: ClassSymbol, category: String, relation: String) = { val otherSelf = other.givenSelfType.asSeenFrom(cls.thisType, other.classSymbol) if (otherSelf.exists && !(cinfo.selfType <:< otherSelf)) ctx.error(DoesNotConformToSelfType(category, cinfo.selfType, cls, otherSelf, relation, other.classSymbol), cls.pos) } for (parent <- cinfo.classParents) - checkSelfConforms(parent, "illegal inheritance", "parent") - for (reqd <- cinfo.givenSelfType.classSymbols) - checkSelfConforms(reqd.typeRef, "missing requirement", "required") + checkSelfConforms(parent.typeSymbol.asClass, "illegal inheritance", "parent") + for (reqd <- cinfo.cls.givenSelfType.classSymbols) + checkSelfConforms(reqd, "missing requirement", "required") case _ => } diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 2e5cdfb55acf..6f2fe09fa887 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -513,17 +513,15 @@ trait TypeAssigner { private def symbolicIfNeeded(sym: Symbol)(implicit ctx: Context) = { val owner = sym.owner - owner.infoOrCompleter match { - case info: ClassInfo if info.givenSelfType.exists => - // In that case a simple typeRef/termWithWithSig could return a member of - // the self type, not the symbol itself. To avoid this, we make the reference - // symbolic. In general it seems to be faster to keep the non-symblic - // reference, since there is less pressure on the uniqueness tables that way - // and less work to update all the different references. That's why symbolic references - // are only used if necessary. - NamedType.withFixedSym(owner.thisType, sym) - case _ => NoType - } + if (owner.isClass && owner.isCompleted && owner.asClass.givenSelfType.exists) + // In that case a simple typeRef/termWithWithSig could return a member of + // the self type, not the symbol itself. To avoid this, we make the reference + // symbolic. In general it seems to be faster to keep the non-symblic + // reference, since there is less pressure on the uniqueness tables that way + // and less work to update all the different references. That's why symbolic references + // are only used if necessary. + NamedType.withFixedSym(owner.thisType, sym) + else NoType } def assertExists(tp: Type) = { assert(tp != NoType); tp } From b98941d523c8b68b6363fb1999e21f342514923e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 23 Jul 2017 11:56:29 +0200 Subject: [PATCH 11/18] Handle AppliedTypes Systematically introduce handlers for AppliedTypes where there was a handler for HKApply. --- .../src/dotty/tools/dotc/config/Config.scala | 2 + .../tools/dotc/core/SymDenotations.scala | 1 + .../tools/dotc/core/TypeApplications.scala | 6 +- .../dotty/tools/dotc/core/TypeComparer.scala | 226 +++++++++++++++++- .../dotty/tools/dotc/core/TypeErasure.scala | 8 +- .../src/dotty/tools/dotc/core/TypeOps.scala | 81 ++++--- .../src/dotty/tools/dotc/core/Types.scala | 46 +++- .../dotc/core/classfile/ClassfileParser.scala | 4 +- .../core/unpickleScala2/Scala2Unpickler.scala | 8 + .../tools/dotc/printing/PlainPrinter.scala | 4 + .../tools/dotc/transform/patmat/Space.scala | 5 + .../src/dotty/tools/dotc/typer/Checking.scala | 23 +- .../dotty/tools/dotc/typer/Implicits.scala | 7 + .../dotty/tools/dotc/typer/ProtoTypes.scala | 5 + .../dotty/tools/dotc/typer/TypeAssigner.scala | 2 + .../dotty/tools/dotc/typer/Variances.scala | 11 + .../tools/dottydoc/model/factories.scala | 2 +- 17 files changed, 387 insertions(+), 54 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/config/Config.scala b/compiler/src/dotty/tools/dotc/config/Config.scala index ffc4d65632cf..22f36dfea790 100644 --- a/compiler/src/dotty/tools/dotc/config/Config.scala +++ b/compiler/src/dotty/tools/dotc/config/Config.scala @@ -174,4 +174,6 @@ object Config { /** When in IDE, turn StaleSymbol errors into warnings instead of crashing */ final val ignoreStaleInIDE = true + + final val newScheme = false } diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 54d7d41d3206..ef8026e22422 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1171,6 +1171,7 @@ object SymDenotations { case tp: TypeBounds => hasSkolems(tp.lo) || hasSkolems(tp.hi) case tp: TypeVar => hasSkolems(tp.inst) case tp: ExprType => hasSkolems(tp.resType) + case tp: AppliedType => hasSkolems(tp.tycon) || tp.args.exists(hasSkolems) case tp: HKApply => hasSkolems(tp.tycon) || tp.args.exists(hasSkolems) case tp: LambdaType => tp.paramInfos.exists(hasSkolems) || hasSkolems(tp.resType) case tp: AndOrType => hasSkolems(tp.tp1) || hasSkolems(tp.tp2) diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index f68d24e36d94..199b63e24880 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -185,7 +185,7 @@ object TypeApplications { case arg => arg } - case _: TypeBounds | _: HKApply => + case _: TypeBounds | _: HKApply | _: AppliedType => val saved = available available = Set() try mapOver(t) @@ -453,7 +453,8 @@ class TypeApplications(val self: Type) extends AnyVal { case _ if typParams.isEmpty || typParams.head.isInstanceOf[LambdaParam] => HKApply(self, args) case dealiased => - matchParams(dealiased, typParams, args) + if (Config.newScheme) ??? + else matchParams(dealiased, typParams, args) } } @@ -593,6 +594,7 @@ class TypeApplications(val self: Type) extends AnyVal { */ final def withoutArgs(typeArgs: List[Type]): Type = self match { case HKApply(tycon, args) => tycon + case AppliedType(tycon, args) => tycon case _ => typeArgs match { case _ :: typeArgs1 => diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 80bf3be04aad..9c7692e441c1 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -342,6 +342,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { // with `B[Y]`. To do the right thing, we need to instantiate `C` to the // common superclass of `A` and `B`. isSubType(tp1.join, tp2) + case tp2: AppliedType if !tp2.tycon.typeSymbol.isClass => + isSubType(tp1.join, tp2) case _ => false } @@ -376,6 +378,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { } private def thirdTry(tp1: Type, tp2: Type): Boolean = tp2 match { + case tp2 @ AppliedType(tycon2, args2) => + compareAppliedType2(tp1, tp2, tycon2, args2) case tp2: NamedType => thirdTryNamed(tp1, tp2) case tp2: TypeParamRef => @@ -566,6 +570,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { (sym1 eq NullClass) && isNullable(tp2) || (sym1 eq PhantomNothingClass) && tp1.topType == tp2.topType } + case tp1 @ AppliedType(tycon1, args1) => + compareAppliedType1(tp1, tycon1, args1, tp2) case tp1: SingletonType => /** if `tp2 == p.type` and `p: q.type` then try `tp1 <:< q.type` as a last effort.*/ def comparePaths = tp2 match { @@ -784,6 +790,172 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { false } + /** Subtype test for the hk application `tp2 = tycon2[args2]`. + */ + def compareAppliedType2(tp1: Type, tp2: AppliedType, tycon2: Type, args2: List[Type]): Boolean = { + val tparams = tycon2.typeParams + if (tparams.isEmpty) return false // can happen for ill-typed programs, e.g. neg/tcpoly_overloaded.scala + + /** True if `tp1` and `tp2` have compatible type constructors and their + * corresponding arguments are subtypes relative to their variance (see `isSubArgs`). + */ + def isMatchingApply(tp1: Type): Boolean = tp1 match { + case AppliedType(tycon1, args1) => + tycon1.dealias match { + case tycon1: TypeParamRef => + (tycon1 == tycon2 || + canConstrain(tycon1) && tryInstantiate(tycon1, tycon2)) && + isSubArgs(args1, args2, tparams) + case tycon1: TypeRef => + tycon2.dealias match { + case tycon2: TypeRef if tycon1.symbol == tycon2.symbol => + isSubType(tycon1.prefix, tycon2.prefix) && + isSubArgs(args1, args2, tparams) + case _ => + false + } + case tycon1: TypeVar => + isMatchingApply(tycon1.underlying) + case tycon1: AnnotatedType => + isMatchingApply(tycon1.underlying) + case _ => + false + } + case _ => + false + } + + /** `param2` can be instantiated to a type application prefix of the LHS + * or to a type application prefix of one of the LHS base class instances + * and the resulting type application is a supertype of `tp1`, + * or fallback to fourthTry. + */ + def canInstantiate(tycon2: TypeParamRef): Boolean = { + + /** Let + * + * `tparams_1, ..., tparams_k-1` be the type parameters of the rhs + * `tparams1_1, ..., tparams1_n-1` be the type parameters of the constructor of the lhs + * `args1_1, ..., args1_n-1` be the type arguments of the lhs + * `d = n - k` + * + * Returns `true` iff `d >= 0` and `tycon2` can be instantiated to + * + * [tparams1_d, ... tparams1_n-1] -> tycon1a[args_1, ..., args_d-1, tparams_d, ... tparams_n-1] + * + * such that the resulting type application is a supertype of `tp1`. + */ + def tyconOK(tycon1a: Type, args1: List[Type]) = { + var tycon1b = tycon1a + val tparams1a = tycon1a.typeParams + val lengthDiff = tparams1a.length - tparams.length + lengthDiff >= 0 && { + val tparams1 = tparams1a.drop(lengthDiff) + variancesConform(tparams1, tparams) && { + if (lengthDiff > 0) + tycon1b = HKTypeLambda(tparams1.map(_.paramName))( + tl => tparams1.map(tparam => tl.integrate(tparams, tparam.paramInfo).bounds), + tl => tycon1a.appliedTo(args1.take(lengthDiff) ++ + tparams1.indices.toList.map(TypeParamRef(tl, _)))) + (ctx.mode.is(Mode.TypevarsMissContext) || + tryInstantiate(tycon2, tycon1b.ensureHK)) && + isSubType(tp1, tycon1b.appliedTo(args2)) + } + } + } + + tp1.widen match { + case tp1w @ HKApply(tycon1, args1) => + tyconOK(tycon1, args1) + case tp1w => + tp1w.typeSymbol.isClass && { + val classBounds = tycon2.classSymbols + def liftToBase(bcs: List[ClassSymbol]): Boolean = bcs match { + case bc :: bcs1 => + classBounds.exists(bc.derivesFrom) && + tyconOK(tp1w.baseTypeTycon(bc), tp1w.baseArgInfos(bc)) || + liftToBase(bcs1) + case _ => + false + } + liftToBase(tp1w.baseClasses) + } || + fourthTry(tp1, tp2) + } + } + + /** Fall back to comparing either with `fourthTry` or against the lower + * approximation of the rhs. + * @param tyconLo The type constructor's lower approximation. + */ + def fallback(tyconLo: Type) = + either(fourthTry(tp1, tp2), isSubType(tp1, tyconLo.applyIfParameterized(args2))) + + /** Let `tycon2bounds` be the bounds of the RHS type constructor `tycon2`. + * Let `app2 = tp2` where the type constructor of `tp2` is replaced by + * `tycon2bounds.lo`. + * If both bounds are the same, continue with `tp1 <:< app2`. + * otherwise continue with either + * + * tp1 <:< tp2 using fourthTry (this might instantiate params in tp1) + * tp1 <:< app2 using isSubType (this might instantiate params in tp2) + */ + def compareLower(tycon2bounds: TypeBounds, tyconIsTypeRef: Boolean): Boolean = + if (tycon2bounds.lo eq tycon2bounds.hi) + isSubType(tp1, + if (tyconIsTypeRef) tp2.superType + else tycon2bounds.lo.applyIfParameterized(args2)) + else + fallback(tycon2bounds.lo) + + tycon2 match { + case param2: TypeParamRef => + isMatchingApply(tp1) || + canConstrain(param2) && canInstantiate(param2) || + compareLower(bounds(param2), tyconIsTypeRef = false) + case tycon2: TypeRef => + isMatchingApply(tp1) || { + tycon2.info match { + case tycon2: TypeBounds => + compareLower(tycon2, tyconIsTypeRef = true) + case tycon2: ClassInfo => + val base = tp1.baseType(tycon2.cls) + if (base.exists && base.ne(tp1)) isSubType(base, tp2) + else fourthTry(tp1, tp2) + case _ => + fourthTry(tp1, tp2) + } + } + case _: TypeVar | _: AnnotatedType => + isSubType(tp1, tp2.superType) + case tycon2: AppliedType => + fallback(tycon2.lowerBound) + case _ => + false + } + } + + /** Subtype test for the application `tp1 = tycon1[args1]`. + */ + def compareAppliedType1(tp1: AppliedType, tycon1: Type, args1: List[Type], tp2: Type): Boolean = + tycon1 match { + case param1: TypeParamRef => + def canInstantiate = tp2 match { + case AnyAppliedType(tycon2, args2) => + tryInstantiate(param1, tycon2.ensureHK) && isSubArgs(args1, args2, tycon2.typeParams) + case _ => + false + } + canConstrain(param1) && canInstantiate || + isSubType(bounds(param1).hi.applyIfParameterized(args1), tp2) + case tycon1: TypeRef if tycon1.symbol.isClass => + false + case tycon1: TypeProxy => + isSubType(tp1.superType, tp2) + case _ => + false + } + /** Subtype test for corresponding arguments in `args1`, `args2` according to * variances in type parameters `tparams`. */ @@ -983,10 +1155,11 @@ 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, RecTypes, And/Or-Types or AnnotatedTypes. + * combiners are AppliedTypes, RefinedTypes, RecTypes, And/Or-Types or AnnotatedTypes. */ private def isCovered(tp: Type): Boolean = tp.dealias.stripTypeVar match { case tp: TypeRef => tp.symbol.isClass && tp.symbol != NothingClass && tp.symbol != NullClass + case tp: AppliedType => isCovered(tp.tycon) case tp: RefinedOrRecType => isCovered(tp.parent) case tp: AnnotatedType => isCovered(tp.underlying) case tp: AndOrType => isCovered(tp.tp1) && isCovered(tp.tp2) @@ -1200,6 +1373,38 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { final def lub(tps: List[Type]): Type = ((defn.NothingType: Type) /: tps)(lub(_,_, canConstrain = false)) + def lubArgs(args1: List[Type], args2: List[Type], tparams: List[TypeParamInfo], canConstrain: Boolean = false): List[Type] = + tparams match { + case tparam :: tparamsRest => + val arg1 :: args1Rest = args1 + val arg2 :: args2Rest = args2 + val v = tparam.paramVariance + val lubArg = + if (v > 0) lub(arg1.hiBound, arg2.hiBound, canConstrain) + else if (v < 0) glb(arg1.loBound, arg2.loBound) + else TypeBounds(glb(arg1.loBound, arg2.loBound), + lub(arg1.hiBound, arg2.hiBound, canConstrain)) + lubArg :: lubArgs(args1Rest, args2Rest, tparamsRest, canConstrain) + case nil => + Nil + } + + def glbArgs(args1: List[Type], args2: List[Type], tparams: List[TypeParamInfo]): List[Type] = + tparams match { + case tparam :: tparamsRest => + val arg1 :: args1Rest = args1 + val arg2 :: args2Rest = args2 + val v = tparam.paramVariance + val glbArg = + if (v > 0) glb(arg1.hiBound, arg2.hiBound) + else if (v < 0) lub(arg1.loBound, arg2.loBound) + else TypeBounds(lub(arg1.loBound, arg2.loBound), + glb(arg1.hiBound, arg2.hiBound)) + glbArg :: glbArgs(args1Rest, args2Rest, tparamsRest) + case nil => + Nil + } + private def recombineAndOr(tp: AndOrType, tp1: Type, tp2: Type) = if (!tp1.exists) tp2 else if (!tp2.exists) tp1 @@ -1339,6 +1544,13 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { * @pre !(tp1 <: tp2) && !(tp2 <:< tp1) -- these cases were handled before */ private def distributeAnd(tp1: Type, tp2: Type): Type = tp1 match { + case tp1 @ AppliedType(tycon1, args1) => + tp2 match { + case AppliedType(tycon2, args2) if tycon1.typeSymbol == tycon2.typeSymbol => + (tycon1 & tycon2).appliedTo(glbArgs(args1, args2, tycon1.typeParams)) + case _ => + NoType + } // opportunistically merge same-named refinements // this does not change anything semantically (i.e. merging or not merging // gives =:= types), but it keeps the type smaller. @@ -1553,17 +1765,17 @@ class ExplainingTypeComparer(initctx: Context) extends TypeComparer(initctx) { override def copyIn(ctx: Context) = new ExplainingTypeComparer(ctx) - override def compareHkApply2(tp1: Type, tp2: HKApply, tycon2: Type, args2: List[Type]): Boolean = { + override def compareAppliedType2(tp1: Type, tp2: AppliedType, tycon2: Type, args2: List[Type]): Boolean = { def addendum = "" - traceIndented(i"compareHkApply2 $tp1, $tp2$addendum") { - super.compareHkApply2(tp1, tp2, tycon2, args2) + traceIndented(i"compareAppliedType2 $tp1, $tp2$addendum") { + super.compareAppliedType2(tp1, tp2, tycon2, args2) } } - override def compareHkApply1(tp1: HKApply, tycon1: Type, args1: List[Type], tp2: Type): Boolean = { + override def compareAppliedType1(tp1: AppliedType, tycon1: Type, args1: List[Type], tp2: Type): Boolean = { def addendum = "" - traceIndented(i"compareHkApply1 $tp1, $tp2$addendum") { - super.compareHkApply1(tp1, tycon1, args1, tp2) + traceIndented(i"compareAppliedType1 $tp1, $tp2$addendum") { + super.compareAppliedType1(tp1, tycon1, args1, tp2) } } diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index 5e67e191e5cf..079e296ef495 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -381,6 +381,8 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean else if (defn.isPhantomTerminalClass(sym)) PhantomErasure.erasedPhantomType else if (sym eq defn.PhantomClass) defn.ObjectType // To erase the definitions of Phantom.{assume, Any, Nothing} else eraseNormalClassRef(tp) + case tp: AppliedType => + apply(tp.superType) case tp: RefinedType => val parent = tp.parent if (parent isRef defn.ArrayClass) eraseArray(tp) @@ -509,8 +511,6 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean */ private def sigName(tp: Type)(implicit ctx: Context): TypeName = try { tp match { - case ErasedValueType(_, underlying) => - sigName(underlying) case tp: TypeRef => if (!tp.denot.exists) throw new MissingType(tp.prefix, tp.name) val sym = tp.symbol @@ -529,6 +529,10 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean sigName(PhantomErasure.erasedPhantomType) else normalizeClass(sym.asClass).fullName.asTypeName + case tp: AppliedType => + sigName(tp.superType) + case ErasedValueType(_, underlying) => + sigName(underlying) case defn.ArrayOf(elem) => sigName(this(tp)) case tp: HKApply => diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index 73f57131646f..1d6b74626c05 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -11,6 +11,7 @@ import NameKinds.DepParamName import Decorators._ import StdNames._ import Annotations._ +import annotation.tailrec import config.Config import util.{SimpleMap, Property} import collection.mutable @@ -83,6 +84,18 @@ trait TypeOps { this: Context => // TODO: Make standalone object. } } + @tailrec + def argForParam(pre: Type, cls: Symbol, pref: TypeParamRef, prefCls: ClassSymbol): Type = + if (cls.is(PackageClass)) pref + else { + val base = pre.baseType(cls) + if (cls eq prefCls) { + val AppliedType(_, args) = base + args(pref.paramNum) + } + else argForParam(base.normalizedPrefix, cls.owner.enclosingClass, pref, prefCls) + } + /*>|>*/ ctx.conditionalTraceIndented(TypeOps.track, s"asSeen ${tp.show} from (${pre.show}, ${cls.show})", show = true) /*<|<*/ { // !!! DEBUG tp match { case tp: NamedType => @@ -103,6 +116,11 @@ trait TypeOps { this: Context => // TODO: Make standalone object. } case tp: ThisType => toPrefix(pre, cls, tp.cls) + case tp: TypeParamRef => + tp.binder.resultType match { + case cinfo: ClassInfo => argForParam(pre, cls, tp, cinfo.cls) + case _ => tp + } case _: BoundType | NoPrefix => tp case tp: RefinedType => @@ -163,7 +181,10 @@ trait TypeOps { this: Context => // TODO: Make standalone object. val bounds = ctx.typeComparer.bounds(tp) if (bounds.lo.isRef(defn.NothingClass)) bounds.hi else bounds.lo } - else typerState.constraint.typeVarOfParam(tp) orElse tp + else { + val tvar = typerState.constraint.typeVarOfParam(tp) + if (tvar.exists) tvar else tp + } case _: ThisType | _: BoundType | NoPrefix => tp case tp: RefinedType => @@ -213,14 +234,22 @@ trait TypeOps { this: Context => // TODO: Make standalone object. defn.ObjectClass :: Nil } - def mergeRefined(tp1: Type, tp2: Type): Type = { + def mergeRefinedOrApplied(tp1: Type, tp2: Type): Type = { def fail = throw new AssertionError(i"Failure to join alternatives $tp1 and $tp2") tp1 match { case tp1 @ RefinedType(parent1, name1, rinfo1) => tp2 match { case RefinedType(parent2, `name1`, rinfo2) => tp1.derivedRefinedType( - mergeRefined(parent1, parent2), name1, rinfo1 | rinfo2) + mergeRefinedOrApplied(parent1, parent2), name1, rinfo1 | rinfo2) + case _ => fail + } + case tp1 @ AppliedType(tycon1, args1) => + tp2 match { + case AppliedType(tycon2, args2) => + tp1.derivedAppliedType( + mergeRefinedOrApplied(tycon1, tycon2), + ctx.typeComparer.lubArgs(args1, args2, tycon1.typeParams)) case _ => fail } case tp1 @ TypeRef(pre1, name1) => @@ -258,7 +287,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object. val base = if (tp1.typeParams.nonEmpty) tp.baseTypeTycon(cls) else tp.baseTypeWithArgs(cls) - base.mapReduceOr(identity)(mergeRefined) + base.mapReduceOr(identity)(mergeRefinedOrApplied) } doms.map(baseTp).reduceLeft(AndType.apply) } @@ -273,35 +302,6 @@ trait TypeOps { this: Context => // TODO: Make standalone object. } } - /** 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 = { - denot setFlag formal.flags & RetainedTypeArgFlags - denot.info = info - } - } - val sym = ctx.newSymbol( - cls, formal.name, - formal.flagsUNSAFE & RetainedTypeArgFlags | BaseTypeArg | Override, - lazyInfo, - coord = cls.coord) - cls.enter(sym, decls) - } - /** If `tpe` is of the form `p.x` where `p` refers to a package * but `x` is not owned by a package, expand it to * @@ -324,6 +324,21 @@ trait TypeOps { this: Context => // TODO: Make standalone object. } } + 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 = { + denot setFlag formal.flags & RetainedTypeArgFlags + denot.info = info + } + } + val sym = ctx.newSymbol( + cls, formal.name, + formal.flagsUNSAFE & RetainedTypeArgFlags | BaseTypeArg | Override, + lazyInfo, + coord = cls.coord) + cls.enter(sym, decls) + } + /** Normalize a list of parent types of class `cls` that may contain refinements * to a list of typerefs referring to classes, by converting all refinements to member * definitions in scope `decls`. Can add members to `decls` as a side-effect. diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 382cf0fddfb9..c3e239103561 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1254,6 +1254,16 @@ object Types { case _ => TypeAlias(this) } + final def loBound: Type = this match { + case tp: TypeBounds => tp.lo + case _ => this + } + + final def hiBound: Type = this match { + case tp: TypeBounds => tp.hi + case _ => this + } + /** The type parameter with given `name`. This tries first `decls` * in order not to provoke a cycle by forcing the info. If that yields * no symbol it tries `member` as an alternative. @@ -1391,7 +1401,7 @@ object Types { /** The closest supertype of this type. This is the same as `underlying`, * except that * - instead of a TyperBounds type it returns its upper bound, and - * - for HKApplys it returns the upper bound of the constructor re-applied to the arguments. + * - for applied types it returns the upper bound of the constructor re-applied to the arguments. */ def superType(implicit ctx: Context): Type = underlying match { case TypeBounds(_, hi) => hi @@ -3735,6 +3745,8 @@ object Types { } if ((tp.cls is Trait) || zeroParams(tp.cls.primaryConstructor.info)) tp // !!! needs to be adapted once traits have parameters else NoType + case tp: AppliedType => + zeroParamClass(tp.superType) case tp: TypeRef => zeroParamClass(tp.underlying) case tp: RefinedType => @@ -3800,6 +3812,8 @@ object Types { tp.derivedTypeBounds(lo, hi) protected def derivedSuperType(tp: SuperType, thistp: Type, supertp: Type): Type = tp.derivedSuperType(thistp, supertp) + protected def derivedAppliedType(tp: AppliedType, tycon: Type, args: List[Type]): Type = + tp.derivedAppliedType(tycon, args) protected def derivedAppliedType(tp: HKApply, tycon: Type, args: List[Type]): Type = tp.derivedAppliedType(tycon, args) protected def derivedAndOrType(tp: AndOrType, tp1: Type, tp2: Type): Type = @@ -3841,6 +3855,16 @@ object Types { | _: BoundType | NoPrefix => tp + case tp: AppliedType => + def mapArg(arg: Type, tparam: ParamInfo): Type = { + val saved = variance + variance *= tparam.paramVariance + try this(arg) + finally variance = saved + } + derivedAppliedType(tp, this(tp.tycon), + tp.args.zipWithConserve(tp.typeParams)(mapArg)) + case tp: RefinedType => derivedRefinedType(tp, this(tp.parent), this(tp.refinedInfo)) @@ -4005,6 +4029,9 @@ object Types { override protected def derivedSuperType(tp: SuperType, thistp: Type, supertp: Type) = if (thistp.exists && supertp.exists) tp.derivedSuperType(thistp, supertp) else NoType + override protected def derivedAppliedType(tp: AppliedType, tycon: Type, args: List[Type]): Type = + if (tycon.exists && args.forall(_.exists)) tp.derivedAppliedType(tycon, args) + else approx() // This is rather coarse, but to do better is a bit complicated override protected def derivedAppliedType(tp: HKApply, tycon: Type, args: List[Type]): Type = if (tycon.exists && args.forall(_.exists)) tp.derivedAppliedType(tycon, args) else approx() // This is rather coarse, but to do better is a bit complicated @@ -4058,6 +4085,23 @@ object Types { | _: BoundType | NoPrefix => x + case tp @ AppliedType(tycon, args) => + @tailrec def foldArgs(x: T, tparams: List[ParamInfo], args: List[Type]): T = + if (args.isEmpty) { + assert(tparams.isEmpty) + x + } + else { + val tparam = tparams.head + val saved = variance + variance *= tparam.paramVariance + val acc = + try this(x, args.head) + finally variance = saved + foldArgs(acc, tparams.tail, args.tail) + } + foldArgs(this(x, tycon), tp.typeParams, args) + case tp: RefinedType => this(this(x, tp.parent), tp.refinedInfo) diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index 4c73115cac50..585085f1d9e5 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -329,11 +329,11 @@ class ClassfileParser( case '*' => TypeBounds.empty } if (formals != null) - tp1 = RefinedType(tp1, formals.head.name, bounds) + tp1 = RefinedType(tp1, formals.head.name, bounds) // @!!! case _ => val info = sig2type(tparams, skiptvs) if (formals != null) - tp1 = RefinedType(tp1, formals.head.name, TypeAlias(info)) + tp1 = RefinedType(tp1, formals.head.name, TypeAlias(info)) // @!!! } if (formals != null) formals = formals.tail diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index c36ce1dc14bb..f9ce4f07b9ab 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -670,6 +670,14 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas } if (tycon1 ne tycon) elim(tycon1.appliedTo(args)) else tp.derivedAppliedType(tycon, args.map(mapArg)) + case tp @ AppliedType(tycon, args) => + val tycon1 = tycon.safeDealias + def mapArg(arg: Type) = arg match { + case arg: TypeRef if isBound(arg) => arg.symbol.info + case _ => arg + } + if (tycon1 ne tycon) elim(tycon1.appliedTo(args)) + else tp.derivedAppliedType(tycon, args.map(mapArg)) case _ => tp } diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index cf409a18103b..908c86b63a8d 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -61,6 +61,8 @@ class PlainPrinter(_ctx: Context) extends Printer { homogenize(tp.info) case tp: LazyRef => homogenize(tp.ref) + case AppliedType(tycon, args) => + tycon.dealias.appliedTo(args) case HKApply(tycon, args) => tycon.dealias.appliedTo(args) case _ => @@ -189,6 +191,8 @@ class PlainPrinter(_ctx: Context) extends Printer { ParamRefNameString(tp) ~ ".type" case AnnotatedType(tpe, annot) => toTextLocal(tpe) ~ " " ~ toText(annot) + case AppliedType(tycon, args) => + toTextLocal(tycon) ~ "[" ~ Text(args.map(argText), ", ") ~ "]" case HKApply(tycon, args) => toTextLocal(tycon) ~ "[" ~ Text(args.map(argText), ", ") ~ "]" case tp: TypeVar => diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index b1d1d77215ba..ce26492d065e 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -457,6 +457,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { /* Erase a type binding according to erasure semantics in pattern matching */ def erase(tp: Type): Type = { def doErase(tp: Type): Type = tp match { + case tp: AppliedType => erase(tp.superType) case tp: HKApply => erase(tp.superType) case tp: RefinedType => erase(tp.parent) case _ => tp @@ -565,9 +566,13 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { * * If `tp1` is `path1.A`, `tp2` is `path2.B`, and `path1` is subtype of * `path2`, then return `path1.B`. + * + * (MO) I don't really understand what this does. Let's try to find a precise + * definition! */ def refine(tp1: Type, tp2: Type): Type = (tp1, tp2) match { case (tp1: RefinedType, _: TypeRef) => tp1.wrapIfMember(refine(tp1.parent, tp2)) + case (tp1: AppliedType, _) => refine(tp1.superType, tp2) case (tp1: HKApply, _) => refine(tp1.superType, tp2) case (TypeRef(ref1: TypeProxy, _), tp2 @ TypeRef(ref2: TypeProxy, _)) => if (ref1.underlying <:< ref2.underlying) tp2.derivedSelect(ref1) else tp2 diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 90586f249670..57d753b23556 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -83,7 +83,16 @@ object Checking { HKTypeLambda.fromParams(tparams, bound).appliedTo(args) checkBounds(orderedArgs, bounds, instantiate) - def checkWildcardHKApply(tp: Type, pos: Position): Unit = tp match { + def checkWildcardApply(tp: Type, pos: Position): Unit = tp match { + case tp @ AppliedType(tycon, args) if args.exists(_.isInstanceOf[TypeBounds]) => + tycon match { + case tycon: TypeLambda => + ctx.errorOrMigrationWarning( + ex"unreducible application of higher-kinded type $tycon to wildcard arguments", + pos) + case _ => + checkWildcardApply(tp.superType, pos) + } case tp @ HKApply(tycon, args) if args.exists(_.isInstanceOf[TypeBounds]) => tycon match { case tycon: TypeLambda => @@ -91,13 +100,13 @@ object Checking { ex"unreducible application of higher-kinded type $tycon to wildcard arguments", pos) case _ => - checkWildcardHKApply(tp.superType, pos) + checkWildcardApply(tp.superType, pos) } case _ => } - def checkValidIfHKApply(implicit ctx: Context): Unit = - checkWildcardHKApply(tycon.tpe.appliedTo(args.map(_.tpe)), tree.pos) - checkValidIfHKApply(ctx.addMode(Mode.AllowLambdaWildcardApply)) + def checkValidIfApply(implicit ctx: Context): Unit = + checkWildcardApply(tycon.tpe.appliedTo(args.map(_.tpe)), tree.pos) + checkValidIfApply(ctx.addMode(Mode.AllowLambdaWildcardApply)) } /** Check that kind of `arg` has the same outline as the kind of paramBounds. @@ -213,6 +222,8 @@ object Checking { case tp: TermRef => this(tp.info) mapOver(tp) + case tp @ AppliedType(tycon, args) => + tp.derivedAppliedType(this(tycon), args.map(this(_, nestedCycleOK, nestedCycleOK))) case tp @ RefinedType(parent, name, rinfo) => tp.derivedRefinedType(this(parent), name, this(rinfo, nestedCycleOK, nestedCycleOK)) case tp: RecType => @@ -234,7 +245,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 _: RefinedOrRecType | _: HKApply => true + case _: RefinedOrRecType | _: HKApply | _: AppliedType => true case _ => false } if (isInteresting(pre)) { diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 10cfcf87cf15..46452a065a19 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -385,6 +385,13 @@ trait ImplicitRunInfo { self: RunInfo => (lead /: tp.classSymbols)(joinClass) case tp: TypeVar => apply(tp.underlying) + case tp: AppliedType => + def applyArg(arg: Type) = arg match { + case TypeBounds(lo, hi) => AndType.make(lo, hi) + case _: WildcardType => defn.AnyType + case _ => arg + } + (apply(tp.tycon) /: tp.args)((tc, arg) => AndType.make(tc, applyArg(arg))) case tp: HKApply => def applyArg(arg: Type) = arg match { case TypeBounds(lo, hi) => AndType.make(lo, hi) diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index 32acd28fb32e..2d11b52d7ab2 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -474,6 +474,11 @@ object ProtoTypes { case tp: NamedType => // default case, inlined for speed if (tp.symbol.isStatic) tp else tp.derivedSelect(wildApprox(tp.prefix, theMap, seen)) + case tp @ AppliedType(tycon, args) => + wildApprox(tycon, theMap, seen) match { + case _: WildcardType => WildcardType // this ensures we get a * type + case tycon1 => tp.derivedAppliedType(tycon1, args.mapConserve(wildApprox(_, theMap, seen))) + } case tp: RefinedType => // default case, inlined for speed tp.derivedRefinedType( wildApprox(tp.parent, theMap, seen), diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 6f2fe09fa887..1ad3edfc9f66 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -102,6 +102,8 @@ trait TypeAssigner { case _ => mapOver(tp) } + case tp @ AppliedType(tycon, args) if toAvoid(tycon) => + apply(tp.superType) case tp @ HKApply(tycon, args) if toAvoid(tycon) => apply(tp.superType) case tp @ AnyAppliedType(tycon, args) if toAvoid(tycon) => diff --git a/compiler/src/dotty/tools/dotc/typer/Variances.scala b/compiler/src/dotty/tools/dotc/typer/Variances.scala index aeeef02755c5..20fadccc3d7d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Variances.scala +++ b/compiler/src/dotty/tools/dotc/typer/Variances.scala @@ -83,6 +83,17 @@ object Variances { flip(varianceInTypes(tp.paramInfos)(tparam)) & varianceInType(tp.resultType)(tparam) case ExprType(restpe) => varianceInType(restpe)(tparam) + case tp @ AppliedType(tycon, args) => + def varianceInArgs(v: Variance, args: List[Type], tparams: List[ParamInfo]): Variance = + args match { + case arg :: args1 => + varianceInArgs( + v & compose(varianceInType(arg)(tparam), tparams.head.paramVariance), + args1, tparams.tail) + case nil => + v + } + varianceInArgs(varianceInType(tycon)(tparam), args, tycon.typeParams) case tp @ HKApply(tycon, args) => def varianceInArgs(v: Variance, args: List[Type], tparams: List[ParamInfo]): Variance = args match { diff --git a/doc-tool/src/dotty/tools/dottydoc/model/factories.scala b/doc-tool/src/dotty/tools/dottydoc/model/factories.scala index 563a20938372..705a06580a89 100644 --- a/doc-tool/src/dotty/tools/dottydoc/model/factories.scala +++ b/doc-tool/src/dotty/tools/dottydoc/model/factories.scala @@ -176,7 +176,7 @@ object factories { paramLists(annot.tpe) case (_: TypeParamRef | _: RefinedType | _: TypeRef | _: ThisType | - _: ExprType | _: OrType | _: AndType | _: HKApply | + _: ExprType | _: OrType | _: AndType | _: HKApply | _: AppliedType | _: TermRef | _: ConstantType) => Nil // return types should not be in the paramlist } From 2a070da5dbf80e7e5cfcf9c0c3cce6736aa8ca41 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 26 Jul 2017 13:24:11 +0200 Subject: [PATCH 12/18] Introduce TypeArgRef Also, change asSeenFrom to use it for arguments --- .../src/dotty/tools/dotc/ast/Desugar.scala | 5 +- .../tools/dotc/core/ConstraintHandling.scala | 12 +- .../src/dotty/tools/dotc/core/Contexts.scala | 2 +- .../dotty/tools/dotc/core/Definitions.scala | 5 +- .../src/dotty/tools/dotc/core/Flags.scala | 4 +- .../dotty/tools/dotc/core/Substituters.scala | 2 +- .../tools/dotc/core/SymDenotations.scala | 39 ++++--- .../src/dotty/tools/dotc/core/Symbols.scala | 2 +- .../tools/dotc/core/TypeApplications.scala | 64 ++++------ .../dotty/tools/dotc/core/TypeComparer.scala | 66 ++++++----- .../dotty/tools/dotc/core/TypeErasure.scala | 24 ++-- .../src/dotty/tools/dotc/core/TypeOps.scala | 86 +++++++------- .../src/dotty/tools/dotc/core/Types.scala | 109 +++++++++++++----- .../dotc/core/classfile/ClassfileParser.scala | 35 +++--- .../tools/dotc/core/tasty/TastyFormat.scala | 5 +- .../tools/dotc/core/tasty/TreePickler.scala | 5 +- .../tools/dotc/core/tasty/TreeUnpickler.scala | 8 +- .../tools/dotc/printing/PlainPrinter.scala | 7 +- .../tools/dotc/printing/RefinedPrinter.scala | 2 +- .../tools/dotc/transform/CheckReentrant.scala | 4 +- .../tools/dotc/transform/PostTyper.scala | 2 +- .../localopt/InlineCaseIntrinsics.scala | 3 +- .../dotty/tools/dotc/typer/Applications.scala | 2 +- .../src/dotty/tools/dotc/typer/Checking.scala | 33 ++++-- .../dotty/tools/dotc/typer/Implicits.scala | 10 +- .../dotty/tools/dotc/typer/RefChecks.scala | 4 +- .../dotty/tools/dotc/typer/TypeAssigner.scala | 2 +- .../src/dotty/tools/dotc/typer/Typer.scala | 15 +-- .../tools/dottydoc/model/factories.scala | 2 +- 29 files changed, 330 insertions(+), 229 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index d311ac834450..296411d059d2 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -245,7 +245,7 @@ object desugar { * class C { type v C$T; type v T = C$T } */ def typeDef(tdef: TypeDef)(implicit ctx: Context): Tree = { - if (tdef.mods is PrivateLocalParam) { + if (tdef.mods.is(PrivateLocalParam) && !dotty.tools.dotc.config.Config.newScheme) { val tparam = cpy.TypeDef(tdef)(name = tdef.name.expandedName(ctx.owner)) .withMods(tdef.mods &~ PrivateLocal) val alias = cpy.TypeDef(tdef)(rhs = refOfDef(tparam)) @@ -1132,7 +1132,8 @@ object desugar { */ def refinedTypeToClass(parent: tpd.Tree, refinements: List[Tree])(implicit ctx: Context): TypeDef = { def stripToCore(tp: Type): List[Type] = tp match { - case tp: RefinedType if tp.argInfos.nonEmpty => tp :: Nil // parameterized class type + case tp: AppliedType => tp :: Nil + case tp: RefinedType if !config.Config.newScheme && tp.argInfos.nonEmpty => tp :: Nil // parameterized class type case tp: TypeRef if tp.symbol.isClass => tp :: Nil // monomorphic class type case tp: TypeProxy => stripToCore(tp.underlying) case AndType(tp1, tp2) => stripToCore(tp1) ::: stripToCore(tp2) diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala index 15f24a7fe50c..206eb616bb58 100644 --- a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -6,7 +6,7 @@ import Types._, Contexts._, Symbols._ import Decorators._ import config.Config import config.Printers.{constr, typr} -import TypeApplications.EtaExpansion +import TypeApplications.{EtaExpansion, TypeParamInfo} import collection.mutable /** Methods for adding constraints and solving them. @@ -194,9 +194,17 @@ trait ConstraintHandling { final def approximation(param: TypeParamRef, fromBelow: Boolean): Type = { val avoidParam = new TypeMap { override def stopAtStatic = true + def avoidInArg(arg: Type, formal: TypeParamInfo): Type = + if (param.occursIn(arg)) TypeBounds.empty else arg def apply(tp: Type) = mapOver { tp match { - case tp: RefinedType if param occursIn tp.refinedInfo => tp.parent + case tp @ AppliedType(tycon, args) => + tp.derivedAppliedType(tycon, + args.zipWithConserve(tycon.typeParams)(avoidInArg)) + case tp: RefinedType if param occursIn tp.refinedInfo => + assert(fromBelow || variance <= 0, + "unsound approximation of $param with bounds ${constraint.fullUpperBound(param)}") + tp.parent case tp: WildcardType => val bounds = tp.optBounds.orElse(TypeBounds.empty).bounds // Try to instantiate the wildcard to a type that is known to conform to it. diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index 97624aab777a..4386aa7e9729 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -605,7 +605,7 @@ object Contexts { } /** A table for hash consing unique refined types */ - private[dotc] val uniqueRefinedTypes = new RefinedUniques + private[dotc] val uniqueRefinedTypes = new RefinedUniques // @!!! replace with uniqueAppliedTypes /** A table for hash consing unique named types */ private[core] val uniqueNamedTypes = new NamedTypeUniques diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 452dc1a03357..86f29dfcab69 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -82,7 +82,7 @@ class Definitions { if (tpe.typeParams.nonEmpty) tpe.appliedTo(typeParam.typeRef) else tpe val parents = parentConstrs.toList map instantiate - val parentRefs: List[TypeRef] = ctx.normalizeToClassRefs(parents, cls, paramDecls) + val parentRefs = ctx.normalizeToClassRefs(parents, cls, paramDecls) denot.info = ClassInfo(ScalaPackageClass.thisType, cls, parentRefs, paramDecls) } } @@ -721,7 +721,8 @@ class Definitions { if (ctx.erasedTypes) JavaArrayType(elem) else ArrayType.appliedTo(elem :: Nil) def unapply(tp: Type)(implicit ctx: Context): Option[Type] = tp.dealias match { - case at: RefinedType if (at isRef ArrayType.symbol) && at.argInfos.length == 1 => Some(at.argInfos.head) + case AppliedType(at, arg :: Nil) if at isRef ArrayType.symbol => Some(arg) + case at: RefinedType if (at isRef ArrayType.symbol) && at.argInfos.length == 1 => Some(at.argInfos.head) // @!!! case _ => None } } diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index 59135bf21441..6ec87012cdc9 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -250,7 +250,7 @@ object Flags { */ final val ParamAccessor = commonFlag(14, "") final val TermParamAccessor = ParamAccessor.toTermFlags - final val TypeParamAccessor = ParamAccessor.toTypeFlags + final val TypeParamAccessor = ParamAccessor.toTypeFlags // @!!! /** A value or class implementing a module */ final val Module = commonFlag(15, "module") @@ -306,7 +306,7 @@ object Flags { /** A binding for a type parameter of a base class or trait. */ - final val BaseTypeArg = typeFlag(25, "") + final val BaseTypeArg = typeFlag(25, "") // @!!! final val CaseAccessorOrBaseTypeArg = CaseAccessor.toCommonFlags diff --git a/compiler/src/dotty/tools/dotc/core/Substituters.scala b/compiler/src/dotty/tools/dotc/core/Substituters.scala index d565ec2293aa..fabcd5712d26 100644 --- a/compiler/src/dotty/tools/dotc/core/Substituters.scala +++ b/compiler/src/dotty/tools/dotc/core/Substituters.scala @@ -16,7 +16,7 @@ trait Substituters { this: Context => else tp.derivedSelect(subst(tp.prefix, from, to, theMap)) case _: ThisType | NoPrefix => tp - case tp: RefinedType => + case tp: RefinedType => // @!!! remove tp.derivedRefinedType(subst(tp.parent, from, to, theMap), tp.refinedName, subst(tp.refinedInfo, from, to, theMap)) case tp: TypeAlias => tp.derivedTypeAlias(subst(tp.alias, from, to, theMap)) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index ef8026e22422..1b2b4c93e8b9 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -46,8 +46,8 @@ trait SymDenotations { this: Context => else { val initial = denot.initial val firstPhaseId = initial.validFor.firstPhaseId.max(ctx.typerPhase.id) - if ((initial ne denot) || ctx.phaseId != firstPhaseId) { - ctx.withPhase(firstPhaseId).stillValidInOwner(initial) || + if ((initial ne denot) || ctx.phaseId != firstPhaseId) { + ctx.withPhase(firstPhaseId).stillValidInOwner(initial) || // Workaround #1895: A symbol might not be entered into an owner // until the second phase where it exists (denot.validFor.containsPhaseId(firstPhaseId + 1)) && @@ -1220,7 +1220,7 @@ object SymDenotations { info2 match { case info2: ClassInfo => info1 match { - case info1: ClassInfo => info1.classParents ne info2.classParents + case info1: ClassInfo => info1.classParentsNEW ne info2.classParentsNEW case _ => completersMatter } case _ => completersMatter @@ -1366,13 +1366,24 @@ object SymDenotations { } /** The denotations of all parents in this class. */ - def classParents(implicit ctx: Context): List[TypeRef] = info match { - case classInfo: ClassInfo => classInfo.classParents + def classParentRefs(implicit ctx: Context): List[TypeRef] = info match { + case classInfo: ClassInfo => classInfo.parentRefs + case _ => Nil + } + + /** The denotations of all parents in this class. */ + def classParentsWithArgs(implicit ctx: Context): List[Type] = info match { + case classInfo: ClassInfo => classInfo.parentsWithArgs + case _ => Nil + } + + def classParentsNEW(implicit ctx: Context): List[Type] = info match { + case classInfo: ClassInfo => classInfo.parentsNEW case _ => Nil } /** The symbol of the superclass, NoSymbol if no superclass exists */ - def superClass(implicit ctx: Context): Symbol = classParents match { + def superClass(implicit ctx: Context): Symbol = classParentsNEW match { case parent :: _ => val cls = parent.classSymbol if (cls is Trait) NoSymbol else cls @@ -1438,10 +1449,10 @@ object SymDenotations { def computeBaseData(implicit onBehalf: BaseData, ctx: Context): (List[ClassSymbol], BaseClassSet) = { def emptyParentsExpected = is(Package) || (symbol == defn.AnyClass) || ctx.erasedTypes && (symbol == defn.ObjectClass) - if (classParents.isEmpty && !emptyParentsExpected) + if (classParentsNEW.isEmpty && !emptyParentsExpected) onBehalf.signalProvisional() val builder = new BaseDataBuilder - for (p <- classParents) builder.addAll(p.symbol.asClass.baseClasses) + for (p <- classParentsNEW) builder.addAll(p.typeSymbol.asClass.baseClasses) (classSymbol :: builder.baseClasses, builder.baseClassSet) } @@ -1567,10 +1578,10 @@ object SymDenotations { val ownDenots = info.decls.denotsNamed(name, selectNonPrivate) if (debugTrace) // DEBUG println(s"$this.member($name), ownDenots = $ownDenots") - def collect(denots: PreDenotation, parents: List[TypeRef]): PreDenotation = parents match { + def collect(denots: PreDenotation, parents: List[Type]): PreDenotation = parents match { case p :: ps => val denots1 = collect(denots, ps) - p.symbol.denot match { + p.typeSymbol.denot match { case parentd: ClassDenotation => denots1 union parentd.nonPrivateMembersNamed(name, inherited = true) @@ -1582,7 +1593,7 @@ object SymDenotations { denots } if (name.isConstructorName) ownDenots - else collect(ownDenots, classParents) + else collect(ownDenots, classParentsNEW) } override final def findMember(name: Name, pre: Type, excluded: FlagSet)(implicit ctx: Context): Denotation = { @@ -1629,7 +1640,7 @@ object SymDenotations { tp else subcls.denot match { case cdenot: ClassDenotation => - if (cdenot.baseClassSet contains symbol) foldGlb(NoType, tp.parentsNEW) // !!! change to parents + if (cdenot.baseClassSet contains symbol) foldGlb(NoType, tp.parentsNEW) else NoType case _ => baseTypeOf(tp.superType) @@ -1684,8 +1695,8 @@ object SymDenotations { def computeMemberNames(keepOnly: NameFilter)(implicit onBehalf: MemberNames, ctx: Context): Set[Name] = { var names = Set[Name]() def maybeAdd(name: Name) = if (keepOnly(thisType, name)) names += name - for (p <- classParents) - for (name <- p.symbol.asClass.memberNames(keepOnly)) + for (p <- classParentsNEW) + for (name <- p.typeSymbol.asClass.memberNames(keepOnly)) maybeAdd(name) val ownSyms = if (keepOnly eq implicitFilter) diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index b5c3baac2cf1..d517f3a14584 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -123,7 +123,7 @@ trait Symbols { this: Context => def complete(denot: SymDenotation)(implicit ctx: Context): Unit = { val cls = denot.asClass.classSymbol val decls = newScope - val parentRefs: List[TypeRef] = normalizeToClassRefs(parentTypes, cls, decls) + val parentRefs = normalizeToClassRefs(parentTypes, cls, decls) denot.info = ClassInfo(owner.thisType, cls, parentRefs, decls) } } diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index 199b63e24880..b1807f9e2a4c 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -25,23 +25,11 @@ object TypeApplications { type TypeParamInfo = ParamInfo.Of[TypeName] /** Assert type is not a TypeBounds instance and return it unchanged */ - val noBounds = (tp: Type) => tp match { + def noBounds(tp: Type) = tp match { case tp: TypeBounds => throw new AssertionError("no TypeBounds allowed") case _ => tp } - /** If `tp` is a TypeBounds instance return its lower bound else return `tp` */ - val boundsToLo = (tp: Type) => tp match { - case tp: TypeBounds => tp.lo - case _ => tp - } - - /** If `tp` is a TypeBounds instance return its upper bound else return `tp` */ - val boundsToHi = (tp: Type) => tp match { - case tp: TypeBounds => tp.hi - case _ => tp - } - /** Does variance `v1` conform to variance `v2`? * This is the case if the variances are the same or `sym` is nonvariant. */ @@ -226,7 +214,7 @@ class TypeApplications(val self: Type) extends AnyVal { else if (!tsym.isCompleting) tsym.info.typeParams else Nil case self: RefinedType => - self.parent.typeParams.filterNot(_.paramName == self.refinedName) + self.parent.typeParams.filterNot(_.paramName == self.refinedName) // @!!! case self: RecType => self.parent.typeParams case _: SingletonType => @@ -259,6 +247,7 @@ class TypeApplications(val self: Type) extends AnyVal { def hkResult(implicit ctx: Context): Type = self.dealias match { case self: TypeRef => self.info.hkResult case self: RefinedType => NoType + case self: AppliedType => NoType case self: HKTypeLambda => self.resultType case self: SingletonType => NoType case self: TypeVar => @@ -401,6 +390,12 @@ class TypeApplications(val self: Type) extends AnyVal { } case nil => t } + def normalizeWildcardArg(arg: Type, tparam: TypeParamInfo): Type = arg match { + case TypeBounds(lo, hi) => + val v = tparam.paramVariance + if (v > 0) hi else if (v < 0) lo else arg + case _ => arg + } val stripped = self.stripTypeVar val dealiased = stripped.safeDealias if (args.isEmpty || ctx.erasedTypes) self @@ -453,8 +448,10 @@ class TypeApplications(val self: Type) extends AnyVal { case _ if typParams.isEmpty || typParams.head.isInstanceOf[LambdaParam] => HKApply(self, args) case dealiased => - if (Config.newScheme) ??? - else matchParams(dealiased, typParams, args) + if (Config.newScheme) + AppliedType(self, args.zipWithConserve(typParams)(normalizeWildcardArg)) + else + matchParams(dealiased, typParams, args) } } @@ -488,10 +485,12 @@ class TypeApplications(val self: Type) extends AnyVal { } /** The type arguments of this type's base type instance wrt. `base`. - * Existential types in arguments are returned as TypeBounds instances. + * Wildcard types in arguments are returned as TypeBounds instances. */ final def baseArgInfos(base: Symbol)(implicit ctx: Context): List[Type] = - if (self derivesFrom base) + if (Config.newScheme) + self.baseType(base).argInfos + else if (self derivesFrom base) self.dealias match { case self: TypeRef if !self.symbol.isClass => self.superType.baseArgInfos(base) case self: HKApply => self.superType.baseArgInfos(base) @@ -500,30 +499,14 @@ class TypeApplications(val self: Type) extends AnyVal { else Nil - /** The type arguments of this type's base type instance wrt.`base`. - * Existential types in arguments are disallowed. - */ - final def baseArgTypes(base: Symbol)(implicit ctx: Context): List[Type] = - baseArgInfos(base) mapConserve noBounds - - /** The type arguments of this type's base type instance wrt.`base`. - * Existential types in arguments are approximated by their lower bound. - */ - final def baseArgTypesLo(base: Symbol)(implicit ctx: Context): List[Type] = - baseArgInfos(base) mapConserve boundsToLo - - /** The type arguments of this type's base type instance wrt.`base`. - * Existential types in arguments are approximated by their upper bound. - */ - final def baseArgTypesHi(base: Symbol)(implicit ctx: Context): List[Type] = - baseArgInfos(base) mapConserve boundsToHi - /** The base type including all type arguments and applicable refinements * of this type. Refinements are applicable if they refine a member of * the parent type which furthermore is not a name-mangled type parameter. * Existential types in arguments are returned as TypeBounds instances. */ - final def baseTypeWithArgs(base: Symbol)(implicit ctx: Context): Type = ctx.traceIndented(s"btwa ${self.show} wrt $base", core, show = true) { + final def baseTypeWithArgs(base: Symbol)(implicit ctx: Context): Type = + if (Config.newScheme) self.baseType(base) + else ctx.traceIndented(s"btwa ${self.show} wrt $base", core, show = true) { def default = self.baseTypeTycon(base).appliedTo(baseArgInfos(base)) def isExpandedTypeParam(sym: Symbol) = sym.is(TypeParam) && sym.name.is(ExpandedName) self match { @@ -557,6 +540,7 @@ class TypeApplications(val self: Type) extends AnyVal { case _ => if (self.derivesFrom(from)) if (ctx.erasedTypes) to.typeRef + else if (Config.newScheme) to.typeRef.appliedTo(self.baseType(from).argInfos) else RefinedType(to.typeRef, to.typeParams.head.name, self.member(from.typeParams.head.name).info) else self } @@ -584,10 +568,10 @@ class TypeApplications(val self: Type) extends AnyVal { def argTypes(implicit ctx: Context) = argInfos mapConserve noBounds /** Argument types where existential types in arguments are approximated by their lower bound */ - def argTypesLo(implicit ctx: Context) = argInfos mapConserve boundsToLo + def argTypesLo(implicit ctx: Context) = argInfos.mapConserve(_.loBound) /** Argument types where existential types in arguments are approximated by their upper bound */ - def argTypesHi(implicit ctx: Context) = argInfos mapConserve boundsToHi + def argTypesHi(implicit ctx: Context) = argInfos.mapConserve(_.hiBound) /** The core type without any type arguments. * @param `typeArgs` must be the type arguments of this type. @@ -598,7 +582,7 @@ class TypeApplications(val self: Type) extends AnyVal { case _ => typeArgs match { case _ :: typeArgs1 => - val RefinedType(tycon, _, _) = self + val RefinedType(tycon, _, _) = self // @!!! tycon.withoutArgs(typeArgs1) case nil => self diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 9c7692e441c1..2a3f37dd3159 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -522,6 +522,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case _ => isSubType(tp1.widenExpr, restpe2) } compareExpr + case tp2: TypeArgRef => + isSubType(tp1, tp2.underlying.loBound) || fourthTry(tp1, tp2) case tp2 @ TypeBounds(lo2, hi2) => def compareTypeBounds = tp1 match { case tp1 @ TypeBounds(lo1, hi1) => @@ -591,6 +593,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { isNewSubType(tp1.parent, tp2) case tp1: RecType => isNewSubType(tp1.parent, tp2) + case tp1: TypeArgRef => + isSubType(tp1.underlying.hiBound, tp2) case tp1 @ HKApply(tycon1, args1) => compareHkApply1(tp1, tycon1, args1, tp2) case tp1: HKTypeLambda => @@ -845,35 +849,36 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { * * such that the resulting type application is a supertype of `tp1`. */ - def tyconOK(tycon1a: Type, args1: List[Type]) = { - var tycon1b = tycon1a - val tparams1a = tycon1a.typeParams - val lengthDiff = tparams1a.length - tparams.length - lengthDiff >= 0 && { - val tparams1 = tparams1a.drop(lengthDiff) - variancesConform(tparams1, tparams) && { - if (lengthDiff > 0) - tycon1b = HKTypeLambda(tparams1.map(_.paramName))( - tl => tparams1.map(tparam => tl.integrate(tparams, tparam.paramInfo).bounds), - tl => tycon1a.appliedTo(args1.take(lengthDiff) ++ - tparams1.indices.toList.map(TypeParamRef(tl, _)))) - (ctx.mode.is(Mode.TypevarsMissContext) || - tryInstantiate(tycon2, tycon1b.ensureHK)) && - isSubType(tp1, tycon1b.appliedTo(args2)) + def appOK(tp1base: Type) = tp1base match { + case tp1base: AppliedType => + var tycon1 = tp1base.tycon + var args1 = tp1base.args + val tparams1all = tycon1.typeParams + val lengthDiff = tparams1all.length - tparams.length + lengthDiff >= 0 && { + val tparams1 = tparams1all.drop(lengthDiff) + variancesConform(tparams1, tparams) && { + if (lengthDiff > 0) + tycon1 = HKTypeLambda(tparams1.map(_.paramName))( + tl => tparams1.map(tparam => tl.integrate(tparams, tparam.paramInfo).bounds), + tl => tp1base.tycon.appliedTo(args1.take(lengthDiff) ++ + tparams1.indices.toList.map(TypeParamRef(tl, _)))) + (ctx.mode.is(Mode.TypevarsMissContext) || + tryInstantiate(tycon2, tycon1.ensureHK)) && + isSubType(tp1, tycon1.appliedTo(args2)) + } } - } + case _ => false } tp1.widen match { - case tp1w @ HKApply(tycon1, args1) => - tyconOK(tycon1, args1) + case tp1w: AppliedType => appOK(tp1w) case tp1w => tp1w.typeSymbol.isClass && { val classBounds = tycon2.classSymbols def liftToBase(bcs: List[ClassSymbol]): Boolean = bcs match { case bc :: bcs1 => - classBounds.exists(bc.derivesFrom) && - tyconOK(tp1w.baseTypeTycon(bc), tp1w.baseArgInfos(bc)) || + classBounds.exists(bc.derivesFrom) && appOK(tp1w.baseType(bc)) || liftToBase(bcs1) case _ => false @@ -982,12 +987,20 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { val classBounds = tp2.classSymbols def recur(bcs: List[ClassSymbol]): Boolean = bcs match { case bc :: bcs1 => - val baseRef = tp1.baseTypeTycon(bc) - (classBounds.exists(bc.derivesFrom) && - variancesConform(baseRef.typeParams, tparams) && - p(baseRef.appliedTo(tp1.baseArgInfos(bc))) - || - recur(bcs1)) + if (Config.newScheme) + (classBounds.exists(bc.derivesFrom) && + variancesConform(bc.typeParams, tparams) && + p(tp1.baseType(bc)) + || + recur(bcs1)) + else { + val baseRef = tp1.baseTypeTycon(bc) + (classBounds.exists(bc.derivesFrom) && + variancesConform(baseRef.typeParams, tparams) && + p(baseRef.appliedTo(tp1.baseArgInfos(bc))) + || + recur(bcs1)) + } case nil => false } @@ -1554,6 +1567,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { // opportunistically merge same-named refinements // this does not change anything semantically (i.e. merging or not merging // gives =:= types), but it keeps the type smaller. + // @!!! still needed? case tp1: RefinedType => tp2 match { case tp2: RefinedType if tp1.refinedName == tp2.refinedName => diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index 079e296ef495..8969fa8b44e1 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -382,10 +382,11 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean else if (sym eq defn.PhantomClass) defn.ObjectType // To erase the definitions of Phantom.{assume, Any, Nothing} else eraseNormalClassRef(tp) case tp: AppliedType => - apply(tp.superType) + if (tp.tycon.isRef(defn.ArrayClass)) eraseArray(tp) + else apply(tp.superType) case tp: RefinedType => val parent = tp.parent - if (parent isRef defn.ArrayClass) eraseArray(tp) + if (parent isRef defn.ArrayClass) eraseArray(tp) // @!!! else this(parent) case _: TermRef | _: ThisType => this(tp.widen) @@ -414,13 +415,12 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean } case tp: PolyType => this(tp.resultType) - case tp @ ClassInfo(pre, cls, classParents, decls, _) => + case tp @ ClassInfo(pre, cls, parents, decls, _) => if (cls is Package) tp else { - def eraseTypeRef(p: TypeRef) = this(p).asInstanceOf[TypeRef] - val parents: List[TypeRef] = + val erasedParents: List[Type] = if ((cls eq defn.ObjectClass) || cls.isPrimitiveValueClass) Nil - else classParents.mapConserve(eraseTypeRef) match { + else parents.mapConserve(apply) match { case tr :: trs1 => assert(!tr.classSymbol.is(Trait), cls) val tr1 = if (cls is Trait) defn.ObjectType else tr @@ -428,7 +428,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean case nil => nil } val erasedDecls = decls.filteredScope(sym => !sym.isType || sym.isClass) - tp.derivedClassInfo(NoPrefix, parents, erasedDecls, erasedRef(tp.selfType)) + tp.derivedClassInfo(NoPrefix, erasedParents, erasedDecls, erasedRef(tp.selfType)) // can't replace selftype by NoType because this would lose the sourceModule link } case NoType | NoPrefix | _: ErrorType | JavaArrayType(_) => @@ -439,7 +439,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean this(tp.underlying) } - private def eraseArray(tp: RefinedType)(implicit ctx: Context) = { + private def eraseArray(tp: Type)(implicit ctx: Context) = { val defn.ArrayOf(elemtp) = tp def arrayErasure(tpToErase: Type) = erasureFn(isJava, semiEraseVCs = false, isConstructor, wildcardOK)(tpToErase) @@ -490,8 +490,10 @@ 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 AppliedType(tycon, _) if !(tycon isRef defn.ArrayClass) => + eraseResult(tycon) case _ => this(tp) } @@ -530,10 +532,10 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean else normalizeClass(sym.asClass).fullName.asTypeName case tp: AppliedType => - sigName(tp.superType) + sigName(if (tp.tycon.isRef(defn.ArrayClass)) this(tp) else tp.superType) case ErasedValueType(_, underlying) => sigName(underlying) - case defn.ArrayOf(elem) => + case defn.ArrayOf(elem) => // @!!! sigName(this(tp)) case tp: HKApply => sigName(tp.superType) diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index 1d6b74626c05..7af511c491b3 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -57,6 +57,18 @@ trait TypeOps { this: Context => // TODO: Make standalone object. */ private def asSeenFrom(tp: Type, pre: Type, cls: Symbol, theMap: AsSeenFromMap): Type = { + def currentVariance = if (theMap != null) theMap.currentVariance else 1 + + def markIfUnsafe(pre: Type) = + if (currentVariance <= 0 && !isLegalPrefix(pre)) { + ctx.base.unsafeNonvariant = ctx.runId + pre match { + case AnnotatedType(_, ann) if ann.symbol == defn.UnsafeNonvariantAnnot => pre + case _ => AnnotatedType(pre, Annotation(defn.UnsafeNonvariantAnnot, Nil)) + } + } + else pre + /** Map a `C.this` type to the right prefix. If the prefix is unstable and * the `C.this` occurs in nonvariant or contravariant position, mark the map * to be unstable. @@ -67,16 +79,8 @@ trait TypeOps { this: Context => // TODO: Make standalone object. else pre match { case pre: SuperType => toPrefix(pre.thistpe, cls, thiscls) case _ => - if (thiscls.derivesFrom(cls) && pre.baseType(thiscls).exists) { // ??? why not derivesFrom ??? - if (theMap != null && theMap.currentVariance <= 0 && !isLegalPrefix(pre)) { - ctx.base.unsafeNonvariant = ctx.runId - pre match { - case AnnotatedType(_, ann) if ann.symbol == defn.UnsafeNonvariantAnnot => pre - case _ => AnnotatedType(pre, Annotation(defn.UnsafeNonvariantAnnot, Nil)) - } - } - else pre - } + if (thiscls.derivesFrom(cls) && pre.baseType(thiscls).exists) // ??? why not derivesFrom ??? + markIfUnsafe(pre) else if ((pre.termSymbol is Package) && !(thiscls is Package)) toPrefix(pre.select(nme.PACKAGE), cls, thiscls) else @@ -85,16 +89,32 @@ trait TypeOps { this: Context => // TODO: Make standalone object. } @tailrec - def argForParam(pre: Type, cls: Symbol, pref: TypeParamRef, prefCls: ClassSymbol): Type = - if (cls.is(PackageClass)) pref - else { - val base = pre.baseType(cls) - if (cls eq prefCls) { - val AppliedType(_, args) = base - args(pref.paramNum) + def argForParam(pre: Type, cls: Symbol, tparam: Symbol): Type = { + def fail() = throw new TypeError(ex"$pre contains no matching argument for ${tparam.showLocated} ") + if ((pre eq NoType) || (pre eq NoPrefix) || (cls is PackageClass)) fail() + val base = pre.baseType(cls) + if (cls `eq` tparam.owner) { + var tparams = cls.typeParams + var args = base.argInfos + var idx = 0 + while (tparams.nonEmpty && args.nonEmpty) { + if (tparams.head.eq(tparam)) + return args.head match { + case bounds: TypeBounds => + val v = currentVariance + if (v > 0) bounds.hi + else if (v < 0) bounds.lo + else TypeArgRef(pre, cls.typeRef, idx) + case arg => arg + } + tparams = tparams.tail + args = args.tail + idx += 1 } - else argForParam(base.normalizedPrefix, cls.owner.enclosingClass, pref, prefCls) + fail() } + else argForParam(base.normalizedPrefix, cls.owner.enclosingClass, tparam) + } /*>|>*/ ctx.conditionalTraceIndented(TypeOps.track, s"asSeen ${tp.show} from (${pre.show}, ${cls.show})", show = true) /*<|<*/ { // !!! DEBUG tp match { @@ -103,7 +123,8 @@ trait TypeOps { this: Context => // TODO: Make standalone object. if (sym.isStatic) tp else { val pre1 = asSeenFrom(tp.prefix, pre, cls, theMap) - if (pre1.isUnsafeNonvariant) { + if (Config.newScheme && sym.is(TypeParam)) argForParam(pre1, cls, sym) + else if (pre1.isUnsafeNonvariant) { val safeCtx = ctx.withProperty(TypeOps.findMemberLimit, Some(())) pre1.member(tp.name)(safeCtx).info match { case TypeAlias(alias) => @@ -116,14 +137,9 @@ trait TypeOps { this: Context => // TODO: Make standalone object. } case tp: ThisType => toPrefix(pre, cls, tp.cls) - case tp: TypeParamRef => - tp.binder.resultType match { - case cinfo: ClassInfo => argForParam(pre, cls, tp, cinfo.cls) - case _ => tp - } case _: BoundType | NoPrefix => tp - case tp: RefinedType => + case tp: RefinedType => //@!!! tp.derivedRefinedType( asSeenFrom(tp.parent, pre, cls, theMap), tp.refinedName, @@ -187,7 +203,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object. } case _: ThisType | _: BoundType | NoPrefix => tp - case tp: RefinedType => + case tp: RefinedType => // @!!! tp.derivedRefinedType(simplify(tp.parent, theMap), tp.refinedName, simplify(tp.refinedInfo, theMap)) case tp: TypeAlias => tp.derivedTypeAlias(simplify(tp.alias, theMap)) @@ -265,6 +281,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object. def approximateOr(tp1: Type, tp2: Type): Type = { def isClassRef(tp: Type): Boolean = tp match { case tp: TypeRef => tp.symbol.isClass + case tp: AppliedType => isClassRef(tp.tycon) case tp: RefinedType => isClassRef(tp.parent) case _ => false } @@ -339,11 +356,11 @@ trait TypeOps { this: Context => // TODO: Make standalone object. cls.enter(sym, decls) } - /** Normalize a list of parent types of class `cls` that may contain refinements - * to a list of typerefs referring to classes, by converting all refinements to member - * definitions in scope `decls`. Can add members to `decls` as a side-effect. + /** Normalize a list of parent types of class `cls` to make sure they are + * all (possibly applied) references to classes. */ - def normalizeToClassRefs(parents: List[Type], cls: ClassSymbol, decls: Scope): List[TypeRef] = { + def normalizeToClassRefs(parents: List[Type], cls: ClassSymbol, decls: Scope): List[Type] = { + if (Config.newScheme) return parents.mapConserve(_.dealias) // println(s"normalizing $parents of $cls in ${cls.owner}") // !!! DEBUG // A map consolidating all refinements arising from parent type parameters @@ -443,15 +460,6 @@ trait TypeOps { this: Context => // TODO: Make standalone object. paramBindings.foreachBinding(forwardRefs) } - /** Used only for debugging: All BaseTypeArg definitions in - * `cls` and all its base classes. - */ - def allBaseTypeArgs(cls: ClassSymbol)(implicit ctx: Context) = - for { bc <- cls.baseClasses - sym <- bc.info.decls.toList - if sym.is(BaseTypeArg) - } yield sym - /** An argument bounds violation is a triple consisting of * - the argument tree * - a string "upper" or "lower" indicating which bound is violated diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index c3e239103561..f6174233022b 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1188,8 +1188,6 @@ object Types { /** The full parent types, including (in new scheme) all type arguments */ def parentsNEW(implicit ctx: Context): List[Type] = this match { - case AppliedType(tycon: HKTypeLambda, args) => // TODO: can be eliminated once ClassInfo is changed, also: cache? - tycon.resType.parentsWithArgs.map(_.substParams(tycon, args)) case tp: TypeProxy => tp.superType.parentsNEW case _ => Nil } @@ -2229,7 +2227,8 @@ object Types { abstract case class RefinedType(parent: Type, refinedName: Name, refinedInfo: Type) extends RefinedOrRecType { if (refinedName.isTermName) assert(refinedInfo.isInstanceOf[TermType]) - else assert(refinedInfo.isInstanceOf[TypeType]) + else assert(refinedInfo.isInstanceOf[TypeType], this) + if (Config.newScheme) assert(!refinedName.is(NameKinds.ExpandedName), this) override def underlying(implicit ctx: Context) = parent @@ -2545,11 +2544,12 @@ object Types { type ThisName <: Name type PInfo <: Type type This <: LambdaType{type PInfo = self.PInfo} + type ParamRefType <: ParamRef def paramNames: List[ThisName] def paramInfos: List[PInfo] def resType: Type - def newParamRef(n: Int): ParamRef + def newParamRef(n: Int): ParamRefType override def resultType(implicit ctx: Context) = resType @@ -2563,7 +2563,7 @@ object Types { final def isTypeLambda = isInstanceOf[TypeLambda] final def isHigherKinded = isInstanceOf[TypeProxy] - lazy val paramRefs: List[ParamRef] = paramNames.indices.toList.map(newParamRef) + lazy val paramRefs: List[ParamRefType] = paramNames.indices.toList.map(newParamRef) protected def computeSignature(implicit ctx: Context) = resultSignature @@ -2634,6 +2634,7 @@ object Types { type ThisName = TermName type PInfo = Type type This <: TermLambda + type ParamRefType = TermParamRef override def resultType(implicit ctx: Context): Type = if (dependencyStatus == FalseDeps) { // dealias all false dependencies @@ -2873,6 +2874,7 @@ object Types { type ThisName = TypeName type PInfo = TypeBounds type This <: TypeLambda + type ParamRefType = TypeParamRef def isDependent(implicit ctx: Context): Boolean = true def isParamDependent(implicit ctx: Context): Boolean = true @@ -3024,7 +3026,7 @@ object Types { final val Provisional: DependencyStatus = 4 // set if dependency status can still change due to type variable instantiations } - // ----- HK types: LambdaParam, HKApply --------------------- + // ----- HK types: LambdaParam, HKApply, TypeArgRef --------------------- /** The parameter of a type lambda */ case class LambdaParam(tl: TypeLambda, n: Int) extends ParamInfo { @@ -3193,6 +3195,24 @@ object Types { unique(new CachedHKApply(tycon, args)).checkInst } + /** A reference to wildcard argument `p.` + * where `p: C[... _ ...]` + */ + abstract case class TypeArgRef(prefix: Type, clsRef: TypeRef, idx: Int) extends CachedProxyType { + override def underlying(implicit ctx: Context): Type = + prefix.baseType(clsRef.symbol).argInfos.apply(idx) + def derivedTypeArgRef(prefix: Type)(implicit ctx: Context): Type = + if (prefix eq this.prefix) this else TypeArgRef(prefix, clsRef, idx) + override def computeHash = doHash(idx, prefix, clsRef) + } + + final class CachedTypeArgRef(prefix: Type, clsRef: TypeRef, idx: Int) extends TypeArgRef(prefix, clsRef, idx) + + object TypeArgRef { + def apply(prefix: Type, clsRef: TypeRef, idx: Int)(implicit ctx: Context) = + unique(new CachedTypeArgRef(prefix, clsRef, idx)) + } + // ----- BoundTypes: ParamRef, RecThis ---------------------------------------- abstract class BoundType extends CachedProxyType with ValueType { @@ -3404,7 +3424,7 @@ object Types { abstract case class ClassInfo( prefix: Type, cls: ClassSymbol, - classParents: List[TypeRef], + classParentsNEW: List[Type], decls: Scope, selfInfo: DotClass /* should be: Type | Symbol */) extends CachedGroundType with TypeType { @@ -3429,14 +3449,16 @@ object Types { private var selfTypeCache: Type = null - private def fullyAppliedRef(base: Type, tparams: List[TypeSymbol])(implicit ctx: Context): Type = tparams match { - case tparam :: tparams1 => - fullyAppliedRef( - RefinedType(base, tparam.name, TypeRef(cls.thisType, tparam).toBounds(tparam)), - tparams1) - case nil => - base - } + private def fullyAppliedRef(base: Type, tparams: List[TypeSymbol])(implicit ctx: Context): Type = + if (Config.newScheme) base.appliedTo(tparams.map(_.typeRef)) + else tparams match { + case tparam :: tparams1 => + fullyAppliedRef( + RefinedType(base, tparam.name, TypeRef(cls.thisType, tparam).toBounds(tparam)), + tparams1) + case nil => + base + } /** The class type with all type parameters */ def fullyAppliedRef(implicit ctx: Context): Type = fullyAppliedRef(cls.typeRef, cls.typeParams) @@ -3455,18 +3477,17 @@ object Types { def symbolicTypeRef(implicit ctx: Context): TypeRef = TypeRef(prefix, cls) // cached because baseType needs parents - private var parentsCache: List[TypeRef] = null + private var parentsCache: List[Type] = null /** The parent type refs as seen from the given prefix */ - override def parentRefs(implicit ctx: Context): List[TypeRef] = { - if (parentsCache == null) - parentsCache = cls.classParents.mapConserve(_.asSeenFrom(prefix, cls.owner).asInstanceOf[TypeRef]) - parentsCache - } + override def parentRefs(implicit ctx: Context): List[TypeRef] = + if (Config.newScheme) parentsNEW.map(_.typeConstructor.asInstanceOf[TypeRef]) + else parentsNEW.mapconserve(_.asInstanceOf[TypeRef]) /** The parent types with all type arguments */ override def parentsWithArgs(implicit ctx: Context): List[Type] = - parentRefs mapConserve { pref => + if (Config.newScheme) parentsNEW + else parentRefs mapConserve { pref => ((pref: Type) /: pref.classSymbol.typeParams) { (parent, tparam) => val targSym = decls.lookup(tparam.name) if (targSym.exists) RefinedType(parent, targSym.name, targSym.info) @@ -3474,23 +3495,26 @@ object Types { } } - override def parentsNEW(implicit ctx: Context): List[Type] = - parentRefs // !!! TODO: change + override def parentsNEW(implicit ctx: Context): List[Type] = { + if (parentsCache == null) + parentsCache = classParentsNEW.mapConserve(_.asSeenFrom(prefix, cls.owner)) + parentsCache + } def derivedClassInfo(prefix: Type)(implicit ctx: Context) = if (prefix eq this.prefix) this - else ClassInfo(prefix, cls, classParents, decls, selfInfo) + else ClassInfo(prefix, cls, classParentsNEW, decls, selfInfo) - def derivedClassInfo(prefix: Type = this.prefix, classParents: List[TypeRef] = classParents, decls: Scope = this.decls, selfInfo: DotClass = this.selfInfo)(implicit ctx: Context) = - if ((prefix eq this.prefix) && (classParents eq this.classParents) && (decls eq this.decls) && (selfInfo eq this.selfInfo)) this - else ClassInfo(prefix, cls, classParents, decls, selfInfo) + def derivedClassInfo(prefix: Type = this.prefix, classParentsNEW: List[Type] = this.classParentsNEW, decls: Scope = this.decls, selfInfo: DotClass = this.selfInfo)(implicit ctx: Context) = + if ((prefix eq this.prefix) && (classParentsNEW eq this.classParentsNEW) && (decls eq this.decls) && (selfInfo eq this.selfInfo)) this + else ClassInfo(prefix, cls, classParentsNEW, decls, selfInfo) override def computeHash = doHash(cls, prefix) override def toString = s"ClassInfo($prefix, $cls)" } - class CachedClassInfo(prefix: Type, cls: ClassSymbol, classParents: List[TypeRef], decls: Scope, selfInfo: DotClass) + class CachedClassInfo(prefix: Type, cls: ClassSymbol, classParents: List[Type], decls: Scope, selfInfo: DotClass) extends ClassInfo(prefix, cls, classParents, decls, selfInfo) /** A class for temporary class infos where `parents` are not yet known. */ @@ -3506,14 +3530,14 @@ object Types { def addSuspension(suspension: Context => Unit): Unit = suspensions ::= suspension /** Install classinfo with known parents in `denot` and resume all suspensions */ - def finalize(denot: SymDenotation, parents: List[TypeRef])(implicit ctx: Context) = { - denot.info = derivedClassInfo(classParents = parents) + def finalize(denot: SymDenotation, parents: List[Type])(implicit ctx: Context) = { + denot.info = derivedClassInfo(classParentsNEW = parents) suspensions.foreach(_(ctx)) } } object ClassInfo { - def apply(prefix: Type, cls: ClassSymbol, classParents: List[TypeRef], decls: Scope, selfInfo: DotClass = NoType)(implicit ctx: Context) = + def apply(prefix: Type, cls: ClassSymbol, classParents: List[Type], decls: Scope, selfInfo: DotClass = NoType)(implicit ctx: Context) = unique(new CachedClassInfo(prefix, cls, classParents, decls, selfInfo)) } @@ -3816,6 +3840,8 @@ object Types { tp.derivedAppliedType(tycon, args) protected def derivedAppliedType(tp: HKApply, tycon: Type, args: List[Type]): Type = tp.derivedAppliedType(tycon, args) + protected def derivedTypeArgRef(tp: TypeArgRef, prefix: Type): Type = + tp.derivedTypeArgRef(prefix) protected def derivedAndOrType(tp: AndOrType, tp1: Type, tp2: Type): Type = tp.derivedAndOrType(tp1, tp2) protected def derivedAnnotatedType(tp: AnnotatedType, underlying: Type, annot: Annotation): Type = @@ -3910,6 +3936,13 @@ object Types { } mapOverLambda + case tp @ TypeArgRef(prefix, _, _) => + val saved = variance + variance = 0 + val prefix1 = this(prefix) + variance = saved + derivedTypeArgRef(tp, prefix1) + case tp @ SuperType(thistp, supertp) => derivedSuperType(tp, this(thistp), this(supertp)) @@ -4035,6 +4068,12 @@ object Types { override protected def derivedAppliedType(tp: HKApply, tycon: Type, args: List[Type]): Type = if (tycon.exists && args.forall(_.exists)) tp.derivedAppliedType(tycon, args) else approx() // This is rather coarse, but to do better is a bit complicated + override protected def derivedTypeArgRef(tp: TypeArgRef, prefix: Type): Type = + if (prefix.exists) tp.derivedTypeArgRef(prefix) + else { + val paramBounds = tp.underlying + approx(paramBounds.loBound, paramBounds.hiBound) + } override protected def derivedAndOrType(tp: AndOrType, tp1: Type, tp2: Type) = if (tp1.exists && tp2.exists) tp.derivedAndOrType(tp1, tp2) else if (tp.isAnd) approx(hi = tp1 & tp2) // if one of tp1d, tp2d exists, it is the result of tp1d & tp2d @@ -4164,6 +4203,12 @@ object Types { case tp: SkolemType => this(x, tp.info) + case tp @ TypeArgRef(prefix, _, _) => + val saved = variance + variance = 0 + try this(x, prefix) + finally variance = saved + case AnnotatedType(underlying, annot) => this(applyToAnnot(x, annot), underlying) diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index 585085f1d9e5..31519af93825 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -308,17 +308,12 @@ class ClassfileParser( case tp: TypeRef => if (sig(index) == '<') { accept('<') - var tp1: Type = tp - var formals: List[Symbol] = - if (skiptvs) - null - else - tp.typeParamSymbols + val argsBuf = if (skiptvs) null else new ListBuffer[Type] while (sig(index) != '>') { - sig(index) match { + val arg = sig(index) match { case variance @ ('+' | '-' | '*') => index += 1 - val bounds = variance match { + variance match { case '+' => objToAny(TypeBounds.upper(sig2type(tparams, skiptvs))) case '-' => val tp = sig2type(tparams, skiptvs) @@ -328,18 +323,24 @@ class ClassfileParser( else TypeBounds.lower(tp) case '*' => TypeBounds.empty } - if (formals != null) - tp1 = RefinedType(tp1, formals.head.name, bounds) // @!!! - case _ => - val info = sig2type(tparams, skiptvs) - if (formals != null) - tp1 = RefinedType(tp1, formals.head.name, TypeAlias(info)) // @!!! + case _ => sig2type(tparams, skiptvs) } - if (formals != null) - formals = formals.tail + if (argsBuf != null) argsBuf += arg } accept('>') - tp1 + if (skiptvs) tp + else if (config.Config.newScheme) tp.appliedTo(argsBuf.toList) + else { + var tp1: Type = tp + (argsBuf.toList, tp.typeParamSymbols).zipped.foreach { (arg, formal) => + val info = arg match { + case arg: TypeBounds => arg + case _ => TypeAlias(arg) + } + tp1 = RefinedType(tp1, formal.name, info) + } + tp1 + } } else tp case tp => assert(sig(index) != '<', tp) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index 3b04d6d67eb7..b87f332c4319 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -164,11 +164,12 @@ Standard-Section: "ASTs" TopLevelStat* BIND Length boundName_NameRef bounds_Type // for type-variables defined in a type pattern BYNAMEtype underlying_Type + PARAMtype Length binder_ASTref paramNum_Nat + TYPEARGtype Length prefix_Type clsRef_Type idx_Nat LAZYref underlying_Type POLYtype Length result_Type NamesTypes METHODtype Length result_Type NamesTypes // needed for refinements TYPELAMBDAtype Length result_Type NamesTypes // variance encoded in front of name: +/-/(nothing) - PARAMtype Length binder_ASTref paramNum_Nat // needed for refinements SHARED type_ASTRef NamesTypes = NameType* NameType = paramName_NameRef typeOrBounds_ASTRef @@ -382,6 +383,7 @@ object TastyFormat { final val LAMBDAtpt = 173 final val PARAMtype = 174 final val ANNOTATION = 175 + final val TYPEARGtype = 176 final val firstSimpleTreeTag = UNITconst final val firstNatTreeTag = SHARED @@ -565,6 +567,7 @@ object TastyFormat { case ENUMconst => "ENUMconst" case SINGLETONtpt => "SINGLETONtpt" case SUPERtype => "SUPERtype" + case TYPEARGtype => "TYPEARGtype" case REFINEDtype => "REFINEDtype" case REFINEDtpt => "REFINEDtpt" case APPLIEDtype => "APPLIEDtype" diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 052be7a366ab..f1c9542ddd5f 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -197,7 +197,10 @@ class TreePickler(pickler: TastyPickler) { } case tpe: SuperType => writeByte(SUPERtype) - withLength { pickleType(tpe.thistpe); pickleType(tpe.supertpe)} + withLength { pickleType(tpe.thistpe); pickleType(tpe.supertpe) } + case tpe: TypeArgRef => + writeByte(TYPEARGtype) + withLength { pickleType(tpe.prefix); pickleType(tpe.clsRef); writeNat(tpe.idx) } case tpe: RecThis => writeByte(RECthis) val binderAddr = pickledTypes.get(tpe.binder) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 3015bef6d8e7..569f1e384bec 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -219,8 +219,6 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi val result = (tag: @switch) match { - case SUPERtype => - SuperType(readType(), readType()) case REFINEDtype => var name: Name = readName() val parent = readType() @@ -246,6 +244,10 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi AndType(readType(), readType()) case ORtype => OrType(readType(), readType()) + case SUPERtype => + SuperType(readType(), readType()) + case TYPEARGtype => + TypeArgRef(readType(), readType().asInstanceOf[TypeRef], readNat()) case BIND => val sym = ctx.newSymbol(ctx.owner, readName().toTypeName, BindDefinedType, readType()) registerSym(start, sym) @@ -715,7 +717,7 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi private def readTemplate(implicit ctx: Context): Template = { val start = currentAddr val cls = ctx.owner.asClass - def setClsInfo(parents: List[TypeRef], selfType: Type) = + def setClsInfo(parents: List[Type], selfType: Type) = cls.info = ClassInfo(cls.owner.thisType, cls, parents, cls.unforcedDecls, selfType) val assumedSelfType = if (cls.is(Module) && cls.owner.isClass) diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 908c86b63a8d..0f334c05b7e5 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -125,7 +125,7 @@ class PlainPrinter(_ctx: Context) extends Printer { */ private def refinementChain(tp: Type): List[Type] = tp :: (tp match { - case AnyAppliedType(_, _) => Nil + case AnyAppliedType(_, _) => Nil // @!!! case tp: RefinedType => refinementChain(tp.parent.stripTypeVar) case _ => Nil }) @@ -156,6 +156,11 @@ class PlainPrinter(_ctx: Context) extends Printer { "{" ~ selfRecName(openRecs.length) ~ " => " ~ toTextGlobal(tp.parent) ~ "}" } finally openRecs = openRecs.tail + case TypeArgRef(prefix, clsRef, idx) => + val cls = clsRef.symbol + val tparams = cls.typeParams + val paramName = if (tparams.length > idx) nameString(tparams(idx)) else "" + toTextPrefix(prefix) ~ "" case AndType(tp1, tp2) => changePrec(AndPrec) { toText(tp1) ~ " & " ~ toText(tp2) } case OrType(tp1, tp2) => diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 482d8c651cce..c5e01c3c96fb 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -64,7 +64,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { override protected def simpleNameString(sym: Symbol): String = { val name = if (ctx.property(XprintMode).isEmpty) sym.originalName else sym.name - nameString(if (sym.is(TypeParam)) name.asTypeName.unexpandedName else name) + nameString(if (sym.is(TypeParam)) name.asTypeName.unexpandedName else name) // @!!! } override def fullNameString(sym: Symbol): String = diff --git a/compiler/src/dotty/tools/dotc/transform/CheckReentrant.scala b/compiler/src/dotty/tools/dotc/transform/CheckReentrant.scala index c9eefb22f75e..94c186a2ac19 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckReentrant.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckReentrant.scala @@ -81,8 +81,8 @@ class CheckReentrant extends MiniPhaseTransform { thisTransformer => sym.info.widenExpr.classSymbols.foreach(addVars) } } - for (parent <- cls.classInfo.classParents) - addVars(parent.symbol.asClass) + for (parent <- cls.classInfo.classParentsNEW) + addVars(parent.typeSymbol.asClass) } } } diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 9db297324014..c3a864bdfafa 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -236,7 +236,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran // Add Child annotation to sealed parents unless current class is anonymous if (!sym.isAnonymousClass) // ignore anonymous class - sym.asClass.classInfo.classParents.foreach { parent => + sym.asClass.classParentsNEW.foreach { parent => val sym2 = if (sym.is(Module)) sym.sourceModule else sym registerChild(sym2, parent) } diff --git a/compiler/src/dotty/tools/dotc/transform/localopt/InlineCaseIntrinsics.scala b/compiler/src/dotty/tools/dotc/transform/localopt/InlineCaseIntrinsics.scala index 8e47f84c17dc..e4f291679afa 100644 --- a/compiler/src/dotty/tools/dotc/transform/localopt/InlineCaseIntrinsics.scala +++ b/compiler/src/dotty/tools/dotc/transform/localopt/InlineCaseIntrinsics.scala @@ -7,6 +7,7 @@ import core.StdNames._ import core.Symbols._ import core.Types._ import core.Flags._ +import core.TypeApplications.noBounds import ast.Trees._ import transform.SymUtils._ import Simplify.desugarIdent @@ -82,7 +83,7 @@ class InlineCaseIntrinsics(val simplifyPhase: Simplify) extends Optimisation { def some(e: Tree) = { val accessors = e.tpe.widenDealias.classSymbol.caseAccessors.filter(_.is(Method)) val fields = accessors.map(x => e.select(x).ensureApplied) - val tplType = a.tpe.baseArgTypes(defn.OptionClass).head + val tplType = noBounds(a.tpe.baseType(defn.OptionClass).argInfos.head) val someTpe = a.tpe.translateParameterized(defn.OptionClass, defn.SomeClass) if (fields.tail.nonEmpty) diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 9468f43daa79..563c1374c833 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -94,7 +94,7 @@ object Applications { if (unapplyName == nme.unapplySeq) { if (unapplyResult derivesFrom defn.SeqClass) seqSelector :: Nil else if (isGetMatch(unapplyResult, pos)) { - val seqArg = boundsToHi(getTp.elemType) + val seqArg = getTp.elemType.hiBound if (seqArg.exists) args.map(Function.const(seqArg)) else fail } diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 57d753b23556..36331443b620 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -454,7 +454,7 @@ object Checking { case tp: ClassInfo => tp.derivedClassInfo( prefix = apply(tp.prefix), - classParents = + classParentsNEW = tp.parentsWithArgs.map { p => apply(p).underlyingClassRef(refinementOK = false) match { case ref: TypeRef => ref @@ -575,17 +575,26 @@ trait Checking { * 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(ex"no type exists between low bound $lo and high bound $hi$where", pos) - TypeAlias(hi) - case _ => - tp - } + def checkFeasible(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = + tp match { + case tp @ AppliedType(tycon, args) => + def checkArg(arg: Type) = arg match { + case tp @ TypeBounds(lo, hi) if !(lo <:< hi) => + ctx.error(ex"no type exists between low bound $lo and high bound $hi$where", pos) + hi + case _ => arg + } + tp.derivedAppliedType(tycon, args.mapConserve(checkArg)) + 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(ex"no type exists between low bound $lo and high bound $hi$where", pos) + TypeAlias(hi) + case _ => + tp + } /** Check that `tree` is a pure expression of constant type */ def checkInlineConformant(tree: Tree, what: => String)(implicit ctx: Context): Unit = diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 46452a065a19..575bbc8d970e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -438,14 +438,14 @@ trait ImplicitRunInfo { self: RunInfo => else if (compSym.exists) comps += companion.asSeenFrom(pre, compSym.owner).asInstanceOf[TermRef] } - def addParentScope(parent: TypeRef): Unit = { - iscopeRefs(parent) foreach addRef + def addParentScope(parent: Type): Unit = { + iscopeRefs(parent.typeConstructor) foreach addRef for (param <- parent.typeParamSymbols) comps ++= iscopeRefs(tp.member(param.name).info) } val companion = cls.companionModule if (companion.exists) addRef(companion.valRef) - cls.classParents foreach addParentScope + cls.classParentsNEW foreach addParentScope } tp.classSymbols(liftingCtx) foreach addClassScope case _ => @@ -942,10 +942,12 @@ trait Implicits { self: Typer => */ class SearchHistory(val searchDepth: Int, val seen: Map[ClassSymbol, Int]) { - /** The number of RefinementTypes in this type, after all aliases are expanded */ + /** The number of applications and refinements in this type, after all aliases are expanded */ private def typeSize(tp: Type)(implicit ctx: Context): Int = { val accu = new TypeAccumulator[Int] { def apply(n: Int, tp: Type): Int = tp match { + case tp: AppliedType => + foldOver(n + 1, tp) case tp: RefinedType => foldOver(n + 1, tp) case tp: TypeRef if tp.info.isAlias => diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 741e174d3ec2..8c0aab5a51ad 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -100,7 +100,7 @@ object RefChecks { ctx.error(DoesNotConformToSelfType(category, cinfo.selfType, cls, otherSelf, relation, other.classSymbol), cls.pos) } - for (parent <- cinfo.classParents) + for (parent <- cinfo.classParentsNEW) checkSelfConforms(parent.typeSymbol.asClass, "illegal inheritance", "parent") for (reqd <- cinfo.cls.givenSelfType.classSymbols) checkSelfConforms(reqd, "missing requirement", "required") @@ -251,7 +251,7 @@ object RefChecks { def compatibleTypes(memberTp: Type, otherTp: Type): Boolean = try if (member.isType) // intersection of bounds to refined types must be nonempty - member.is(BaseTypeArg) || + member.is(BaseTypeArg) || // @!!! memberTp.bounds.hi.hasSameKindAs(otherTp.bounds.hi) && ((memberTp frozen_<:< otherTp) || !member.owner.derivesFrom(other.owner) && { diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 1ad3edfc9f66..ec958a491518 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -110,7 +110,7 @@ trait TypeAssigner { val base = apply(tycon) var args = tp.baseArgInfos(base.typeSymbol) if (base.typeParams.length != args.length) - args = base.typeParams.map(_.paramInfo) + args = base.typeParams.map(_.paramInfo) // TODO: not sure we need this apply(base.appliedTo(args)) case tp @ RefinedType(parent, name, rinfo) if variance > 0 => val parent1 = apply(tp.parent) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index a389cf77e98c..11cad9b716b6 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1436,7 +1436,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } // Check that phantom lattices are defined in a static object - if (cls.classParents.exists(_.classSymbol eq defn.PhantomClass) && !cls.isStaticOwner) + if (cls.classParentsNEW.exists(_.typeSymbol eq defn.PhantomClass) && !cls.isStaticOwner) ctx.error("only static objects can extend scala.Phantom", cdef.pos) // check value class constraints @@ -1470,8 +1470,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def realClassParent(cls: Symbol): ClassSymbol = if (!cls.isClass) defn.ObjectClass else if (!(cls is Trait)) cls.asClass - else cls.asClass.classParents match { - case parentRef :: _ => realClassParent(parentRef.symbol) + else cls.asClass.classParentsNEW match { + case parentRef :: _ => realClassParent(parentRef.typeSymbol) case nil => defn.ObjectClass } def improve(candidate: ClassSymbol, parent: Type): ClassSymbol = { @@ -2042,13 +2042,14 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit // Follow proxies and approximate type paramrefs by their upper bound // in the current constraint in order to figure out robustly // whether an expected type is some sort of function type. - def underlyingRefined(tp: Type): Type = tp.stripTypeVar match { + def underlyingApplied(tp: Type): Type = tp.stripTypeVar match { case tp: RefinedType => tp - case tp: TypeParamRef => underlyingRefined(ctx.typeComparer.bounds(tp).hi) - case tp: TypeProxy => underlyingRefined(tp.superType) + case tp: AppliedType => tp + case tp: TypeParamRef => underlyingApplied(ctx.typeComparer.bounds(tp).hi) + case tp: TypeProxy => underlyingApplied(tp.superType) case _ => tp } - val ptNorm = underlyingRefined(pt) + val ptNorm = underlyingApplied(pt) val arity = if (defn.isFunctionType(ptNorm)) if (!isFullyDefined(pt, ForceDegree.none) && isFullyDefined(wtp, ForceDegree.none)) diff --git a/doc-tool/src/dotty/tools/dottydoc/model/factories.scala b/doc-tool/src/dotty/tools/dottydoc/model/factories.scala index 705a06580a89..2e14b83bceb1 100644 --- a/doc-tool/src/dotty/tools/dottydoc/model/factories.scala +++ b/doc-tool/src/dotty/tools/dottydoc/model/factories.scala @@ -198,7 +198,7 @@ object factories { case _ => false } - cd.classParents.collect { + cd.classParentRefs.collect { case t: TypeRef if !isJavaLangObject(t) && !isProductWithArity(t) => UnsetLink(t.name.toString, path(t.symbol).mkString(".")) } From 1ce6c70d8b239e30ed49a19132be42719233a4f1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 28 Jul 2017 14:11:40 +0200 Subject: [PATCH 13/18] Various fixes --- .../src/dotty/tools/dotc/core/Contexts.scala | 5 +++-- .../tools/dotc/core/SymDenotations.scala | 6 +++--- .../tools/dotc/core/TypeApplications.scala | 2 +- .../src/dotty/tools/dotc/core/TypeOps.scala | 21 +++++++++++-------- .../src/dotty/tools/dotc/core/Types.scala | 7 ++++++- .../dotc/core/classfile/ClassfileParser.scala | 2 +- .../core/unpickleScala2/Scala2Unpickler.scala | 2 +- .../src/dotty/tools/dotc/typer/Checking.scala | 12 +++++++++-- .../src/dotty/tools/dotc/typer/Namer.scala | 2 +- 9 files changed, 38 insertions(+), 21 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index 4386aa7e9729..d061ca4443d7 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -14,7 +14,6 @@ import NameOps._ import Uniques._ import SymDenotations._ import Comments._ -import Flags.ParamAccessor import util.Positions._ import ast.Trees._ import ast.untpd @@ -335,7 +334,9 @@ object Contexts { * from constructor parameters to class parameter accessors. */ def superCallContext: Context = { - val locals = newScopeWith(owner.asClass.paramAccessors: _*) + val locals = newScopeWith( + (if (Config.newScheme) owner.typeParams ++ owner.asClass.paramAccessors + else owner.asClass.paramAccessors): _*) superOrThisCallContext(owner.primaryConstructor, locals) } diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 1b2b4c93e8b9..7b00047c13f6 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1728,11 +1728,11 @@ object SymDenotations { else constrNamed(nme.CONSTRUCTOR).orElse(constrNamed(nme.TRAIT_CONSTRUCTOR)) } - /** The parameter accessors of this class. Term and type accessors, - * getters and setters are all returned int his list + /** The term parameter accessors of this class. + * Both getters and setters are returned in this list. */ def paramAccessors(implicit ctx: Context): List[Symbol] = - unforcedDecls.filter(_ is ParamAccessor).toList + unforcedDecls.filter(_.is(ParamAccessor)).toList /** If this class has the same `decls` scope reference in `phase` and * `phase.next`, install a new denotation with a cloned scope in `phase.next`. diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index b1807f9e2a4c..79da18fe2326 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -515,7 +515,7 @@ class TypeApplications(val self: Type) extends AnyVal { case TypeBounds(_, hi) => hi.baseTypeWithArgs(base) case _ => default } - case tp @ RefinedType(parent, name, _) if !isExpandedTypeParam(tp.member(name).symbol) => + case tp @ RefinedType(parent, name, _) if !Config.newScheme && !isExpandedTypeParam(tp.member(name).symbol) => tp.wrapIfMember(parent.baseTypeWithArgs(base)) case tp: TermRef => tp.underlying.baseTypeWithArgs(base) diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index 7af511c491b3..b63f68c1fb28 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -123,17 +123,20 @@ trait TypeOps { this: Context => // TODO: Make standalone object. if (sym.isStatic) tp else { val pre1 = asSeenFrom(tp.prefix, pre, cls, theMap) - if (Config.newScheme && sym.is(TypeParam)) argForParam(pre1, cls, sym) - else if (pre1.isUnsafeNonvariant) { - val safeCtx = ctx.withProperty(TypeOps.findMemberLimit, Some(())) - pre1.member(tp.name)(safeCtx).info match { - case TypeAlias(alias) => - // try to follow aliases of this will avoid skolemization. - return alias - case _ => + if (Config.newScheme && sym.is(TypeParam)) + argForParam(pre1, cls, sym) + else { + if (pre1.isUnsafeNonvariant) { + val safeCtx = ctx.withProperty(TypeOps.findMemberLimit, Some(())) + pre1.member(tp.name)(safeCtx).info match { + case TypeAlias(alias) => + // try to follow aliases of this will avoid skolemization. + return alias + case _ => + } } + tp.derivedSelect(pre1) } - tp.derivedSelect(pre1) } case tp: ThisType => toPrefix(pre, cls, tp.cls) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index f6174233022b..5dd944428e87 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -591,6 +591,8 @@ object Types { case tl: HKTypeLambda => go(tl.resType).mapInfo(info => tl.derivedLambdaAbstraction(tl.paramNames, tl.paramInfos, info).appliedTo(tp.args)) + case tc: TypeRef if tc.symbol.isClass => + go(tc) case _ => go(tp.superType) } @@ -1188,7 +1190,10 @@ object Types { /** The full parent types, including (in new scheme) all type arguments */ def parentsNEW(implicit ctx: Context): List[Type] = this match { - case tp: TypeProxy => tp.superType.parentsNEW + case tp @ AppliedType(tycon, args) if tycon.typeSymbol.isClass => + tycon.parentsNEW.map(_.subst(tycon.typeSymbol.typeParams, args)) + case tp: TypeProxy => + tp.superType.parentsNEW case _ => Nil } diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index 31519af93825..91e6b2776c95 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -424,7 +424,7 @@ class ClassfileParser( val start = index while (sig(index) != '>') { val tpname = subName(':'.==).toTypeName - val expname = if (owner.isClass) tpname.expandedName(owner) else tpname + val expname = if (owner.isClass && !config.Config.newScheme) tpname.expandedName(owner) else tpname val s = ctx.newSymbol( owner, expname, owner.typeParamCreationFlags, typeParamCompleter(index), coord = indexCoord(index)) diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index f9ce4f07b9ab..70a6a56ce5ec 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -486,7 +486,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas var name1 = name.asTypeName var flags1 = flags if (flags is TypeParam) { - name1 = name1.expandedName(owner) + if (!dotty.tools.dotc.config.Config.newScheme) name1 = name1.expandedName(owner) flags1 |= owner.typeParamCreationFlags } ctx.newSymbol(owner, name1, flags1, localMemberUnpickler, coord = start) diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 36331443b620..53163e997994 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -425,7 +425,7 @@ object Checking { * @pre The signature of `sym` refers to `other` */ def isLeaked(other: Symbol) = - other.is(Private) && { + other.is(Private, butNot = TypeParam) && { val otherBoundary = other.owner val otherLinkedBoundary = otherBoundary.linkedClass !(symBoundary.isContainedIn(otherBoundary) || @@ -455,6 +455,14 @@ object Checking { tp.derivedClassInfo( prefix = apply(tp.prefix), classParentsNEW = + if (config.Config.newScheme) + tp.parentsWithArgs.map { p => + apply(p).stripAnnots match { + case ref: RefType => ref + case _ => defn.ObjectType // can happen if class files are missing + } + } + else tp.parentsWithArgs.map { p => apply(p).underlyingClassRef(refinementOK = false) match { case ref: TypeRef => ref @@ -497,7 +505,7 @@ object Checking { ctx.error(ValueClassesMayNotWrapItself(clazz), clazz.pos) else { val clParamAccessors = clazz.asClass.paramAccessors.filter { param => - param.isTerm && !param.is(Flags.Accessor) + param.isTerm && !param.is(Flags.Accessor) // @!!! } clParamAccessors match { case List(param) => diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 93a2b3eda707..dce7ef6de5cd 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -915,7 +915,7 @@ class Namer { typer: Typer => val parentTypes = ensureFirstIsClass(parents.map(checkedParentType(_))) val parentRefs = ctx.normalizeToClassRefs(parentTypes, cls, decls) - typr.println(s"completing $denot, parents = $parents, parentTypes = $parentTypes, parentRefs = $parentRefs") + typr.println(i"completing $denot, parents = $parents%, %, parentTypes = $parentTypes%, %, parentRefs = $parentRefs%, %") tempInfo.finalize(denot, parentRefs) From 544c5dccd5421025fc6393e80b488e3ed95d5e5d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 28 Jul 2017 16:34:38 +0200 Subject: [PATCH 14/18] More fixes --- compiler/src/dotty/tools/dotc/ast/tpd.scala | 2 +- .../src/dotty/tools/dotc/config/Config.scala | 4 +- .../tools/dotc/core/ConstraintHandling.scala | 2 - .../dotty/tools/dotc/core/Denotations.scala | 4 ++ .../tools/dotc/core/SymDenotations.scala | 11 +++- .../tools/dotc/core/TypeApplications.scala | 6 +- .../dotty/tools/dotc/core/TypeComparer.scala | 1 + .../src/dotty/tools/dotc/core/TypeOps.scala | 56 ++++++++++--------- .../src/dotty/tools/dotc/core/Types.scala | 38 +++++++++---- .../tools/dotc/interactive/Interactive.scala | 2 +- .../tools/dotc/transform/ExplicitOuter.scala | 2 +- .../dotc/transform/FunctionalInterfaces.scala | 2 +- .../dotty/tools/dotc/transform/Splitter.scala | 2 +- .../tools/dotc/transform/SuperAccessors.scala | 6 +- .../dotc/transform/SyntheticMethods.scala | 2 +- .../dotty/tools/dotc/transform/TailRec.scala | 4 +- .../dotty/tools/dotc/typer/Applications.scala | 2 +- .../src/dotty/tools/dotc/typer/Namer.scala | 2 +- .../dotty/tools/dotc/typer/RefChecks.scala | 1 + .../dotty/tools/dotc/typer/TypeAssigner.scala | 2 +- .../src/dotty/tools/dotc/typer/Typer.scala | 2 +- 21 files changed, 93 insertions(+), 60 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 4ee30af5ede1..8a179ada87cb 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -214,7 +214,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def ClassDef(cls: ClassSymbol, constr: DefDef, body: List[Tree], superArgs: List[Tree] = Nil)(implicit ctx: Context): TypeDef = { val firstParentRef :: otherParentRefs = cls.info.parentRefs - val firstParent = cls.typeRef.baseTypeWithArgs(firstParentRef.symbol) + val firstParent = cls.appliedRef.baseTypeWithArgs(firstParentRef.symbol) val superRef = if (cls is Trait) TypeTree(firstParent) else { diff --git a/compiler/src/dotty/tools/dotc/config/Config.scala b/compiler/src/dotty/tools/dotc/config/Config.scala index 22f36dfea790..58d152de1f6d 100644 --- a/compiler/src/dotty/tools/dotc/config/Config.scala +++ b/compiler/src/dotty/tools/dotc/config/Config.scala @@ -79,7 +79,7 @@ object Config { final val traceDeepSubTypeRecursions = false /** When explaining subtypes and this flag is set, also show the classes of the compared types. */ - final val verboseExplainSubtype = false + final val verboseExplainSubtype = true /** If this flag is set, take the fast path when comparing same-named type-aliases and types */ final val fastPathForRefinedSubtype = true @@ -175,5 +175,5 @@ object Config { /** When in IDE, turn StaleSymbol errors into warnings instead of crashing */ final val ignoreStaleInIDE = true - final val newScheme = false + final val newScheme = true } diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala index 206eb616bb58..4202a590843f 100644 --- a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -202,8 +202,6 @@ trait ConstraintHandling { tp.derivedAppliedType(tycon, args.zipWithConserve(tycon.typeParams)(avoidInArg)) case tp: RefinedType if param occursIn tp.refinedInfo => - assert(fromBelow || variance <= 0, - "unsound approximation of $param with bounds ${constraint.fullUpperBound(param)}") tp.parent case tp: WildcardType => val bounds = tp.optBounds.orElse(TypeBounds.empty).bounds diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index 74a9138ca3b8..14a4af68d8fd 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -644,6 +644,10 @@ object Denotations { // ------ Forming types ------------------------------------------- /** The TypeRef representing this type denotation at its original location. */ + def appliedRef(implicit ctx: Context): Type = + if (Config.newScheme) typeRef.appliedTo(symbol.typeParams.map(_.typeRef)) + else typeRef + def typeRef(implicit ctx: Context): TypeRef = TypeRef(symbol.owner.thisType, symbol.name.asTypeName, this) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 7b00047c13f6..5caab5961651 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1424,7 +1424,14 @@ object SymDenotations { else ThisType.raw(TypeRef(pre, symbol.asType)) } */ - private[this] var myTypeRef: TypeRef = null + private[this] var myAppliedRef: Type = null // @!!!: Use classInfo.appliedRef instead? + private[this] var myTypeRef: TypeRef = null // @!!!: Use classInfo.appliedRef instead? + + override def appliedRef(implicit ctx: Context): Type = { + if (myAppliedRef == null) myAppliedRef = super.appliedRef + if (ctx.erasedTypes) myAppliedRef.typeConstructor + else myAppliedRef + } override def typeRef(implicit ctx: Context): TypeRef = { if (myTypeRef == null) myTypeRef = super.typeRef @@ -1632,7 +1639,7 @@ object SymDenotations { def computeBaseTypeOf(tp: Type): Type = { Stats.record("computeBaseTypeOf") if (symbol.isStatic && tp.derivesFrom(symbol) && symbol.typeParams.isEmpty) - symbol.typeRef + symbol.appliedRef else tp match { case tp: RefType => val subcls = tp.symbol diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index 79da18fe2326..622e163ea477 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -221,6 +221,8 @@ class TypeApplications(val self: Type) extends AnyVal { Nil case self: WildcardType => self.optBounds.typeParams + case self: AppliedType => + Nil case self: TypeProxy => self.superType.typeParams case _ => @@ -292,7 +294,7 @@ class TypeApplications(val self: Type) extends AnyVal { */ def EtaExpand(tparams: List[TypeSymbol])(implicit ctx: Context): Type = { val tparamsToUse = if (variancesConform(typeParams, tparams)) tparams else typeParamSymbols - HKTypeLambda.fromParams(tparamsToUse, self.appliedTo(tparams map (_.typeRef))) + HKTypeLambda.fromParams(tparamsToUse, self.appliedTo(tparams.map(_.typeRef))) //.ensuring(res => res.EtaReduce =:= self, s"res = $res, core = ${res.EtaReduce}, self = $self, hc = ${res.hashCode}") } @@ -539,7 +541,7 @@ class TypeApplications(val self: Type) extends AnyVal { self.derivedExprType(tp.translateParameterized(from, to)) case _ => if (self.derivesFrom(from)) - if (ctx.erasedTypes) to.typeRef + if (ctx.erasedTypes) to.typeRef // @!!! can be dropped; appliedTo does the right thing anyway else if (Config.newScheme) to.typeRef.appliedTo(self.baseType(from).argInfos) else RefinedType(to.typeRef, to.typeParams.head.name, self.member(from.typeParams.head.name).info) else self diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 2a3f37dd3159..fa3b8fe4aa85 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -563,6 +563,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { def isNullable(tp: Type): Boolean = tp.widenDealias match { case tp: TypeRef => tp.symbol.isNullableClass case tp: RefinedOrRecType => isNullable(tp.parent) + case tp: AppliedType => isNullable(tp.tycon) case AndType(tp1, tp2) => isNullable(tp1) && isNullable(tp2) case OrType(tp1, tp2) => isNullable(tp1) || isNullable(tp2) case _ => false diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index b63f68c1fb28..1a6c922225e2 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -49,8 +49,10 @@ trait TypeOps { this: Context => // TODO: Make standalone object. * The scheme is efficient in particular because we expect that unsafe situations are rare; * most compiles would contain none, so no scanning would be necessary. */ - final def asSeenFrom(tp: Type, pre: Type, cls: Symbol): Type = + final def asSeenFrom(tp: Type, pre: Type, cls: Symbol): Type = { + //println(i"$tp.asSeenFrom($pre, $cls)") asSeenFrom(tp, pre, cls, null) + } /** Helper method, taking a map argument which is instantiated only for more * complicated cases of asSeenFrom. @@ -88,32 +90,32 @@ trait TypeOps { this: Context => // TODO: Make standalone object. } } - @tailrec - def argForParam(pre: Type, cls: Symbol, tparam: Symbol): Type = { - def fail() = throw new TypeError(ex"$pre contains no matching argument for ${tparam.showLocated} ") - if ((pre eq NoType) || (pre eq NoPrefix) || (cls is PackageClass)) fail() - val base = pre.baseType(cls) - if (cls `eq` tparam.owner) { - var tparams = cls.typeParams - var args = base.argInfos - var idx = 0 - while (tparams.nonEmpty && args.nonEmpty) { - if (tparams.head.eq(tparam)) - return args.head match { - case bounds: TypeBounds => - val v = currentVariance - if (v > 0) bounds.hi - else if (v < 0) bounds.lo - else TypeArgRef(pre, cls.typeRef, idx) - case arg => arg - } - tparams = tparams.tail - args = args.tail - idx += 1 - } - fail() + def argForParam(pre: Type, tparam: Symbol): Type = { + val tparamCls = tparam.owner + pre.baseType(tparamCls) match { + case AppliedType(_, allArgs) => + var tparams = tparamCls.typeParams + var args = allArgs + var idx = 0 + while (tparams.nonEmpty && args.nonEmpty) { + if (tparams.head.eq(tparam)) + return args.head match { + case bounds: TypeBounds => + val v = currentVariance + if (v > 0) bounds.hi + else if (v < 0) bounds.lo + else TypeArgRef(pre, cls.typeRef, idx) + case arg => arg + } + tparams = tparams.tail + args = args.tail + idx += 1 + } + throw new AssertionError(ex"$pre contains no matching argument for ${tparam.showLocated} ") + case OrType(tp1, tp2) => argForParam(tp1, cls) | argForParam(tp2, cls) + case AndType(tp1, tp2) => argForParam(tp1, cls) & argForParam(tp2, cls) + case _ => tp } - else argForParam(base.normalizedPrefix, cls.owner.enclosingClass, tparam) } /*>|>*/ ctx.conditionalTraceIndented(TypeOps.track, s"asSeen ${tp.show} from (${pre.show}, ${cls.show})", show = true) /*<|<*/ { // !!! DEBUG @@ -124,7 +126,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object. else { val pre1 = asSeenFrom(tp.prefix, pre, cls, theMap) if (Config.newScheme && sym.is(TypeParam)) - argForParam(pre1, cls, sym) + argForParam(pre1, sym) else { if (pre1.isUnsafeNonvariant) { val safeCtx = ctx.withProperty(TypeOps.findMemberLimit, Some(())) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 5dd944428e87..074ab4e9ca2d 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -462,7 +462,7 @@ object Types { // We need a valid prefix for `asSeenFrom` val pre = this match { case tp: ClassInfo => - tp.typeRef + tp.typeRef // @!!! appliedRef case _ => widenIfUnstable } @@ -1248,7 +1248,7 @@ object Types { /** This type seen as a TypeBounds */ final def bounds(implicit ctx: Context): TypeBounds = this match { case tp: TypeBounds => tp - case ci: ClassInfo => TypeAlias(ci.typeRef) + case ci: ClassInfo => TypeAlias(ci.appliedRef) case wc: WildcardType => wc.optBounds match { case bounds: TypeBounds => bounds @@ -2011,7 +2011,7 @@ object Types { // Those classes are non final as Linker extends them. class TermRefWithFixedSym(prefix: Type, name: TermName, val fixedSym: TermSymbol) extends TermRef(prefix, name) with WithFixedSym - class TypeRefWithFixedSym(prefix: Type, name: TypeName, val fixedSym: TypeSymbol) extends TypeRef(prefix, name) with WithFixedSym + class TypeRefWithFixedSym(prefix: Type, name: TypeName, val fixedSym: TypeSymbol) extends TypeRef(prefix, name) with WithFixedSym { /** Assert current phase does not have erasure semantics */ private def assertUnerased()(implicit ctx: Context) = @@ -3059,14 +3059,16 @@ object Types { if (ctx.period != validSuper) { validSuper = ctx.period cachedSuper = tycon match { - case tp: HKTypeLambda => defn.AnyType - case tp: TypeVar if !tp.inst.exists => + case tycon: HKTypeLambda => defn.AnyType + case tycon: TypeVar if !tycon.inst.exists => // supertype not stable, since underlying might change validSuper = Nowhere - tp.underlying.applyIfParameterized(args) - case tp: TypeProxy => - if (tp.typeSymbol.is(Provisional)) validSuper = Nowhere - tp.superType.applyIfParameterized(args) + tycon.underlying.applyIfParameterized(args) + case tycon: TypeProxy => + val sym = tycon.typeSymbol + if (sym.is(Provisional)) validSuper = Nowhere + if (sym.isClass) tycon + else tycon.superType.applyIfParameterized(args) case _ => defn.AnyType } } @@ -3466,8 +3468,11 @@ object Types { } /** The class type with all type parameters */ - def fullyAppliedRef(implicit ctx: Context): Type = fullyAppliedRef(cls.typeRef, cls.typeParams) + def fullyAppliedRef(implicit ctx: Context): Type = + if (Config.newScheme && false) cls.appliedRef + else fullyAppliedRef(cls.typeRef, cls.typeParams) + private var appliedRefCache: Type = null private var typeRefCache: TypeRef = null def typeRef(implicit ctx: Context): TypeRef = { @@ -3479,6 +3484,19 @@ object Types { typeRefCache } + def appliedRef(implicit ctx: Context): Type = { + def clsDenot = if (prefix eq cls.owner.thisType) cls.denot else cls.denot.copySymDenotation(info = this) + if (appliedRefCache == null) { + val tref = + if ((cls is PackageClass) || cls.owner.isTerm) symbolicTypeRef + else TypeRef(prefix, cls.name, clsDenot) + appliedRefCache = + if (Config.newScheme) tref.appliedTo(cls.typeParams.map(_.typeRef)) + else tref + } + appliedRefCache + } + def symbolicTypeRef(implicit ctx: Context): TypeRef = TypeRef(prefix, cls) // cached because baseType needs parents diff --git a/compiler/src/dotty/tools/dotc/interactive/Interactive.scala b/compiler/src/dotty/tools/dotc/interactive/Interactive.scala index 4284da2967a9..9d3ab2e57e9e 100644 --- a/compiler/src/dotty/tools/dotc/interactive/Interactive.scala +++ b/compiler/src/dotty/tools/dotc/interactive/Interactive.scala @@ -79,7 +79,7 @@ object Interactive { // those from the enclosing class boundary.enclosingClass match { case csym: ClassSymbol => - val classRef = csym.classInfo.typeRef + val classRef = csym.classInfo.appliedRef completions(classRef, boundary) case _ => Nil diff --git a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala index 8573e1e930d9..cb10173dd554 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -157,7 +157,7 @@ object ExplicitOuter { /** A new outer accessor or param accessor */ private def newOuterSym(owner: ClassSymbol, cls: ClassSymbol, name: TermName, flags: FlagSet)(implicit ctx: Context) = { - val target = cls.owner.enclosingClass.typeRef + val target = cls.owner.enclosingClass.appliedRef val info = if (flags.is(Method)) ExprType(target) else target ctx.withPhaseNoEarlier(ctx.explicitOuterPhase.next) // outer accessors are entered at explicitOuter + 1, should not be defined before. .newSymbol(owner, name, Synthetic | flags, info, coord = cls.coord) diff --git a/compiler/src/dotty/tools/dotc/transform/FunctionalInterfaces.scala b/compiler/src/dotty/tools/dotc/transform/FunctionalInterfaces.scala index bde902152414..d8920fc6b043 100644 --- a/compiler/src/dotty/tools/dotc/transform/FunctionalInterfaces.scala +++ b/compiler/src/dotty/tools/dotc/transform/FunctionalInterfaces.scala @@ -72,7 +72,7 @@ class FunctionalInterfaces extends MiniPhaseTransform { // symbols loaded from classpath aren't defined in periods earlier than when they where loaded val interface = ctx.withPhase(ctx.typerPhase).getClassIfDefined(functionPackage ++ interfaceName) if (interface.exists) { - val tpt = tpd.TypeTree(interface.asType.typeRef) + val tpt = tpd.TypeTree(interface.asType.appliedRef) tpd.Closure(tree.env, tree.meth, tpt) } else tree } else tree diff --git a/compiler/src/dotty/tools/dotc/transform/Splitter.scala b/compiler/src/dotty/tools/dotc/transform/Splitter.scala index d62be1a827e0..dbd9f15e8532 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splitter.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splitter.scala @@ -92,7 +92,7 @@ class Splitter extends MiniPhaseTransform { thisTransform => else { def choose(qual: Tree, syms: List[Symbol]): Tree = { def testOrCast(which: Symbol, mbr: Symbol) = - qual.select(which).appliedToType(mbr.owner.typeRef) + qual.select(which).appliedToType(mbr.owner.appliedRef) def select(sym: Symbol) = { val qual1 = if (qual.tpe derivesFrom sym.owner) qual diff --git a/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala b/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala index 9a505ce8d49e..7e59b770e02c 100644 --- a/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ b/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -297,7 +297,7 @@ class SuperAccessors(thisTransformer: DenotTransformer) { val clazz = currentClass val host = hostForAccessorOf(sym, clazz) def accessibleThroughSubclassing = - validCurrentClass && (clazz.classInfo.selfType <:< sym.owner.typeRef) && !clazz.is(Trait) + validCurrentClass && (clazz.classInfo.selfType <:< sym.owner.appliedRef) && !clazz.is(Trait) val isCandidate = ( sym.is(Protected) @@ -308,7 +308,7 @@ class SuperAccessors(thisTransformer: DenotTransformer) { && (sym.enclosingPackageClass == sym.accessBoundary(sym.enclosingPackageClass)) ) val hostSelfType = host.classInfo.selfType - def isSelfType = !(host.typeRef <:< hostSelfType) && { + def isSelfType = !(host.appliedRef <:< hostSelfType) && { if (hostSelfType.typeSymbol.is(JavaDefined)) ctx.restrictionError( s"cannot accesses protected $sym from within $clazz with host self type $hostSelfType", pos) @@ -332,7 +332,7 @@ class SuperAccessors(thisTransformer: DenotTransformer) { */ private def hostForAccessorOf(sym: Symbol, referencingClass: ClassSymbol)(implicit ctx: Context): ClassSymbol = if (referencingClass.derivesFrom(sym.owner) - || referencingClass.classInfo.selfType <:< sym.owner.typeRef + || referencingClass.classInfo.selfType <:< sym.owner.appliedRef || referencingClass.enclosingPackageClass == sym.owner.enclosingPackageClass) { assert(referencingClass.isClass, referencingClass) referencingClass diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala index c8bef28dcd66..5bf8539adb69 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala @@ -54,7 +54,7 @@ class SyntheticMethods(thisTransformer: DenotTransformer) { /** The synthetic methods of the case or value class `clazz`. */ def syntheticMethods(clazz: ClassSymbol)(implicit ctx: Context): List[Tree] = { - val clazzType = clazz.typeRef + val clazzType = clazz.appliedRef lazy val accessors = if (isDerivedValueClass(clazz)) clazz.termParamAccessors diff --git a/compiler/src/dotty/tools/dotc/transform/TailRec.scala b/compiler/src/dotty/tools/dotc/transform/TailRec.scala index 10c18e165dfb..906a9d5c3ad7 100644 --- a/compiler/src/dotty/tools/dotc/transform/TailRec.scala +++ b/compiler/src/dotty/tools/dotc/transform/TailRec.scala @@ -244,7 +244,7 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete if (isRecursiveCall) { if (ctx.tailPos) { - val receiverIsSame = enclosingClass.typeRef.widenDealias =:= recvWiden + val receiverIsSame = enclosingClass.appliedRef.widenDealias =:= recvWiden val receiverIsThis = prefix.tpe =:= thisType || prefix.tpe.widen =:= thisType def rewriteTailCall(recv: Tree): Tree = { @@ -282,7 +282,7 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete } else fail(defaultReason) } else { - val receiverIsSuper = (method.name eq sym) && enclosingClass.typeRef.widen <:< recvWiden + val receiverIsSuper = (method.name eq sym) && enclosingClass.appliedRef.widen <:< recvWiden if (receiverIsSuper) fail("it contains a recursive call targeting a supertype") else continue diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 563c1374c833..ba8937db84c4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1075,7 +1075,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => tp1.paramInfos.isEmpty && tp2.isInstanceOf[LambdaType] case tp1: PolyType => // (2) val tparams = ctx.newTypeParams(alt1.symbol, tp1.paramNames, EmptyFlags, tp1.instantiateBounds) - isAsSpecific(alt1, tp1.instantiate(tparams map (_.typeRef)), alt2, tp2) + isAsSpecific(alt1, tp1.instantiate(tparams.map(_.typeRef)), alt2, tp2) case _ => // (3) tp2 match { case tp2: MethodType => true // (3a) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index dce7ef6de5cd..798c439b66ac 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -92,7 +92,7 @@ trait NamerContextOps { this: Context => * type of the constructed instance is returned */ def effectiveResultType(sym: Symbol, typeParams: List[Symbol], given: Type) = - if (sym.name == nme.CONSTRUCTOR) sym.owner.typeRef.appliedTo(typeParams map (_.typeRef)) + if (sym.name == nme.CONSTRUCTOR) sym.owner.typeRef.appliedTo(typeParams.map(_.typeRef)) else given /** if isConstructor, make sure it has one non-implicit parameter list */ diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 8c0aab5a51ad..b52891ccddef 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -84,6 +84,7 @@ object RefChecks { */ private def upwardsThisType(cls: Symbol)(implicit ctx: Context) = cls.info match { case ClassInfo(_, _, _, _, tp: Type) if (tp ne cls.typeRef) && !cls.is(ModuleOrFinal) => + // @!!! case ClassInfo(_, _, _, _, selfTp: Type) if selfTp.exists && !cls.is(ModuleOrFinal) => SkolemType(cls.typeRef).withName(nme.this_) case _ => cls.thisType diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index ec958a491518..68619e03c017 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -304,7 +304,7 @@ trait TypeAssigner { errorType("ambiguous parent class qualifier", tree.pos) } val owntype = - if (mixinClass.exists) mixinClass.typeRef + if (mixinClass.exists) mixinClass.appliedRef else if (!mix.isEmpty) findMixinSuper(cls.info) else if (inConstrCall || ctx.erasedTypes) cls.info.firstParentRef else { diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 11cad9b716b6..346243507081 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1427,7 +1427,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val impl1 = cpy.Template(impl)(constr1, parents1, self1, body1) .withType(dummy.nonMemberTermRef) checkVariance(impl1) - if (!cls.is(AbstractOrTrait) && !ctx.isAfterTyper) checkRealizableBounds(cls.typeRef, cdef.namePos) + if (!cls.is(AbstractOrTrait) && !ctx.isAfterTyper) checkRealizableBounds(cls.appliedRef, cdef.namePos) val cdef1 = assignType(cpy.TypeDef(cdef)(name, impl1), cls) if (ctx.phase.isTyper && cdef1.tpe.derivesFrom(defn.DynamicClass) && !ctx.dynamicsEnabled) { val isRequired = parents1.exists(_.tpe.isRef(defn.DynamicClass)) From 9d7ed03410b4f3ca64dc679a11a1f5aefc40056c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 29 Jul 2017 12:58:21 +0200 Subject: [PATCH 15/18] Fix stray brace --- compiler/src/dotty/tools/dotc/core/Types.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 074ab4e9ca2d..f17f56d8fae4 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -2011,7 +2011,7 @@ object Types { // Those classes are non final as Linker extends them. class TermRefWithFixedSym(prefix: Type, name: TermName, val fixedSym: TermSymbol) extends TermRef(prefix, name) with WithFixedSym - class TypeRefWithFixedSym(prefix: Type, name: TypeName, val fixedSym: TypeSymbol) extends TypeRef(prefix, name) with WithFixedSym { + class TypeRefWithFixedSym(prefix: Type, name: TypeName, val fixedSym: TypeSymbol) extends TypeRef(prefix, name) with WithFixedSym /** Assert current phase does not have erasure semantics */ private def assertUnerased()(implicit ctx: Context) = @@ -2695,7 +2695,7 @@ object Types { * def f(x: C)(y: x.S) // dependencyStatus = TrueDeps * def f(x: C)(y: x.T) // dependencyStatus = FalseDeps, i.e. * // dependency can be eliminated by dealiasing. - */ + */ private def dependencyStatus(implicit ctx: Context): DependencyStatus = { if (myDependencyStatus != Unknown) myDependencyStatus else { From f2baac37b66fac89785d041f64c2d2d081f8e020 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 29 Jul 2017 17:55:26 +0200 Subject: [PATCH 16/18] Fix Reducer for applied types. --- .../tools/dotc/core/TypeApplications.scala | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index 622e163ea477..633385dc83ac 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -160,10 +160,25 @@ object TypeApplications { p.binder == tycon && args(p.paramNum).isInstanceOf[TypeBounds] def canReduceWildcard(p: TypeParamRef) = !ctx.mode.is(Mode.AllowLambdaWildcardApply) || available.contains(p.paramNum) - def apply(t: Type) = t match { - case t @ TypeAlias(p: TypeParamRef) if hasWildcardArg(p) && canReduceWildcard(p) => + + // If this is a reference to a reducable type parameter corresponding to a + // wildcard argument, return the wildcard argument, otherwise apply recursively. + def applyArg(arg: Type): Type = arg match { + case p: TypeParamRef if hasWildcardArg(p) && canReduceWildcard(p) => available -= p.paramNum args(p.paramNum) + case _ => + apply(arg) + } + + def apply(t: Type) = t match { + case t @ TypeAlias(alias) => + applyArg(alias) match { + case arg1: TypeBounds => arg1 + case arg1 => t.derivedTypeAlias(arg1) + } + case t @ AppliedType(tycon, args1) if tycon.typeSymbol.isClass => + t.derivedAppliedType(apply(tycon), args1.mapConserve(applyArg)) case p: TypeParamRef if p.binder == tycon => args(p.paramNum) match { case TypeBounds(lo, hi) => From b715a78af57e74b5be972d385a91918ca76f5faa Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 30 Jul 2017 12:43:05 +0200 Subject: [PATCH 17/18] Fix typing of _* arguments --- compiler/src/dotty/tools/dotc/core/TypeApplications.scala | 2 +- compiler/src/dotty/tools/dotc/typer/Typer.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index 633385dc83ac..7819e882e693 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -170,7 +170,7 @@ object TypeApplications { case _ => apply(arg) } - + def apply(t: Type) = t match { case t @ TypeAlias(alias) => applyArg(alias) match { diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 346243507081..51d2656c9867 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -538,7 +538,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit if (untpd.isWildcardStarArg(tree)) cases( ifPat = ascription(TypeTree(defn.RepeatedParamType.appliedTo(pt)), isWildcard = true), - ifExpr = seqToRepeated(typedExpr(tree.expr, defn.SeqType)), + ifExpr = seqToRepeated(typedExpr(tree.expr, defn.SeqType.appliedTo(defn.AnyType))), wildName = nme.WILDCARD_STAR) else { def typedTpt = checkSimpleKinded(typedType(tree.tpt)) From 36eabf46ee9edd87612285747970f4cae91e3949 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 30 Jul 2017 16:28:38 +0200 Subject: [PATCH 18/18] Fix bounds propagation The previous type parameter representation in terms of type members achieved bounds propagation by waiting until a type member was selected and then taking original bounds and refinements together as its info. This no longer works with explicit applications. Instead, we need to propagate bounds into wildcard arguments explicitly, when a type application is created. --- .../src/dotty/tools/dotc/config/Config.scala | 2 +- .../tools/dotc/core/TypeApplications.scala | 43 +++++++++++++++++-- .../src/dotty/tools/dotc/typer/Namer.scala | 2 +- .../dotty/tools/dotc/typer/ProtoTypes.scala | 1 + 4 files changed, 43 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/config/Config.scala b/compiler/src/dotty/tools/dotc/config/Config.scala index 58d152de1f6d..b4c72b196ccd 100644 --- a/compiler/src/dotty/tools/dotc/config/Config.scala +++ b/compiler/src/dotty/tools/dotc/config/Config.scala @@ -175,5 +175,5 @@ object Config { /** When in IDE, turn StaleSymbol errors into warnings instead of crashing */ final val ignoreStaleInIDE = true - final val newScheme = true + val newScheme = true } diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index 7819e882e693..b7fc1b6f6767 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -407,14 +407,51 @@ class TypeApplications(val self: Type) extends AnyVal { } case nil => t } + val stripped = self.stripTypeVar + val dealiased = stripped.safeDealias + + /** Normalize a TypeBounds argument. This involves (1) propagating bounds + * from the type parameters into the argument, (2) possibly choosing the argument's + * upper or lower bound according to variance. + * + * Bounds propagation works as follows: If the dealiased type constructor is a TypeRef + * `p.c`, + * - take the type paramater bounds of `c` as seen from `p`, + * - substitute concrete (non-bound) arguments for corresponding formal type parameters, + * - interpolate, so that any (F-bounded) type parameters in the resulting bounds are avoided. + * The resulting bounds are joined (via &) with corresponding type bound arguments. + */ def normalizeWildcardArg(arg: Type, tparam: TypeParamInfo): Type = arg match { case TypeBounds(lo, hi) => val v = tparam.paramVariance - if (v > 0) hi else if (v < 0) lo else arg + val avoidParams = new ApproximatingTypeMap { + variance = v + def apply(t: Type) = t match { + case t: TypeRef if typParams contains t.symbol => + val bounds = apply(t.info).bounds + approx(bounds.lo, bounds.hi) + case _ => mapOver(t) + } + } + val pbounds = dealiased match { + case dealiased @ TypeRef(prefix, _) => + val (concreteArgs, concreteParams) = + args.zip(typeParams.asInstanceOf[List[TypeSymbol]]) + .filter(!_._1.isInstanceOf[TypeBounds]) + .unzip + avoidParams( + tparam.paramInfo.asSeenFrom(prefix, dealiased.symbol.owner) + .subst(concreteParams, concreteArgs)).orElse(TypeBounds.empty) + case _ => + TypeBounds.empty + } + //typr.println(i"normalize arg $arg for $tparam in $self app $args%, %, pbounds, = $pbounds") + if (v > 0) hi & pbounds.hiBound + else if (v < 0) lo | pbounds.loBound + else arg & pbounds case _ => arg } - val stripped = self.stripTypeVar - val dealiased = stripped.safeDealias + if (args.isEmpty || ctx.erasedTypes) self else dealiased match { case dealiased: HKTypeLambda => diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 798c439b66ac..51b6b5b85a64 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -336,7 +336,7 @@ class Namer { typer: Typer => */ def enterSymbol(sym: Symbol)(implicit ctx: Context) = { if (sym.exists) { - typr.println(s"entered: $sym in ${ctx.owner} and ${ctx.effectiveScope}") + typr.println(s"entered: $sym in ${ctx.owner}") ctx.enter(sym) } sym diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index 2d11b52d7ab2..c6396acc65a9 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -83,6 +83,7 @@ object ProtoTypes { def isMatchedBy(tp1: Type)(implicit ctx: Context) = true def map(tm: TypeMap)(implicit ctx: Context): ProtoType = this def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T = x + override def toString = getClass.toString } /** A class marking ignored prototypes that can be revealed by `deepenProto` */