From 4124ff23501eb8d30f0aa873bb3ea79115812f82 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 7 Jun 2013 22:45:56 +0200 Subject: Integrated new treatment of higher-kinded types with Unpickler. Could drop quite a bit of code in the process. --- .../annotation/internal/ContravariantBetween.scala | 3 - .../annotation/internal/CovariantBetween.scala | 3 - .../annotation/internal/InvariantBetween.scala | 3 - src/dotty/tools/dotc/core/Definitions.scala | 81 ++++++++-------------- src/dotty/tools/dotc/core/TypeComparers.scala | 19 ++--- src/dotty/tools/dotc/core/Types.scala | 75 +++++++------------- src/dotty/tools/dotc/core/pickling/UnPickler.scala | 45 +++--------- 7 files changed, 69 insertions(+), 160 deletions(-) delete mode 100644 src/dotty/annotation/internal/ContravariantBetween.scala delete mode 100644 src/dotty/annotation/internal/CovariantBetween.scala delete mode 100644 src/dotty/annotation/internal/InvariantBetween.scala diff --git a/src/dotty/annotation/internal/ContravariantBetween.scala b/src/dotty/annotation/internal/ContravariantBetween.scala deleted file mode 100644 index 7eb068b89..000000000 --- a/src/dotty/annotation/internal/ContravariantBetween.scala +++ /dev/null @@ -1,3 +0,0 @@ -package dotty.annotation.internal - -trait ContravariantBetween[-Lo, +Hi] \ No newline at end of file diff --git a/src/dotty/annotation/internal/CovariantBetween.scala b/src/dotty/annotation/internal/CovariantBetween.scala deleted file mode 100644 index d65a5f1b9..000000000 --- a/src/dotty/annotation/internal/CovariantBetween.scala +++ /dev/null @@ -1,3 +0,0 @@ -package dotty.annotation.internal - -trait CovariantBetween[-Lo, +Hi] \ No newline at end of file diff --git a/src/dotty/annotation/internal/InvariantBetween.scala b/src/dotty/annotation/internal/InvariantBetween.scala deleted file mode 100644 index bf572b66c..000000000 --- a/src/dotty/annotation/internal/InvariantBetween.scala +++ /dev/null @@ -1,3 +0,0 @@ -package dotty.annotation.internal - -trait InvariantBetween[-Lo, +Hi] \ No newline at end of file diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index ae8b7887f..9d7f84449 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -289,39 +289,10 @@ class Definitions(implicit ctx: Context) { // ----- Higher kinds machinery ------------------------------------------ private var _hkTraits: Set[Symbol] = Set() - private var _hkTrait: Map[Int, ClassSymbol] = Map() - /** The set of all `HigherKinded_n` traits that are referred to in thos compilation run. */ + /** The set of HigherKindedXYZ traits encountered so far */ def hkTraits: Set[Symbol] = _hkTraits - /** A trait `HigherKinded_n[B1, ..., Bn]` that represents - * the bounds of a higher-kinded type. - */ - def hkTrait(n: Int): ClassSymbol = { - val completer = new LazyType { - def complete(denot: SymDenotation): Unit = { - val cls = denot.asClass.classSymbol - val paramDecls = newScope - for (i <- 0 until n) - newSyntheticTypeParam(cls, paramDecls, "B"+i).setFlag(Covariant) - denot.info = ClassInfo(ScalaPackageClass.thisType, cls, List(ObjectClass.typeConstructor), paramDecls) - } - } - _hkTrait get n match { - case Some(cls) => - cls - case None => - val cls = ctx.newClassSymbol( - ScalaPackageClass, - tpnme.higherKindedTraitName(n), - Trait | Interface | Synthetic, - completer).entered - _hkTraits += cls - _hkTrait = _hkTrait.updated(n, cls) - cls - } - } - private var hkTraitOfArity = mutable.Map[List[Int], ClassSymbol]() /** The HigherKinded trait corresponding to symbols `boundSyms` (which are assumed @@ -337,45 +308,47 @@ class Definitions(implicit ctx: Context) { * - v_i are the variances of the bound symbols (i.e. +, -, or empty). * - _$hk$i are hgiher-kinded parameter names, which are special treated in type application. */ - def hkTrait(boundSyms: List[Symbol]) = { + def hkTrait(variances: List[Int]) = { + + def varianceSuffix(v: Int) = v match { + case -1 => "N" + case 0 => "I" + case 1 => "P" + } + + def varianceFlags(v: Int)= v match { + case -1 => Contravariant + case 0 => Covariant + case 1 => EmptyFlags + } val completer = new LazyType { def complete(denot: SymDenotation): Unit = { val cls = denot.asClass.classSymbol val paramDecls = newScope - for ((bsym, i) <- boundSyms.zipWithIndex) - newTypeParam(cls, tpnme.higherKindedParamName(i), bsym.flags & VarianceFlags, paramDecls) + for ((v, i) <- variances.zipWithIndex) + newTypeParam(cls, tpnme.higherKindedParamName(i), varianceFlags(v), paramDecls) denot.info = ClassInfo(ScalaPackageClass.thisType, cls, List(ObjectClass.typeConstructor), paramDecls) } } - def varianceSuffix(v: Int) = v match { - case -1 => "N" - case 0 => "I" - case 1 => "P" - } - - val variances = boundSyms map (_.variance) val traitName = - tpnme.higherKindedTraitName(boundSyms.length) ++ (variances map varianceSuffix).mkString - - def createTrait = ctx.newClassSymbol( - ScalaPackageClass, - traitName, - Trait | Interface | Synthetic, - completer).entered + tpnme.higherKindedTraitName(variances.length) ++ (variances map varianceSuffix).mkString + + def createTrait = { + val cls = ctx.newClassSymbol( + ScalaPackageClass, + traitName, + Trait | Interface | Synthetic, + completer).entered + _hkTraits += cls + cls + } hkTraitOfArity.getOrElseUpdate(variances, createTrait) } - /** The bounds trait corresponding to the given variance */ - def hkBoundsClass(variance: Int) = variance match { - case 0 => InvariantBetweenClass - case 1 => CovariantBetweenClass - case -1 => ContravariantBetweenClass - } - // ----- Value class machinery ------------------------------------------ lazy val ScalaValueClasses: collection.Set[Symbol] = Set( diff --git a/src/dotty/tools/dotc/core/TypeComparers.scala b/src/dotty/tools/dotc/core/TypeComparers.scala index 7dd874325..e59954fca 100644 --- a/src/dotty/tools/dotc/core/TypeComparers.scala +++ b/src/dotty/tools/dotc/core/TypeComparers.scala @@ -201,21 +201,22 @@ object TypeComparers { } */ /** Is `tp1` a subtype of a type `tp2` of the form - * `scala.HigerKindedN[xBetween_1[Lo_1, Hi_1], ..., xBetween_n[Lo_n, Hi_n]]`? - * This is the case if `tp1` has `n` type parameters and - * for all `0 <= i < n`, the i'th type parameter's bounds are contained in - * `Lo_i..Hi_i` and its variance corresponds to the xBetween_i class. + * `scala.HigerKindedXYZ { ... }? + * This is the case if `tp1` and `tp2` have the same number + * of type parameters, the bounds of tp1's paremeters + * are contained in the corresponding bounds of tp2's parameters + * and the variances of correesponding parameters agree. */ def isSubTypeHK(tp1: Type, tp2: Type): Boolean = { val tparams = tp1.typeParams val hkArgs = tp2.typeArgs (hkArgs.length == tparams.length) && { val base = ctx.newSkolemSingleton(tp1) - (hkArgs, tparams).zipped.forall { (hkArg, tparam) => - val lo2 :: hi2 :: Nil = hkArg.typeArgs - val TypeBounds(lo1, hi1) = base.memberInfo(tparam) - isSubType(lo2, lo1) && isSubType(hi1, hi2) && - defn.hkBoundsClass(tparam.variance) == hkArg.typeSymbol + (tparams, hkArgs).zipped.forall { (tparam, hkArg) => + base.memberInfo(tparam) <:< hkArg.bounds // TODO: base.memberInfo needed? + } && + (tparams, tp2.typeSymbol.typeParams).zipped.forall { (tparam, tparam2) => + tparam.variance == tparam2.variance } } } diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 0acf084c5..71a7e555c 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -227,6 +227,9 @@ object Types { /** The type parameters of this type are: * For a ClassInfo type, the type parameters of its class. * For a typeref referring to a class, the type parameters of the class. + * For a typeref referring to an alias type, the type parameters of the aliased type. + * For a typeref referring to an abstract type with a HigherKindedXYZ bound, the + * type parameters of the HigherKinded class. * For a refinement type, the type parameters of its parent, unless there's a * refinement with the same name. Inherited by all other type proxies. * Empty list for all other types. @@ -238,7 +241,12 @@ object Types { val tsym = tp.typeSymbol if (tsym.isClass) tsym.typeParams else if (tsym.isAliasType) tp.underlying.typeParams - else Nil + else tp.info.bounds.hi match { + case AndType(hkBound, other) if defn.hkTraits contains hkBound.typeSymbol => + hkBound.typeSymbol.typeParams + case _ => + Nil + } case tp: RefinedType => tp.parent.typeParams filterNot (_.name == tp.refinedName) case tp: TypeProxy => @@ -630,59 +638,21 @@ object Types { case nil => tp } - def hkApp(tp: Type): Type = tp match { - case AndType(l, r) => - hkApp(l) orElse hkApp(r) - case tp: RefinedType if defn.hkTraits contains tp.typeSymbol => - tp - case tp: TypeProxy => - hkApp(tp.underlying) - } - - def hkRefinement(tp: TypeRef): Type = { - val hkArgs = - if (tp.symbol.isCompleting) - // This can happen if a higher-kinded type appears applied to arguments in its own bounds. - // TODO: Catch this case and mark as "proceed at own risk" later. - args map (_ => defn.InvariantBetweenClass.typeConstructor) - else { - if (tp.info == NoType) { - println(s"typeless type ref: $tp") - debugTrace = true - tp.prefix.member(tp.name) - } - - hkApp(tp.info).typeArgs - } - ((tp: Type) /: hkArgs.zipWithIndex.zip(args)) { - case (parent, ((hkArg, idx), arg)) => - val vsym = hkArg.typeSymbol - val rhs = - if (vsym == defn.InvariantBetweenClass) - TypeAlias(arg) - else if (vsym == defn.CovariantBetweenClass) - TypeBounds.upper(arg) - else { - assert(vsym == defn.ContravariantBetweenClass) - TypeBounds.lower(arg) - } - RefinedType(parent, tpnme.higherKindedParamName(idx), rhs) + def safeTypeParams(tsym: Symbol) = + if (tsym.isClass || !typeSymbol.isCompleting) typeParams + else { + ctx.warning("encountered F-bounded higher-kinded type parameters; assuming they are invariant") + defn.hkTrait(args map Function.const(0)).typeParams } - } - // begin applied type if (args.isEmpty) this else this match { case tp: PolyType => tp.instantiate(args) case tp: TypeRef => val tsym = tp.symbol - if (tsym.isClass) - recur(tp, tp.typeParams, args) - else if (tsym.isAliasType) - tp.underlying.appliedTo(args) - else - hkRefinement(tp) + if (tsym.isAliasType) tp.underlying.appliedTo(args) + else recur(tp, safeTypeParams(tsym), args) case tp: TypeProxy => tp.underlying.appliedTo(args) case AndType(l, r) => @@ -1236,14 +1206,17 @@ object Types { override def underlying(implicit ctx: Context) = parent - def derivedRefinedType(parent: Type, refinedName: Name, refinedInfo: Type)(implicit ctx: Context): RefinedType = + def derivedRefinedType(parent: Type, refinedName: Name, refinedInfo: Type)(implicit ctx: Context): RefinedType = { + def originalName = parent.typeParams.apply(refinedName.hkParamIndex).name if ((parent eq this.parent) && (refinedName eq this.refinedName) && (refinedInfo eq this.refinedInfo)) this - else if (refinedName.isHkParamName && typeParams.length > refinedName.hkParamIndex) - derivedRefinedType( - parent, parent.typeParams.apply(refinedName.hkParamIndex).name, refinedInfo) + else if (refinedName.isHkParamName && + refinedName.hkParamIndex < typeParams.length && + originalName != refinedName) + derivedRefinedType(parent, originalName, refinedInfo) else RefinedType(parent, refinedName, rt => refinedInfo.substThis(this, RefinedThis(rt))) + } override def equals(that: Any) = that match { case that: RefinedType => @@ -1641,7 +1614,7 @@ object Types { * @see Definitions.hkTrait */ def higherKinded(boundSyms: List[Symbol])(implicit ctx: Context) = { - val parent = defn.hkTrait(boundSyms).typeConstructor + val parent = defn.hkTrait(boundSyms map (_.variance)).typeConstructor val hkParamNames = boundSyms.indices.toList map tpnme.higherKindedParamName def substBoundSyms(tp: Type)(rt: RefinedType): Type = tp.subst(boundSyms, hkParamNames map (TypeRef(RefinedThis(rt), _))) diff --git a/src/dotty/tools/dotc/core/pickling/UnPickler.scala b/src/dotty/tools/dotc/core/pickling/UnPickler.scala index f96115f95..f35c24fd7 100644 --- a/src/dotty/tools/dotc/core/pickling/UnPickler.scala +++ b/src/dotty/tools/dotc/core/pickling/UnPickler.scala @@ -34,47 +34,18 @@ object UnPickler { case class TempClassInfoType(parentTypes: List[Type], decls: Scope, clazz: Symbol) extends UncachedGroundType /** Convert temp poly type to some native Dotty idiom. - * @param forSym The symbol that gets the converted type as info. - * If `forSym` is not an abstract type, this simply returns an equivalent `PolyType`. - * If `forSym` is an abstract type, it converts a + * @param denot The denotation that gets the converted type as info. + * If `denot` is not an abstract type, this simply returns an equivalent `PolyType`. + * If `denot` is an abstract type, it converts a * * TempPolyType(List(v_1 T_1, ..., v_n T_n), lo .. hi) * - * to - * - * lo .. HigherKinded_n[v_1Between[lo_1, hi_1],..., v_nBetween[lo_n, hi_n]] & hi - * - * where lo_i, hi_i are the lower/upper bounds of the type parameters T_i. - * This works only as long as none of the type parameters T_i appears in any - * of the bounds or the result type. Such occurrences of type parameters are - * replaced by Any, and a warning is issued in this case. - */ - def depoly(tp: Type, forSym: SymDenotation)(implicit ctx: Context): Type = tp match { + * to a higher-kinded type appoximation (@see TypeBounds.higherKinded) + */ + def depoly(tp: Type, denot: SymDenotation)(implicit ctx: Context): Type = tp match { case TempPolyType(tparams, restpe) => - if (forSym.isAbstractType) { - val typeArgs = for (tparam <- tparams) yield { - val TypeBounds(lo, hi) = tparam.info - defn.hkBoundsClass(tparam.variance).typeConstructor - .appliedTo(List(lo, hi)) - } - val elimTparams: Type => Type = _.subst(tparams, tparams map (_ => defn.AnyType)) - val correctedArgs = typeArgs.mapConserve(elimTparams) - val correctedRes = elimTparams(restpe) - val hkBound = defn.hkTrait(tparams.length).typeConstructor - .appliedTo(correctedArgs) - val result = correctedRes match { - case TypeBounds(lo, hi) => - val hi1 = if (hi == defn.AnyType) hkBound else AndType(hkBound, hi) - TypeBounds(lo, hi1) //note: using & instead would be too eager - } - if ((typeArgs ne correctedArgs) || (restpe ne correctedRes)) - ctx.warning(s"""failure to import F-bounded higher-kinded type - |original type definition: ${forSym.show}${tp.show} - |definition used instead : ${forSym.show}${result.show} - |proceed at own risk.""".stripMargin) - result - } else - PolyType.fromSymbols(tparams, restpe) + if (denot.isAbstractType) restpe.bounds.higherKinded(tparams) + else PolyType.fromSymbols(tparams, restpe) case tp => tp } -- cgit v1.2.3