aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/core/TypeApplications.scala
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2013-11-25 11:31:46 +0100
committerMartin Odersky <odersky@gmail.com>2013-11-25 11:34:04 +0100
commit45ea511ec8ef12e0a192e4f4925923e75ef9ae3a (patch)
treef498f664b8a2b7133c86bc37c481aafccfef99b3 /src/dotty/tools/dotc/core/TypeApplications.scala
parent92ed954f361ed08ef4821afd530357379956904a (diff)
downloaddotty-45ea511ec8ef12e0a192e4f4925923e75ef9ae3a.tar.gz
dotty-45ea511ec8ef12e0a192e4f4925923e75ef9ae3a.tar.bz2
dotty-45ea511ec8ef12e0a192e4f4925923e75ef9ae3a.zip
Types refactorings
- moving out type applicaton related operations to a decorator: TypeApplications - converting some set operations to list operations to make them replayable. - moving unused operations to Types.overflow
Diffstat (limited to 'src/dotty/tools/dotc/core/TypeApplications.scala')
-rw-r--r--src/dotty/tools/dotc/core/TypeApplications.scala302
1 files changed, 302 insertions, 0 deletions
diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala
new file mode 100644
index 000000000..624a86b21
--- /dev/null
+++ b/src/dotty/tools/dotc/core/TypeApplications.scala
@@ -0,0 +1,302 @@
+package dotty.tools.dotc
+package core
+
+import Types._
+import Contexts._
+import Symbols._
+import Decorators._
+import util.Stats._
+import Names._
+import Flags._
+import util.Positions.Position
+import collection.mutable
+
+/** A decorator that provides methods for modeling type application */
+class TypeApplications(val self: Type) extends AnyVal {
+
+ /** 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.
+ * For an intersection type A & B, the type parameters of its left operand, A.
+ * Empty list for all other types.
+ */
+ final def typeParams(implicit ctx: Context): List[TypeSymbol] = track("typeParams") {
+ self match {
+ case tp: ClassInfo =>
+ tp.cls.typeParams
+ case tp: TypeRef =>
+ val tsym = tp.typeSymbol
+ if (tsym.isClass) tsym.typeParams
+ else if (tsym.isAliasType) tp.underlying.typeParams
+ 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 =>
+ tp.underlying.typeParams
+ case tp: AndType =>
+ tp.tp1.typeParams
+ case _ =>
+ Nil
+ }
+ }
+ /** The type parameters of the underlying class.
+ * This is like `typeParams`, except for 4 differences.
+ * First, it does not adjust type parameters in refined types. I.e. type arguments
+ * do not remove corresponding type parameters.
+ * Second, it will return Nil instead of forcing a symbol, in order to rule
+ * out CyclicReference exceptions.
+ * Third, it will return Nil for BoundTypes because we might get a NullPointer exception
+ * on PolyParam#underlying otherwise (demonstrated by showClass test).
+ * Fourth, it won't return higher-kinded type parameters.
+ */
+ final def safeUnderlyingTypeParams(implicit ctx: Context): List[TypeSymbol] = {
+ def ifCompleted(sym: Symbol): Symbol = if (sym.isCompleted) sym else NoSymbol
+ self match {
+ case tp: ClassInfo =>
+ if (tp.cls.isCompleted) tp.cls.typeParams else Nil
+ case tp: TypeRef =>
+ val tsym = tp.typeSymbol
+ if (tsym.isClass && tsym.isCompleted) tsym.typeParams
+ else if (tsym.isAliasType) tp.underlying.safeUnderlyingTypeParams
+ else Nil
+ case tp: BoundType =>
+ Nil
+ case tp: TypeProxy =>
+ tp.underlying.safeUnderlyingTypeParams
+ case tp: AndType =>
+ tp.tp1.safeUnderlyingTypeParams
+ case _ =>
+ Nil
+ }
+ }
+
+ def uninstantiatedTypeParams(implicit ctx: Context): List[TypeSymbol] =
+ typeParams filter (tparam => self.member(tparam.name) == tparam)
+
+ /** Encode the type resulting from applying this type to given arguments */
+ final def appliedTo(args: List[Type])(implicit ctx: Context): Type = track("appliedTo") {
+
+ def recur(tp: Type, tparams: List[TypeSymbol], args: List[Type]): Type = args match {
+ case arg :: args1 =>
+ if (tparams.isEmpty) {
+ println(s"applied type mismatch: $self $args, typeParams = $typeParams, tsym = ${self.typeSymbol.debugString}") // !!! DEBUG
+ println(s"precomplete decls = ${self.typeSymbol.decls.toList.map(_.denot).mkString("\n ")}")
+ }
+ val tparam = tparams.head
+ val tp1 = RefinedType(tp, tparam.name, arg.toBounds(tparam))
+ recur(tp1, tparams.tail, args1)
+ case nil => tp
+ }
+
+ def safeTypeParams(tsym: Symbol) =
+ if (tsym.isClass || !self.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
+ }
+
+ if (args.isEmpty) self
+ else self match {
+ case tp: TypeRef =>
+ val tsym = tp.symbol
+ if (tsym.isAliasType) tp.underlying.appliedTo(args)
+ else recur(tp, safeTypeParams(tsym), args)
+ case tp: TypeProxy =>
+ tp.underlying.appliedTo(args)
+ case AndType(l, r) =>
+ l.appliedTo(args) & r
+ case ErrorType =>
+ self
+ }
+ }
+
+ final def appliedTo(arg: Type)(implicit ctx: Context): Type = appliedTo(arg :: Nil)
+ final def appliedTo(arg1: Type, arg2: Type)(implicit ctx: Context): Type = appliedTo(arg1 :: arg2 :: Nil)
+
+ /** Turn this type, which is used as an argument for
+ * type parameter `tparam`, into a TypeBounds RHS
+ */
+ final def toBounds(tparam: Symbol)(implicit ctx: Context): TypeBounds = {
+ val v = tparam.variance
+ if (v > 0 && !(tparam is LocalOrExpanded)) TypeBounds.upper(self)
+ else if (v < 0 && !(tparam is LocalOrExpanded)) TypeBounds.lower(self)
+ else TypeAlias(self, v)
+ }
+
+ /** The type arguments of the base type instance wrt `base` of this type */
+ final def baseTypeArgs(base: Symbol)(implicit ctx: Context): List[Type] =
+ if (self derivesFrom base)
+ base.typeParams map (param => self.member(param.name).info.argType(param))
+ else
+ Nil
+
+ /** The first type argument of the base type instance wrt `base` of this type */
+ final def firstBaseTypeArg(base: Symbol)(implicit ctx: Context): Type = base.typeParams match {
+ case param :: _ if self derivesFrom base =>
+ self.member(param.name).info.argType(param)
+ case _ =>
+ NoType
+ }
+
+ /** Translate a type of the form From[T] to To[T], keep other types as they are.
+ * `from` and `to` must be static classes, both with one type parameter, and the same variance.
+ */
+ def translateParameterized(from: ClassSymbol, to: ClassSymbol)(implicit ctx: Context): Type =
+ if (self derivesFrom from)
+ RefinedType(to.typeRef, to.typeParams.head.name, self.member(from.typeParams.head.name).info)
+ else self
+
+ /** If this is an encoding of a (partially) applied type, return its arguments,
+ * otherwise return Nil
+ */
+ final def typeArgs(implicit ctx: Context): List[Type] = {
+ var tparams: List[TypeSymbol] = null
+ def recur(tp: Type, refineCount: Int): mutable.ListBuffer[Type] = tp.stripTypeVar match {
+ case tp @ RefinedType(tycon, name) =>
+ val buf = recur(tycon, refineCount + 1)
+ if (buf == null) null
+ else {
+ if (tparams == null) tparams = tycon.typeParams
+ if (buf.size < tparams.length) {
+ val tparam = tparams(buf.size)
+ if (name == tparam.name) buf += tp.refinedInfo.argType(tparam)
+ else null
+ } else null
+ }
+ case _ =>
+ if (refineCount == 0) null
+ else new mutable.ListBuffer[Type]
+ }
+ val buf = recur(self, 0)
+ if (buf == null) Nil else buf.toList
+ }
+
+ /** The core type without any type arguments.
+ * @param `typeArgs` must be the type arguments of this type.
+ */
+ final def withoutArgs(typeArgs: List[Type]): Type = typeArgs match {
+ case _ :: typeArgs1 =>
+ val RefinedType(tycon, _) = self
+ tycon.withoutArgs(typeArgs1)
+ case nil =>
+ self
+ }
+
+ /** If this is the image of a type argument to type parameter `tparam`,
+ * recover the type argument, otherwise NoType.
+ */
+ final def argType(tparam: Symbol)(implicit ctx: Context): Type = self match {
+ case TypeBounds(lo, hi) =>
+ if (lo eq hi) hi
+ else {
+ val v = tparam.variance
+ if (v > 0 && (lo isRef defn.NothingClass)) hi
+ else if (v < 0 && (hi isRef defn.AnyClass)) lo
+ else NoType
+ }
+ case _ =>
+ NoType
+ }
+
+ /** The element type of a sequence or array */
+ def elemType(implicit ctx: Context): Type =
+ firstBaseTypeArg(defn.SeqClass) orElse firstBaseTypeArg(defn.ArrayClass)
+
+ /** If this type is of the normalized form Array[...[Array[T]...]
+ * return the number of Array wrappers and T.
+ * Otherwise return 0 and the type itself
+ */
+ final def splitArray(implicit ctx: Context): (Int, Type) = {
+ def recur(n: Int, tp: Type): (Int, Type) = tp.stripTypeVar match {
+ case RefinedType(tycon, _) if tycon isRef defn.ArrayClass =>
+ tp.typeArgs match {
+ case arg :: Nil => recur(n + 1, arg)
+ case _ => (n, tp)
+ }
+ case _ =>
+ (n, tp)
+ }
+ recur(0, self)
+ }
+
+ /** Given a type alias
+ *
+ * type T[boundSyms] = p.C[targs]
+ *
+ * produce its equivalent right hand side RHS that makes no reference to the bound
+ * symbols on the left hand side. I.e. the type alias can be replaced by
+ *
+ * type T = RHS
+ *
+ * It is required that `C` is a class and that every bound symbol in `boundSyms` appears
+ * as an argument in `targs`. If these requirements are not met an error is
+ * signalled by calling the parameter `error`.
+ *
+ * The rewriting replaces bound symbols by references to the
+ * parameters of class C. Example:
+ *
+ * Say we have:
+ *
+ * class Triple[type T1, type T2, type T3]
+ * type A[X] = Triple[(X, X), X, String]
+ *
+ * Then this is rewritable, as `X` appears as second type argument to `Triple`.
+ * Occurrences of `X` are rewritten to `this.T2` and the whole definition becomes:
+ *
+ * type A = Triple { type T1 = (this.T2, this.T2); type T3 = String }
+ *
+ * If the RHS is an intersection type A & B, we Lambda abstract on A instead and
+ * then recombine with & B.
+ */
+ def LambdaAbstract(boundSyms: List[Symbol])(error: (String, Position) => Unit)(implicit ctx: Context): Type = self match {
+ case AndType(l, r) =>
+ AndType(l.LambdaAbstract(boundSyms)(error), r)
+ case _ =>
+ val cls = self.typeSymbol
+ if (!cls.isClass)
+ error("right-hand side of parameterized alias type must refer to a class", cls.pos)
+
+ val correspondingParamName: Map[Symbol, TypeName] = {
+ for {
+ (tparam, targ: TypeRef) <- cls.typeParams zip typeArgs
+ if boundSyms contains targ.symbol
+ } yield targ.symbol -> tparam.name
+ }.toMap
+
+ val correspondingNames = correspondingParamName.values.toSet
+
+ def replacements(rt: RefinedType): List[Type] =
+ for (sym <- boundSyms) yield {
+ correspondingParamName get sym match {
+ case Some(name) =>
+ TypeRef(RefinedThis(rt), name)
+ case None =>
+ error(s"parameter $sym of type alias does not appear as type argument of the aliased $cls", sym.pos)
+ defn.AnyType
+ }
+ }
+
+ def rewrite(tp: Type): Type = tp match {
+ case tp @ RefinedType(parent, name: TypeName) =>
+ if (correspondingNames contains name) rewrite(parent)
+ else RefinedType(
+ rewrite(parent),
+ name,
+ rt => tp.refinedInfo.subst(boundSyms, replacements(rt)))
+ case tp =>
+ tp
+ }
+
+ rewrite(self)
+ }
+} \ No newline at end of file