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/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/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 016db8e2c01b..8a179ada87cb 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -213,8 +213,8 @@ 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 firstParent = cls.typeRef.baseTypeWithArgs(firstParentRef.symbol) + val firstParentRef :: otherParentRefs = cls.info.parentRefs + val firstParent = cls.appliedRef.baseTypeWithArgs(firstParentRef.symbol) val superRef = if (cls is Trait) TypeTree(firstParent) else { @@ -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/config/Config.scala b/compiler/src/dotty/tools/dotc/config/Config.scala index ffc4d65632cf..b4c72b196ccd 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 @@ -174,4 +174,6 @@ object Config { /** When in IDE, turn StaleSymbol errors into warnings instead of crashing */ final val ignoreStaleInIDE = true + + val newScheme = true } diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala index 15f24a7fe50c..4202a590843f 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,15 @@ 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 => + 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..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) } @@ -605,7 +606,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/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/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 bfaa12729090..5caab5961651 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)) && @@ -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 )) @@ -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) @@ -1219,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 @@ -1266,10 +1267,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 +1283,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 +1303,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 +1314,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 _ => } @@ -1365,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 @@ -1379,6 +1391,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 @@ -1404,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 @@ -1429,10 +1456,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) } @@ -1558,10 +1585,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) @@ -1573,7 +1600,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 = { @@ -1581,11 +1608,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 +1624,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,28 +1636,28 @@ object SymDenotations { } } - def computeBaseTypeRefOf(tp: Type): Type = { + def computeBaseTypeOf(tp: Type): Type = { Stats.record("computeBaseTypeOf") - if (symbol.isStatic && tp.derivesFrom(symbol)) - symbol.typeRef + if (symbol.isStatic && tp.derivesFrom(symbol) && symbol.typeParams.isEmpty) + symbol.appliedRef else tp match { - case tp: TypeRef => + case tp: RefType => val subcls = tp.symbol if (subcls eq symbol) 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) 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 +1665,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 +1686,7 @@ object SymDenotations { throw ex } case _ => - computeBaseTypeRefOf(tp) + computeBaseTypeOf(tp) } } } @@ -1675,8 +1702,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) @@ -1708,11 +1735,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/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 fb2300ff1829..b7fc1b6f6767 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. */ @@ -76,7 +64,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 +75,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 +99,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 _ => @@ -170,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) => @@ -183,7 +188,7 @@ object TypeApplications { case arg => arg } - case _: TypeBounds | _: HKApply => + case _: TypeBounds | _: HKApply | _: AppliedType => val saved = available available = Set() try mapOver(t) @@ -224,13 +229,15 @@ 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 => Nil case self: WildcardType => self.optBounds.typeParams + case self: AppliedType => + Nil case self: TypeProxy => self.superType.typeParams case _ => @@ -257,6 +264,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 => @@ -301,7 +309,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}") } @@ -401,6 +409,49 @@ class TypeApplications(val self: Type) extends AnyVal { } 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 + 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 + } + if (args.isEmpty || ctx.erasedTypes) self else dealiased match { case dealiased: HKTypeLambda => @@ -408,7 +459,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 +472,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 @@ -443,7 +494,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 => @@ -451,7 +502,10 @@ 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) + AppliedType(self, args.zipWithConserve(typParams)(normalizeWildcardArg)) + else + matchParams(dealiased, typParams, args) } } @@ -485,10 +539,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) @@ -497,31 +553,15 @@ 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) { - def default = self.baseTypeRef(base).appliedTo(baseArgInfos(base)) + 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 { case tp: TypeRef => @@ -529,7 +569,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) @@ -553,7 +593,8 @@ 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 } @@ -573,7 +614,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 } @@ -581,20 +622,21 @@ 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. */ final def withoutArgs(typeArgs: List[Type]): Type = self match { case HKApply(tycon, args) => tycon + case AppliedType(tycon, args) => tycon 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 4e911eed5bf4..fa3b8fe4aa85 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 } @@ -368,7 +370,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 } @@ -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 => @@ -518,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) => @@ -557,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 @@ -566,6 +573,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 { @@ -585,6 +594,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 => @@ -713,7 +724,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 +782,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 @@ -784,6 +795,173 @@ 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 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: 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) && appOK(tp1w.baseType(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`. */ @@ -810,12 +988,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.baseTypeRef(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 } @@ -983,10 +1169,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 +1387,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,9 +1558,17 @@ 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. + // @!!! still needed? case tp1: RefinedType => tp2 match { case tp2: RefinedType if tp1.refinedName == tp2.refinedName => @@ -1553,17 +1780,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..8969fa8b44e1 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -381,9 +381,12 @@ 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 => + 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) @@ -412,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 @@ -426,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(_) => @@ -437,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) @@ -488,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) } @@ -509,8 +513,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,7 +531,11 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean sigName(PhantomErasure.erasedPhantomType) else normalizeClass(sym.asClass).fullName.asTypeName - case defn.ArrayOf(elem) => + case tp: AppliedType => + sigName(if (tp.tycon.isRef(defn.ArrayClass)) this(tp) else tp.superType) + case ErasedValueType(_, underlying) => + sigName(underlying) + 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 829b5acec00c..1a6c922225e2 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 @@ -48,14 +49,28 @@ 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. */ 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. @@ -66,20 +81,40 @@ 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 (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 - toPrefix(pre.baseTypeRef(cls).normalizedPrefix, cls.owner, thiscls) + toPrefix(pre.baseType(cls).normalizedPrefix, cls.owner, thiscls) + } + } + + 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 } } @@ -90,22 +125,26 @@ trait TypeOps { this: Context => // TODO: Make standalone object. if (sym.isStatic) tp else { val pre1 = asSeenFrom(tp.prefix, pre, cls, theMap) - 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, 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) case _: BoundType | NoPrefix => tp - case tp: RefinedType => + case tp: RefinedType => //@!!! tp.derivedRefinedType( asSeenFrom(tp.parent, pre, cls, theMap), tp.refinedName, @@ -163,10 +202,13 @@ 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 => + case tp: RefinedType => // @!!! tp.derivedRefinedType(simplify(tp.parent, theMap), tp.refinedName, simplify(tp.refinedInfo, theMap)) case tp: TypeAlias => tp.derivedTypeAlias(simplify(tp.alias, theMap)) @@ -213,14 +255,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) => @@ -236,6 +286,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 } @@ -256,9 +307,9 @@ 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) + base.mapReduceOr(identity)(mergeRefinedOrApplied) } doms.map(baseTp).reduceLeft(AndType.apply) } @@ -273,35 +324,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,11 +346,26 @@ trait TypeOps { this: Context => // TODO: Make standalone object. } } - /** 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. + 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` 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 @@ -428,15 +465,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 40fbc80f85f0..f17f56d8fae4 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 } @@ -461,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 } @@ -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 => @@ -566,6 +569,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 @@ -583,7 +587,17 @@ 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 tc: TypeRef if tc.symbol.isClass => + go(tc) + 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)) @@ -826,13 +840,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) } @@ -958,6 +976,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 @@ -966,10 +992,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 } @@ -1001,6 +1023,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. @@ -1010,6 +1038,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 => @@ -1147,29 +1177,36 @@ 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 tp @ AppliedType(tycon, args) if tycon.typeSymbol.isClass => + tycon.parentsNEW.map(_.subst(tycon.typeSymbol.typeParams, args)) + case tp: TypeProxy => + tp.superType.parentsNEW + case _ => Nil } /** The first parent of this type, AnyRef if list of parents is empty */ - def firstParent(implicit ctx: Context): TypeRef = parents match { + def firstParentRef(implicit ctx: Context): TypeRef = parentRefs match { case p :: _ => p 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 first parent of this type, AnyRef if list of parents is empty */ + def firstParentNEW(implicit ctx: Context): Type = parentsNEW match { + case p :: _ => p + case _ => defn.AnyType } /** The parameter types of a PolyType or MethodType, Empty list for others */ @@ -1211,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 @@ -1220,6 +1257,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. @@ -1357,7 +1404,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 @@ -1435,6 +1482,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 +1518,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 @@ -1595,10 +1647,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) || @@ -2149,25 +2199,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 ------------------------------------------------ @@ -2184,7 +2232,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 @@ -2197,7 +2246,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 @@ -2500,11 +2549,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 @@ -2518,7 +2568,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 @@ -2589,6 +2639,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 @@ -2644,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 { @@ -2828,6 +2879,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 @@ -2979,7 +3031,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 { @@ -2994,6 +3046,89 @@ 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 = { + if (ctx.period != validSuper) { + validSuper = ctx.period + cachedSuper = tycon match { + case tycon: HKTypeLambda => defn.AnyType + case tycon: TypeVar if !tycon.inst.exists => + // supertype not stable, since underlying might change + validSuper = Nowhere + 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 + } + } + 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 { @@ -3067,6 +3202,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 { @@ -3278,7 +3431,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 { @@ -3290,7 +3443,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 @@ -3301,28 +3454,25 @@ 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 { - 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) + 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 = { @@ -3334,21 +3484,33 @@ 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 - private var parentsCache: List[TypeRef] = null + private var parentsCache: List[Type] = null /** The parent type refs as seen from the given prefix */ - override def parents(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] = - parents 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) @@ -3356,20 +3518,26 @@ object Types { } } + 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. */ @@ -3385,14 +3553,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)) } @@ -3624,6 +3792,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 => @@ -3689,8 +3859,12 @@ 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 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 = @@ -3713,12 +3887,33 @@ 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 + 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)) @@ -3764,11 +3959,18 @@ 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)) case tp: LazyRef => - LazyRef(() => this(tp.ref)) + LazyRef(_ => this(tp.ref)) case tp: ClassInfo => mapClassInfo(tp) @@ -3828,12 +4030,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) } } @@ -3883,9 +4085,18 @@ 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 + 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 @@ -3913,9 +4124,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 @@ -3936,6 +4147,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) @@ -3998,6 +4226,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) @@ -4144,7 +4378,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/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index 4c73115cac50..91e6b2776c95 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) @@ -423,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/tasty/TastyFormat.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index cd0d427f3510..b87f332c4319 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -164,10 +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 @@ -256,7 +258,7 @@ object TastyFormat { final val OBJECTCLASS = 40 final val SIGNED = 63 - + final val firstInternalTag = 64 final val IMPLMETH = 64 @@ -322,6 +324,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 @@ -380,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 @@ -512,6 +516,7 @@ object TastyFormat { case DOUBLEconst => "DOUBLEconst" case STRINGconst => "STRINGconst" case RECtype => "RECtype" + case LAZYref => "LAZYref" case IDENT => "IDENT" case IDENTtpt => "IDENTtpt" @@ -562,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 338a395ab685..f1c9542ddd5f 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) => @@ -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) @@ -246,6 +249,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..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) @@ -293,6 +295,10 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi RecType(rt => registeringType(rt, readType())) case RECthis => RecThis(readTypeRef().asInstanceOf[RecType]) + case LAZYref => + val rdr = fork + skipTree() + LazyRef(implicit ctx => rdr.readType()) case SHARED => val ref = readAddr() typeAtAddr.getOrElseUpdate(ref, forkAt(ref).readType()) @@ -711,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) @@ -991,7 +997,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() diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index c36ce1dc14bb..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) @@ -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/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/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 8a10ef60ebbe..0f334c05b7e5 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._ @@ -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 _ => @@ -123,7 +125,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 +144,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]) = @@ -154,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) => @@ -189,6 +196,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 => @@ -323,7 +332,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 +410,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/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index c494110201e4..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 = @@ -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/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index 3b398d23661c..2526fe9d9c54 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 @@ -1258,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/sbt/ExtractAPI.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala index 5488d1979649..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 @@ -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/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/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 937490ab060a..cb10173dd554 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)))) } @@ -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) @@ -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/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/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/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/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/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/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/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/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 5e56a9bbea40..ba8937db84c4 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 } @@ -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 } } } @@ -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 } @@ -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/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 72ce89a33942..53163e997994 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. @@ -133,7 +142,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) } @@ -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)) { @@ -249,7 +260,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 } @@ -414,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) || @@ -443,7 +454,15 @@ object Checking { case tp: ClassInfo => tp.derivedClassInfo( prefix = apply(tp.prefix), - classParents = + 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 @@ -486,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) => @@ -564,17 +583,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 10cfcf87cf15..575bbc8d970e 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) @@ -431,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 _ => @@ -935,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/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 diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 7dd741666cdb..51b6b5b85a64 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 */ @@ -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 @@ -915,14 +915,14 @@ 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) 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/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index 32acd28fb32e..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` */ @@ -474,6 +475,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/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 97d50b07e34e..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 @@ -94,16 +95,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") + for (parent <- cinfo.classParentsNEW) + checkSelfConforms(parent.typeSymbol.asClass, "illegal inheritance", "parent") + for (reqd <- cinfo.cls.givenSelfType.classSymbols) + checkSelfConforms(reqd, "missing requirement", "required") case _ => } @@ -251,7 +252,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) && { @@ -285,7 +286,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 3a330b3f041d..68619e03c017 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._ @@ -102,13 +102,15 @@ 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 @ 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) - 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) @@ -293,7 +295,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 => @@ -302,9 +304,9 @@ 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.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) @@ -513,17 +515,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 } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 76bc9360f6aa..51d2656c9867 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 { @@ -525,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)) @@ -1414,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)) @@ -1423,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 @@ -1457,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 = { @@ -2029,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/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 03f11335ead1..2e14b83bceb1 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)) @@ -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 } @@ -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(".")) }