aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/core
diff options
context:
space:
mode:
authorodersky <odersky@gmail.com>2015-12-15 17:50:36 +0100
committerodersky <odersky@gmail.com>2015-12-15 17:50:36 +0100
commitc864e118e7bd04dcbd0503ab9af6729fda94ff3f (patch)
treea5ae9122b65ce2948b5375665e3bddb5633ac349 /src/dotty/tools/dotc/core
parent6fc069f55d9d29a781b9ca2e021f8b3273327c56 (diff)
parent7e8f40164a48aa45d40b690929f969a0345b5503 (diff)
downloaddotty-c864e118e7bd04dcbd0503ab9af6729fda94ff3f.tar.gz
dotty-c864e118e7bd04dcbd0503ab9af6729fda94ff3f.tar.bz2
dotty-c864e118e7bd04dcbd0503ab9af6729fda94ff3f.zip
Merge pull request #1000 from dotty-staging/fix-#241
Fix #241
Diffstat (limited to 'src/dotty/tools/dotc/core')
-rw-r--r--src/dotty/tools/dotc/core/ConstraintHandling.scala10
-rw-r--r--src/dotty/tools/dotc/core/Definitions.scala16
-rw-r--r--src/dotty/tools/dotc/core/Denotations.scala2
-rw-r--r--src/dotty/tools/dotc/core/Flags.scala15
-rw-r--r--src/dotty/tools/dotc/core/NameOps.scala16
-rw-r--r--src/dotty/tools/dotc/core/StdNames.scala20
-rw-r--r--src/dotty/tools/dotc/core/SymDenotations.scala3
-rw-r--r--src/dotty/tools/dotc/core/Symbols.scala2
-rw-r--r--src/dotty/tools/dotc/core/TypeApplications.scala753
-rw-r--r--src/dotty/tools/dotc/core/TypeComparer.scala201
-rw-r--r--src/dotty/tools/dotc/core/TypeOps.scala44
-rw-r--r--src/dotty/tools/dotc/core/Types.scala147
-rw-r--r--src/dotty/tools/dotc/core/tasty/TreePickler.scala30
-rw-r--r--src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala53
14 files changed, 724 insertions, 588 deletions
diff --git a/src/dotty/tools/dotc/core/ConstraintHandling.scala b/src/dotty/tools/dotc/core/ConstraintHandling.scala
index 577b958c8..ff7afe99a 100644
--- a/src/dotty/tools/dotc/core/ConstraintHandling.scala
+++ b/src/dotty/tools/dotc/core/ConstraintHandling.scala
@@ -232,6 +232,16 @@ trait ConstraintHandling {
}
}
+ /** Instantiate `param` to `tp` if the constraint stays satisfiable */
+ protected def tryInstantiate(param: PolyParam, tp: Type): Boolean = {
+ val saved = constraint
+ constraint =
+ if (addConstraint(param, tp, fromBelow = true) &&
+ addConstraint(param, tp, fromBelow = false)) constraint.replace(param, tp)
+ else saved
+ constraint ne saved
+ }
+
/** Check that constraint is fully propagated. See comment in Config.checkConstraintsPropagated */
def checkPropagated(msg: => String)(result: Boolean): Boolean = {
if (Config.checkConstraintsPropagated && result && addConstraintInvocations == 0) {
diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala
index 4556dd9d5..5f794f2d5 100644
--- a/src/dotty/tools/dotc/core/Definitions.scala
+++ b/src/dotty/tools/dotc/core/Definitions.scala
@@ -588,6 +588,12 @@ class Definitions {
name.startsWith(prefix) && name.drop(prefix.length).forall(_.isDigit)
}
+ def isBottomClass(cls: Symbol) = cls == NothingClass || cls == NullClass
+ def isBottomType(tp: Type) = tp match {
+ case tp: TypeRef => isBottomClass(tp.symbol)
+ case _ => false
+ }
+
def isFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.Function)
def isAbstractFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.AbstractFunction)
def isTupleClass(cls: Symbol) = isVarArityClass(cls, tpnme.Tuple)
@@ -642,9 +648,9 @@ class Definitions {
* to be the type parameters of a higher-kided type). This is a class symbol that
* would be generated by the following schema.
*
- * class LambdaXYZ extends Object with P1 with ... with Pn {
- * type v_1 $hk$Arg0; ...; type v_N $hk$ArgN;
- * type Apply
+ * trait LambdaXYZ extends Object with P1 with ... with Pn {
+ * type v_1 hk$0; ...; type v_N hk$N;
+ * type +$Apply
* }
*
* Here:
@@ -669,7 +675,7 @@ class Definitions {
val cls = denot.asClass.classSymbol
val paramDecls = newScope
for (i <- 0 until vcs.length)
- newTypeParam(cls, tpnme.LambdaArgName(i), varianceFlags(vcs(i)), paramDecls)
+ newTypeParam(cls, tpnme.hkArg(i), varianceFlags(vcs(i)), paramDecls)
newTypeField(cls, tpnme.hkApply, Covariant, paramDecls)
val parentTraitRefs =
for (i <- 0 until vcs.length if vcs(i) != 0)
@@ -679,7 +685,7 @@ class Definitions {
}
}
- val traitName = tpnme.LambdaTraitName(vcs)
+ val traitName = tpnme.hkLambda(vcs)
def createTrait = {
val cls = newClassSymbol(
diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala
index b6519e1cd..fcd60ef16 100644
--- a/src/dotty/tools/dotc/core/Denotations.scala
+++ b/src/dotty/tools/dotc/core/Denotations.scala
@@ -324,7 +324,7 @@ object Denotations {
info1 // follow Scala2 linearization -
// compare with way merge is performed in SymDenotation#computeMembersNamed
else
- throw new MergeError(s"${ex.getMessage} as members of type ${pre.show}")
+ throw new MergeError(s"${ex.getMessage} as members of type ${pre.show}", ex.tp1, ex.tp2)
}
new JointRefDenotation(sym, jointInfo, denot1.validFor & denot2.validFor)
}
diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala
index c8a9dc13b..42d06c2ab 100644
--- a/src/dotty/tools/dotc/core/Flags.scala
+++ b/src/dotty/tools/dotc/core/Flags.scala
@@ -306,10 +306,12 @@ object Flags {
/** A case parameter accessor */
final val CaseAccessor = termFlag(25, "<caseaccessor>")
- /** An type parameter which is an alias for some other (non-visible) type parameter */
- final val TypeArgument = typeFlag(25, "<type-param-inst>")
+ /** A binding for a type parameter of a base class or trait.
+ * TODO: Replace with combination of isType, ExpandedName, and Override?
+ */
+ final val BaseTypeArg = typeFlag(25, "<basetypearg>")
- final val CaseAccessorOrTypeArgument = CaseAccessor.toCommonFlags
+ final val CaseAccessorOrBaseTypeArg = CaseAccessor.toCommonFlags
/** A super accessor */
final val SuperAccessor = termFlag(26, "<superaccessor>")
@@ -446,7 +448,7 @@ object Flags {
final val FromStartFlags =
AccessFlags | Module | Package | Deferred | Final | MethodOrHKCommon | Param | ParamAccessor | Scala2ExistentialCommon |
InSuperCall | Touched | JavaStatic | CovariantOrOuter | ContravariantOrLabel | ExpandedName | AccessorOrSealed |
- CaseAccessorOrTypeArgument | Fresh | Frozen | Erroneous | ImplicitCommon | Permanent |
+ CaseAccessorOrBaseTypeArg | Fresh | Frozen | Erroneous | ImplicitCommon | Permanent |
LazyOrTrait | SuperAccessorOrScala2x | SelfNameOrImplClass
assert(FromStartFlags.isTermFlags && FromStartFlags.isTypeFlags)
@@ -545,7 +547,7 @@ object Flags {
final val TypeParamOrAccessor = TypeParam | TypeParamAccessor
/** If symbol of a type alias has these flags, prefer the alias */
- final val AliasPreferred = TypeParam | TypeArgument | ExpandedName
+ final val AliasPreferred = TypeParam | BaseTypeArg | ExpandedName
/** A covariant type parameter instance */
final val LocalCovariant = allOf(Local, Covariant)
@@ -583,6 +585,9 @@ object Flags {
/** A private[this] parameter accessor */
final val PrivateLocalParamAccessor = allOf(Private, Local, ParamAccessor)
+ /** A parameter forwarder */
+ final val ParamForwarder = allOf(Method, Stable, ParamAccessor)
+
/** A private[this] parameter */
final val PrivateLocalParam = allOf(Private, Local, Param)
diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala
index 1a2646347..6c1930c9f 100644
--- a/src/dotty/tools/dotc/core/NameOps.scala
+++ b/src/dotty/tools/dotc/core/NameOps.scala
@@ -81,6 +81,7 @@ object NameOps {
def isScala2LocalSuffix = name.endsWith(" ")
def isModuleVarName(name: Name): Boolean =
name.stripAnonNumberSuffix endsWith MODULE_VAR_SUFFIX
+ def isSelectorName = name.startsWith(" ") && name.tail.forall(_.isDigit)
/** Is name a variable name? */
def isVariableName: Boolean = name.length > 0 && {
@@ -99,19 +100,22 @@ object NameOps {
}
/** Is this the name of a higher-kinded type parameter of a Lambda? */
- def isLambdaArgName =
+ def isHkArgName =
name.length > 0 &&
- name.head == tpnme.LAMBDA_ARG_PREFIXhead &&
- name.startsWith(tpnme.LAMBDA_ARG_PREFIX) && {
- val digits = name.drop(tpnme.LAMBDA_ARG_PREFIX.length)
+ name.head == tpnme.hkArgPrefixHead &&
+ name.startsWith(tpnme.hkArgPrefix) && {
+ val digits = name.drop(tpnme.hkArgPrefixLength)
digits.length <= 4 && digits.forall(_.isDigit)
}
/** The index of the higher-kinded type parameter with this name.
* Pre: isLambdaArgName.
*/
- def LambdaArgIndex: Int =
- name.drop(tpnme.LAMBDA_ARG_PREFIX.length).toString.toInt
+ def hkArgIndex: Int =
+ name.drop(tpnme.hkArgPrefixLength).toString.toInt
+
+ def isLambdaTraitName(implicit ctx: Context): Boolean =
+ name.startsWith(tpnme.hkLambdaPrefix)
/** If the name ends with $nn where nn are
* all digits, strip the $ and the digits.
diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala
index e8cddd3d4..e2add1a52 100644
--- a/src/dotty/tools/dotc/core/StdNames.scala
+++ b/src/dotty/tools/dotc/core/StdNames.scala
@@ -173,8 +173,6 @@ object StdNames {
final val WILDCARD_STAR: N = "_*"
final val REIFY_TREECREATOR_PREFIX: N = "$treecreator"
final val REIFY_TYPECREATOR_PREFIX: N = "$typecreator"
- final val LAMBDA_ARG_PREFIX: N = "hk$"
- final val LAMBDA_ARG_PREFIXhead: Char = LAMBDA_ARG_PREFIX.head
final val AbstractFunction: N = "AbstractFunction"
final val Any: N = "Any"
@@ -314,7 +312,6 @@ object StdNames {
val AnnotatedType: N = "AnnotatedType"
val AppliedTypeTree: N = "AppliedTypeTree"
- val hkApply: N = "$apply"
val ArrayAnnotArg: N = "ArrayAnnotArg"
val Constant: N = "Constant"
val ConstantType: N = "ConstantType"
@@ -322,7 +319,6 @@ object StdNames {
val Flag : N = "Flag"
val Ident: N = "Ident"
val Import: N = "Import"
- val LambdaPrefix: N = "Lambda$"
val Literal: N = "Literal"
val LiteralAnnotArg: N = "LiteralAnnotArg"
val Modifiers: N = "Modifiers"
@@ -530,9 +526,14 @@ object StdNames {
val nothingRuntimeClass: N = "scala.runtime.Nothing$"
val nullRuntimeClass: N = "scala.runtime.Null$"
-
val synthSwitch: N = "$synthSwitch"
+ val hkApply: N = "$Apply"
+ val hkArgPrefix: N = "$hk"
+ val hkLambdaPrefix: N = "Lambda$"
+ val hkArgPrefixHead: Char = hkArgPrefix.head
+ val hkArgPrefixLength: Int = hkArgPrefix.length
+
// unencoded operators
object raw {
final val AMP : N = "&"
@@ -742,14 +743,13 @@ object StdNames {
def syntheticTypeParamNames(num: Int): List[TypeName] =
(0 until num).map(syntheticTypeParamName)(breakOut)
- def LambdaTraitName(vcs: List[Int]): TypeName = LambdaPrefix ++ vcs.map(varianceSuffix).mkString
- def LambdaArgName(n: Int) = LAMBDA_ARG_PREFIX ++ n.toString
-
- final val Conforms = encode("<:<")
+ def hkLambda(vcs: List[Int]): TypeName = hkLambdaPrefix ++ vcs.map(varianceSuffix).mkString
+ def hkArg(n: Int): TypeName = hkArgPrefix ++ n.toString
def varianceSuffix(v: Int): Char = varianceSuffixes.charAt(v + 1)
-
val varianceSuffixes = "NIP"
+
+ final val Conforms = encode("<:<")
}
abstract class JavaNames[N <: Name] extends DefinedNames[N] {
diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala
index d7fa183c9..e351557a3 100644
--- a/src/dotty/tools/dotc/core/SymDenotations.scala
+++ b/src/dotty/tools/dotc/core/SymDenotations.scala
@@ -167,7 +167,6 @@ object SymDenotations {
completions.println(i"${" " * indent}completing ${if (isType) "type" else "val"} $name")
indent += 1
}
- indent += 1
if (myFlags is Touched) throw CyclicReference(this)
myFlags |= Touched
@@ -446,7 +445,7 @@ object SymDenotations {
/** is this symbol a trait representing a type lambda? */
final def isLambdaTrait(implicit ctx: Context): Boolean =
- isClass && name.startsWith(tpnme.LambdaPrefix) && owner == defn.ScalaPackageClass
+ isClass && name.startsWith(tpnme.hkLambdaPrefix) && owner == defn.ScalaPackageClass
/** Is this symbol a package object or its module class? */
def isPackageObject(implicit ctx: Context): Boolean = {
diff --git a/src/dotty/tools/dotc/core/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala
index e33f9651e..2a76f18d8 100644
--- a/src/dotty/tools/dotc/core/Symbols.scala
+++ b/src/dotty/tools/dotc/core/Symbols.scala
@@ -368,7 +368,7 @@ object Symbols {
type ThisName <: Name
- //assert(_id != 30214)
+ //assert(id != 4285)
/** The last denotation of this symbol */
private[this] var lastDenot: SymDenotation = _
diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala
index 661975dab..fbab4ee39 100644
--- a/src/dotty/tools/dotc/core/TypeApplications.scala
+++ b/src/dotty/tools/dotc/core/TypeApplications.scala
@@ -36,6 +36,145 @@ object TypeApplications {
case tp: TypeBounds => tp.hi
case _ => tp
}
+
+ /** Does the variance of `sym1` conform to the variance of `sym2`?
+ * This is the case if the variances are the same or `sym` is nonvariant.
+ */
+ def varianceConforms(sym1: TypeSymbol, sym2: TypeSymbol)(implicit ctx: Context) =
+ sym1.variance == sym2.variance || sym2.variance == 0
+
+ def variancesConform(syms1: List[TypeSymbol], syms2: List[TypeSymbol])(implicit ctx: Context) =
+ syms1.corresponds(syms2)(varianceConforms)
+
+ /** Extractor for
+ *
+ * [v1 X1: B1, ..., vn Xn: Bn] -> T
+ * ==>
+ * Lambda$_v1...vn { type $hk_i: B_i, type $Apply = [X_i := this.$Arg_i] T }
+ */
+ object TypeLambda {
+ def apply(variances: List[Int],
+ argBoundsFns: List[RefinedType => TypeBounds],
+ bodyFn: RefinedType => Type)(implicit ctx: Context): Type = {
+ def argRefinements(parent: Type, i: Int, bs: List[RefinedType => TypeBounds]): Type = bs match {
+ case b :: bs1 =>
+ argRefinements(RefinedType(parent, tpnme.hkArg(i), b), i + 1, bs1)
+ case nil =>
+ parent
+ }
+ assert(variances.nonEmpty)
+ assert(argBoundsFns.length == variances.length)
+ RefinedType(
+ argRefinements(defn.LambdaTrait(variances).typeRef, 0, argBoundsFns),
+ tpnme.hkApply, bodyFn(_).bounds.withVariance(1))
+ }
+
+ def unapply(tp: Type)(implicit ctx: Context): Option[(List[Int], List[TypeBounds], Type)] = tp match {
+ case app @ RefinedType(parent, tpnme.hkApply) =>
+ val cls = parent.typeSymbol
+ val variances = cls.typeParams.map(_.variance)
+ def collectBounds(t: Type, acc: List[TypeBounds]): List[TypeBounds] = t match {
+ case t @ RefinedType(p, rname) =>
+ assert(rname.isHkArgName)
+ collectBounds(p, t.refinedInfo.bounds :: acc)
+ case TypeRef(_, lname) =>
+ assert(lname.isLambdaTraitName)
+ acc
+ }
+ val argBounds = collectBounds(parent, Nil)
+ Some((variances, argBounds, app.refinedInfo.argInfo))
+ case _ =>
+ None
+ }
+ }
+
+ /** Extractor for
+ *
+ * [v1 X1: B1, ..., vn Xn: Bn] -> C[X1, ..., Xn]
+ *
+ * where v1, ..., vn and B1, ..., Bn are the variances and bounds of the type parameters
+ * of the class C.
+ *
+ * @param tycon C
+ */
+ object EtaExpansion {
+ def apply(tycon: TypeRef)(implicit ctx: Context) = {
+ assert(tycon.isEtaExpandable)
+ tycon.EtaExpand(tycon.typeParams)
+ }
+
+ def unapply(tp: Type)(implicit ctx: Context): Option[TypeRef] = {
+ def argsAreForwarders(args: List[Type], n: Int): Boolean = args match {
+ case Nil =>
+ n == 0
+ case TypeRef(RefinedThis(rt), sel) :: args1 =>
+ rt.eq(tp) && sel == tpnme.hkArg(n - 1) && argsAreForwarders(args1, n - 1)
+ case _ =>
+ false
+ }
+ tp match {
+ case TypeLambda(_, argBounds, AppliedType(fn: TypeRef, args))
+ if argsAreForwarders(args, tp.typeParams.length) => Some(fn)
+ case _ => None
+ }
+ }
+ }
+
+ /** Extractor for type application T[U_1, ..., U_n]. This is the refined type
+ *
+ * T { type p_1 v_1= U_1; ...; type p_n v_n= U_n }
+ *
+ * where v_i, p_i are the variances and names of the type parameters of T,
+ * If `T`'s class symbol is a lambda trait, follow the refined type with a
+ * projection
+ *
+ * T { ... } # $Apply
+ */
+ object AppliedType {
+ 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 {
+ case TypeRef(prefix, tpnme.hkApply) => unapp(prefix)
+ case _ =>
+ unapp(tp) match {
+ case Some((tycon: TypeRef, _)) if tycon.symbol.isLambdaTrait =>
+ // We are seeing part of a lambda abstraction, not an applied type
+ None
+ case x => x
+ }
+ }
+
+ private def unapp(tp: Type)(implicit ctx: Context): Option[(Type, List[Type])] = tp match {
+ case _: RefinedType =>
+ val tparams = tp.classSymbol.typeParams
+ if (tparams.isEmpty) None
+ else {
+ val argBuf = new mutable.ListBuffer[Type]
+ def stripArgs(tp: Type, n: Int): Type =
+ if (n == 0) tp
+ else tp match {
+ case tp @ RefinedType(parent, pname) if pname == tparams(n - 1).name =>
+ val res = stripArgs(parent, n - 1)
+ if (res.exists) argBuf += tp.refinedInfo.argInfo
+ res
+ case _ =>
+ NoType
+ }
+ val res = stripArgs(tp, tparams.length)
+ if (res.exists) Some((res, argBuf.toList)) else None
+ }
+ case _ => None
+ }
+ }
+
+ /** Adapt all arguments to possible higher-kinded type parameters using etaExpandIfHK
+ */
+ def etaExpandIfHK(tparams: List[Symbol], args: List[Type])(implicit ctx: Context): List[Type] =
+ if (tparams.isEmpty) args
+ else args.zipWithConserve(tparams)((arg, tparam) => arg.etaExpandIfHK(tparam.infoOrCompleter))
+
+ def argRefs(rt: RefinedType, n: Int)(implicit ctx: Context) =
+ List.range(0, n).map(i => RefinedThis(rt).select(tpnme.hkArg(i)))
}
import TypeApplications._
@@ -61,27 +200,27 @@ class TypeApplications(val self: Type) extends AnyVal {
case self: ClassInfo =>
self.cls.typeParams
case self: TypeRef =>
- val tsym = self.typeSymbol
+ val tsym = self.symbol
if (tsym.isClass) tsym.typeParams
else if (tsym.isAliasType) self.underlying.typeParams
- else {
- val lam = LambdaClass(forcing = false)
- if (lam.exists) lam.typeParams else Nil
- }
+ else if (tsym.isCompleting)
+ // We are facing a problem when computing the type parameters of an uncompleted
+ // abstract type. We can't access the bounds of the symbol yet because that
+ // would cause a cause a cyclic reference. So we return `Nil` instead
+ // and try to make up for it later. The acrobatics in Scala2Unpicker#readType
+ // for reading a TypeRef show what's neeed.
+ Nil
+ else tsym.info.typeParams
case self: RefinedType =>
- def isBoundedLambda(tp: Type): Boolean = tp match {
- case tp: TypeRef => tp.typeSymbol.isLambdaTrait
- case tp: RefinedType => tp.refinedName.isLambdaArgName && isBoundedLambda(tp.parent)
- case _ => false
- }
- val tparams = self.parent.typeParams
- self.refinedInfo match {
- case rinfo: TypeBounds if rinfo.isAlias || isBoundedLambda(self) =>
- tparams.filterNot(_.name == self.refinedName)
- case _ =>
- tparams
+ // inlined and optimized version of
+ // val sym = self.LambdaTrait
+ // if (sym.exists) return sym.typeParams
+ if (self.refinedName == tpnme.hkApply) {
+ val sym = self.parent.classSymbol
+ if (sym.isLambdaTrait) return sym.typeParams
}
- case self: SingletonType =>
+ self.parent.typeParams.filterNot(_.name == self.refinedName)
+ case self: SingletonType =>
Nil
case self: TypeProxy =>
self.underlying.typeParams
@@ -90,76 +229,24 @@ class TypeApplications(val self: Type) extends AnyVal {
}
}
- /** The type parameters of the underlying class.
- * This is like `typeParams`, except for 3 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 for BoundTypes because we might get a NullPointer exception
- * on PolyParam#underlying otherwise (demonstrated by showClass test).
- * Third, it won't return abstract higher-kinded type parameters, i.e. the type parameters of
- * an abstract type are always empty.
- */
- final def rawTypeParams(implicit ctx: Context): List[TypeSymbol] = {
- self match {
- case self: ClassInfo =>
- self.cls.typeParams
- case self: TypeRef =>
- val tsym = self.typeSymbol
- if (tsym.isClass) tsym.typeParams
- else if (tsym.isAliasType) self.underlying.rawTypeParams
- else Nil
- case _: BoundType | _: SingletonType =>
- Nil
- case self: TypeProxy =>
- self.underlying.rawTypeParams
- case _ =>
- Nil
- }
+ /** The Lambda trait underlying a type lambda */
+ def LambdaTrait(implicit ctx: Context): Symbol = self.stripTypeVar match {
+ case RefinedType(parent, tpnme.hkApply) =>
+ val sym = self.classSymbol
+ if (sym.isLambdaTrait) sym else NoSymbol
+ case TypeBounds(lo, hi) => hi.LambdaTrait
+ case _ => NoSymbol
}
- /** If type `self` is equal, aliased-to, or upperbounded-by a type of the form
- * `LambdaXYZ { ... }`, the class symbol of that type, otherwise NoSymbol.
- * symbol of that type, otherwise NoSymbol.
- * @param forcing if set, might force completion. If not, never forces
- * but returns NoSymbol when it would have to otherwise.
- */
- def LambdaClass(forcing: Boolean)(implicit ctx: Context): Symbol = track("LambdaClass") { self.stripTypeVar match {
- case self: TypeRef =>
- val sym = self.symbol
- if (sym.isLambdaTrait) sym
- else if (sym.isClass || sym.isCompleting && !forcing) NoSymbol
- else self.info.LambdaClass(forcing)
- case self: TypeProxy =>
- self.underlying.LambdaClass(forcing)
- case _ =>
- NoSymbol
- }}
-
- /** Is type `self` equal, aliased-to, or upperbounded-by a type of the form
- * `LambdaXYZ { ... }`?
- */
- def isLambda(implicit ctx: Context): Boolean =
- LambdaClass(forcing = true).exists
-
- /** Same is `isLambda`, except that symbol denotations are not forced
- * Symbols in completion count as not lambdas.
- */
- def isSafeLambda(implicit ctx: Context): Boolean =
- LambdaClass(forcing = false).exists
-
- /** Is type `self` a Lambda with all hk$i fields fully instantiated? */
- def isInstantiatedLambda(implicit ctx: Context): Boolean =
- isSafeLambda && typeParams.isEmpty
-
/** Is receiver type higher-kinded (i.e. of kind != "*")? */
def isHK(implicit ctx: Context): Boolean = self.dealias match {
case self: TypeRef => self.info.isHK
- case RefinedType(_, name) => name == tpnme.hkApply || name.isLambdaArgName
+ case RefinedType(_, name) => name == tpnme.hkApply
case TypeBounds(_, hi) => hi.isHK
case _ => false
}
- /** is receiver of the form T#$apply? */
+ /** is receiver of the form T#$Apply? */
def isHKApply: Boolean = self match {
case TypeRef(_, name) => name == tpnme.hkApply
case _ => false
@@ -173,101 +260,237 @@ class TypeApplications(val self: Type) extends AnyVal {
*
* but without forcing anything.
*/
- def noHK(implicit ctx: Context): Boolean = self.stripTypeVar match {
+ def classNotLambda(implicit ctx: Context): Boolean = self.stripTypeVar match {
case self: RefinedType =>
- self.parent.noHK
+ self.parent.classNotLambda
case self: TypeRef =>
- (self.denot.exists) && {
+ self.denot.exists && {
val sym = self.symbol
if (sym.isClass) !sym.isLambdaTrait
- else sym.isCompleted && self.info.isAlias && self.info.bounds.hi.noHK
+ else sym.isCompleted && self.info.isAlias && self.info.bounds.hi.classNotLambda
}
case _ =>
false
}
- /** Encode the type resulting from applying this type to given arguments */
- final def appliedTo(args: List[Type])(implicit ctx: Context): Type = /*>|>*/ track("appliedTo") /*<|<*/ {
- def matchParams(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.unforcedDecls.toList.map(_.denot).mkString("\n ")}")
- }
- val tparam = tparams.head
- val tp1 = RefinedType(tp, tparam.name, arg.toBounds(tparam))
- matchParams(tp1, tparams.tail, args1)
- case nil => tp
- }
+ /** Lambda abstract `self` with given type parameters. Examples:
+ *
+ * type T[X] = U becomes type T = [X] -> U
+ * type T[X] >: L <: U becomes type T >: L <: ([X] -> _ <: U)
+ */
+ def LambdaAbstract(tparams: List[Symbol])(implicit ctx: Context): Type = {
- /** Instantiate type `tp` with `args`.
- * @param original The original type for which we compute the type parameters
- * This makes a difference for refinement types, because
- * refinements bind type parameters and thereby remove them
- * from `typeParams`.
+ /** Replace references to type parameters with references to hk arguments `this.$hk_i`
+ * Care is needed not to cause cycles, hence `SafeSubstMap`.
*/
- def instantiate(tp: Type, original: Type): Type = tp match {
- case tp: TypeRef =>
- val tsym = tp.symbol
- if (tsym.isAliasType) tp.underlying.appliedTo(args)
- else {
- val safeTypeParams =
- if (tsym.isClass || !tp.typeSymbol.isCompleting) original.typeParams
- else {
- ctx.warning(i"encountered F-bounded higher-kinded type parameters for $tsym; assuming they are invariant")
- defn.LambdaTrait(args map alwaysZero).typeParams // @@@ can we force?
- }
- matchParams(tp, safeTypeParams, args)
- }
- case tp: RefinedType =>
- val redux = tp.EtaReduce
- if (redux.exists) redux.appliedTo(args) // Rewrite ([hk$0] => C[hk$0])(T) to C[T]
- else tp.derivedRefinedType(
- instantiate(tp.parent, original),
- tp.refinedName,
- tp.refinedInfo)
- case tp: TypeProxy =>
- instantiate(tp.underlying, original)
- case tp: PolyType =>
- tp.instantiate(args)
- case ErrorType =>
- tp
+ def internalize[T <: Type](tp: T) =
+ (rt: RefinedType) =>
+ new ctx.SafeSubstMap(tparams, argRefs(rt, tparams.length))
+ .apply(tp).asInstanceOf[T]
+
+ def expand(tp: Type) = {
+ TypeLambda(
+ tparams.map(_.variance),
+ tparams.map(tparam => internalize(self.memberInfo(tparam).bounds)),
+ internalize(tp))
}
+ self match {
+ case self: TypeAlias =>
+ self.derivedTypeAlias(expand(self.alias))
+ case self @ TypeBounds(lo, hi) =>
+ self.derivedTypeBounds(lo, expand(TypeBounds.upper(hi)))
+ case _ => expand(self)
+ }
+ }
- /** Same as isHK, except we classify all abstract types as HK,
- * (they must be, because they are applied). This avoids some forcing and
- * CyclicReference errors of the standard isHK.
- */
- def isKnownHK(tp: Type): Boolean = tp match {
- case tp: TypeRef =>
- val sym = tp.symbol
- if (sym.isClass) sym.isLambdaTrait
- else !sym.isAliasType || isKnownHK(tp.info)
- case tp: TypeProxy => isKnownHK(tp.underlying)
- case _ => false
+ /** A type ref is eta expandable if it refers to a non-lambda class.
+ * In that case we can look for parameterized base types of the type
+ * to eta expand them.
+ */
+ def isEtaExpandable(implicit ctx: Context) = self match {
+ case self: TypeRef => self.symbol.isClass && !self.name.isLambdaTraitName
+ case _ => false
+ }
+
+ /** Convert a type constructor `TC` which has type parameters `T1, ..., Tn`
+ * in a context where type parameters `U1,...,Un` are expected to
+ *
+ * LambdaXYZ { Apply = TC[hk$0, ..., hk$n] }
+ *
+ * Here, XYZ corresponds to the variances of
+ * - `U1,...,Un` if the variances of `T1,...,Tn` are pairwise compatible with `U1,...,Un`,
+ * - `T1,...,Tn` otherwise.
+ * v1 is compatible with v2, if v1 = v2 or v2 is non-variant.
+ */
+ def EtaExpand(tparams: List[TypeSymbol])(implicit ctx: Context): Type = {
+ val tparamsToUse = if (variancesConform(typeParams, tparams)) tparams else typeParams
+ self.appliedTo(tparams map (_.typeRef)).LambdaAbstract(tparamsToUse)
+ //.ensuring(res => res.EtaReduce =:= self, s"res = $res, core = ${res.EtaReduce}, self = $self, hc = ${res.hashCode}")
+ }
+
+ /** Eta expand the prefix in front of any refinements.
+ * @param tparamsForBottom Type parameters to use if core is a bottom type
+ */
+ def EtaExpandCore(tparamsForBottom: List[TypeSymbol])(implicit ctx: Context): Type = self.stripTypeVar match {
+ case self: RefinedType =>
+ self.derivedRefinedType(self.parent.EtaExpandCore(tparamsForBottom), self.refinedName, self.refinedInfo)
+ case tp: TypeRef if defn.isBottomClass(tp.symbol) =>
+ self.LambdaAbstract(tparamsForBottom)
+ case _ =>
+ self.EtaExpand(self.typeParams)
+ }
+
+ /** Eta expand if `self` is a (non-lambda) class reference and `bound` is a higher-kinded type */
+ def etaExpandIfHK(bound: Type)(implicit ctx: Context): Type = {
+ val boundLambda = bound.LambdaTrait
+ val hkParams = boundLambda.typeParams
+ if (hkParams.isEmpty) self
+ else self match {
+ case self: TypeRef if self.symbol.isClass && self.typeParams.length == hkParams.length =>
+ EtaExpansion(self)
+ case _ => self
}
+ }
- if (args.isEmpty || ctx.erasedTypes) self
+ /** If argument A and type parameter P are higher-kinded, adapt the variances
+ * of A to those of P, ensuring that the variances of the type lambda A
+ * agree with the variances of corresponding higher-kinded type parameters of P. Example:
+ *
+ * class GenericCompanion[+CC[X]]
+ * GenericCompanion[List]
+ *
+ * with adaptHkVariances, the argument `List` will expand to
+ *
+ * [X] => List[X]
+ *
+ * instead of
+ *
+ * [+X] => List[X]
+ *
+ * even though `List` is covariant. This adaptation is necessary to ignore conflicting
+ * variances in overriding members that have types of hk-type parameters such as
+ * `GenericCompanion[GenTraversable]` or `GenericCompanion[ListBuffer]`.
+ * When checking overriding, we need to validate the subtype relationship
+ *
+ * GenericCompanion[[X] -> ListBuffer[X]] <: GenericCompanion[[+X] -> GenTraversable[X]]
+ *
+ * Without adaptation, this would be false, and hence an overriding error would
+ * result. But with adaptation, the rhs argument will be adapted to
+ *
+ * [X] -> GenTraversable[X]
+ *
+ * which makes the subtype test succeed. The crucial point here is that, since
+ * GenericCompanion only expects a non-variant CC, the fact that GenTraversable
+ * is covariant is irrelevant, so can be ignored.
+ */
+ def adaptHkVariances(bound: Type)(implicit ctx: Context): Type = {
+ val boundLambda = bound.LambdaTrait
+ val hkParams = boundLambda.typeParams
+ if (hkParams.isEmpty) self
else {
- val res = instantiate(self, self)
- if (isKnownHK(res)) TypeRef(res, tpnme.hkApply) else res
+ def adaptArg(arg: Type): Type = arg match {
+ case arg: TypeRef if arg.symbol.isLambdaTrait &&
+ !arg.symbol.typeParams.corresponds(hkParams)(_.variance == _.variance) &&
+ arg.symbol.typeParams.corresponds(hkParams)(varianceConforms) =>
+ arg.prefix.select(boundLambda)
+ case arg: RefinedType =>
+ arg.derivedRefinedType(adaptArg(arg.parent), arg.refinedName, arg.refinedInfo)
+ case arg @ TypeAlias(alias) =>
+ arg.derivedTypeAlias(adaptArg(alias))
+ case arg @ TypeBounds(lo, hi) =>
+ arg.derivedTypeBounds(lo, adaptArg(hi))
+ case _ =>
+ arg
+ }
+ adaptArg(self)
}
}
- /** Simplify a fully instantiated type of the form `LambdaX{... type Apply = T } # Apply` to `T`.
+ /** Encode
+ *
+ * T[U1, ..., Un]
+ *
+ * where
+ * @param self = `T`
+ * @param args = `U1,...,Un`
+ * performing the following simplifications
+ *
+ * 1. If `T` is an eta expansion `[X1,..,Xn] -> C[X1,...,Xn]` of class `C` compute
+ * `C[U1, ..., Un]` instead.
+ * 2. If `T` is some other type lambda `[X1,...,Xn] -> S` none of the arguments
+ * `U1,...,Un` is a wildcard, compute `[X1:=U1, ..., Xn:=Un]S` instead.
+ * 3. If `T` is a polytype, instantiate it to `U1,...,Un`.
*/
- def simplifyApply(implicit ctx: Context): Type = self match {
- case self @ TypeRef(prefix, tpnme.hkApply) if prefix.isInstantiatedLambda =>
- prefix.member(tpnme.hkApply).info match {
- case TypeAlias(alias) => alias
- case _ => self
+ final def appliedTo(args: List[Type])(implicit ctx: Context): Type = /*>|>*/ track("appliedTo") /*<|<*/ {
+ def substHkArgs = new TypeMap {
+ def apply(tp: Type): Type = tp match {
+ case TypeRef(RefinedThis(rt), name) if rt.eq(self) && name.isHkArgName =>
+ args(name.hkArgIndex)
+ case _ =>
+ mapOver(tp)
}
- case _ => self
+ }
+ if (args.isEmpty || ctx.erasedTypes) self
+ else self.stripTypeVar match {
+ case EtaExpansion(self1) =>
+ self1.appliedTo(args)
+ case TypeLambda(_, _, body) if !args.exists(_.isInstanceOf[TypeBounds]) =>
+ substHkArgs(body)
+ case self: PolyType =>
+ self.instantiate(args)
+ case _ =>
+ appliedTo(args, typeParams)
+ }
+ }
+
+ /** Encode application `T[U1, ..., Un]` without simplifications, where
+ * @param self = `T`
+ * @param args = `U1, ..., Un`
+ * @param tparams are assumed to be the type parameters of `T`.
+ */
+ final def appliedTo(args: List[Type], typParams: List[TypeSymbol])(implicit ctx: Context): Type = {
+ def matchParams(t: Type, tparams: List[TypeSymbol], args: List[Type])(implicit ctx: Context): Type = args match {
+ case arg :: args1 =>
+ try {
+ val tparam :: tparams1 = tparams
+ matchParams(RefinedType(t, tparam.name, arg.toBounds(tparam)), tparams1, args1)
+ } catch {
+ case ex: MatchError =>
+ println(s"applied type mismatch: $self $args, typeParams = $typParams") // !!! DEBUG
+ //println(s"precomplete decls = ${self.typeSymbol.unforcedDecls.toList.map(_.denot).mkString("\n ")}")
+ throw ex
+ }
+ case nil => t
+ }
+ assert(args.nonEmpty)
+ matchParams(self, typParams, args) match {
+ case refined @ RefinedType(_, pname) if pname.isHkArgName =>
+ TypeRef(refined, tpnme.hkApply)
+ case refined =>
+ refined
+ }
}
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)
+ /** A cycle-safe version of `appliedTo` where computing type parameters do not force
+ * the typeconstructor. Instead, if the type constructor is completing, we make
+ * up hk type parameters matching the arguments. This is needed when unpickling
+ * Scala2 files such as `scala.collection.generic.Mapfactory`.
+ */
+ final def safeAppliedTo(args: List[Type])(implicit ctx: Context) = {
+ val safeTypeParams = self match {
+ case self: TypeRef if !self.symbol.isClass && self.symbol.isCompleting =>
+ // This happens when unpickling e.g. scala$collection$generic$GenMapFactory$$CC
+ ctx.warning(i"encountered F-bounded higher-kinded type parameters for ${self.symbol}; assuming they are invariant")
+ defn.LambdaTrait(args map alwaysZero).typeParams
+ case _ =>
+ typeParams
+ }
+ appliedTo(args, safeTypeParams)
+ }
+
/** Turn this type, which is used as an argument for
* type parameter `tparam`, into a TypeBounds RHS
*/
@@ -281,7 +504,7 @@ class TypeApplications(val self: Type) extends AnyVal {
else TypeAlias(self, v)
}
- /** The type arguments of this type's base type instance wrt.`base`.
+ /** The type arguments of this type's base type instance wrt. `base`.
* Existential types in arguments are returned as TypeBounds instances.
*/
final def baseArgInfos(base: Symbol)(implicit ctx: Context): List[Type] =
@@ -370,26 +593,9 @@ class TypeApplications(val self: Type) extends AnyVal {
* otherwise return Nil.
* Existential types in arguments are returned as TypeBounds instances.
*/
- final def argInfos(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.argInfo
- 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
+ final def argInfos(implicit ctx: Context): List[Type] = self match {
+ case AppliedType(tycon, args) => args
+ case _ => Nil
}
/** Argument types where existential types in arguments are disallowed */
@@ -412,6 +618,11 @@ class TypeApplications(val self: Type) extends AnyVal {
self
}
+ final def typeConstructor(implicit ctx: Context): Type = self.stripTypeVar match {
+ case AppliedType(tycon, _) => tycon
+ case self => self
+ }
+
/** If this is the image of a type argument; recover the type argument,
* otherwise NoType.
*/
@@ -421,6 +632,12 @@ class TypeApplications(val self: Type) extends AnyVal {
case _ => NoType
}
+ /** If this is a type alias, its underlying type, otherwise the type itself */
+ def dropAlias(implicit ctx: Context): Type = self match {
+ case TypeAlias(alias) => alias
+ case _ => self
+ }
+
/** The element type of a sequence or array */
def elemType(implicit ctx: Context): Type = self match {
case defn.ArrayOf(elemtp) => elemtp
@@ -428,6 +645,9 @@ class TypeApplications(val self: Type) extends AnyVal {
case _ => firstBaseArgInfo(defn.SeqClass)
}
+ /** Does this type contain RefinedThis type with `target` as its underling
+ * refinement type?
+ */
def containsRefinedThis(target: Type)(implicit ctx: Context): Boolean = {
def recur(tp: Type): Boolean = tp.stripTypeVar match {
case RefinedThis(tp) =>
@@ -451,201 +671,4 @@ class TypeApplications(val self: Type) extends AnyVal {
}
recur(self)
}
-
- /** The typed lambda abstraction of this type `T` relative to `boundSyms`.
- * This is:
- *
- * LambdaXYZ{ bounds }{ type Apply = toHK(T) }
- *
- * where
- * - XYZ reflects the variances of the bound symbols,
- * - `bounds` consists of type declarations `type hk$i >: toHK(L) <: toHK(U),
- * one for each type parameter in `T` with non-trivial bounds L,U.
- * - `toHK` is a substitution that replaces every bound symbol sym_i by
- * `this.hk$i`.
- *
- * TypeBounds are lambda abstracting by lambda abstracting their upper bound.
- *
- * @param cycleParanoid If `true` don't force denotation of a TypeRef unless
- * its name matches one of `boundSyms`. Needed to avoid cycles
- * involving F-boundes hk-types when reading Scala2 collection classes
- * with new hk-scheme.
- */
- def LambdaAbstract(boundSyms: List[Symbol], cycleParanoid: Boolean = false)(implicit ctx: Context): Type = {
- def expand(tp: Type): Type = {
- val lambda = defn.LambdaTrait(boundSyms.map(_.variance))
- def toHK(tp: Type) = (rt: RefinedType) => {
- val argRefs = boundSyms.indices.toList.map(i =>
- RefinedThis(rt).select(tpnme.LambdaArgName(i)))
- val substituted =
- if (cycleParanoid) new ctx.SafeSubstMap(boundSyms, argRefs).apply(tp)
- else tp.subst(boundSyms, argRefs)
- substituted.bounds.withVariance(1)
- }
- val boundNames = new mutable.ListBuffer[Name]
- val boundss = new mutable.ListBuffer[TypeBounds]
- for (sym <- boundSyms) {
- val bounds = sym.info.bounds
- if (!(TypeBounds.empty frozen_<:< bounds)) {
- boundNames += sym.name
- boundss += bounds
- }
- }
- val lambdaWithBounds =
- RefinedType.make(lambda.typeRef, boundNames.toList, boundss.toList.map(toHK))
- RefinedType(lambdaWithBounds, tpnme.hkApply, toHK(tp))
- }
- self match {
- case self @ TypeBounds(lo, hi) =>
- self.derivedTypeBounds(lo, expand(TypeBounds.upper(hi)))
- case _ =>
- expand(self)
- }
- }
-
- /** Convert a type constructor `TC` which has type parameters `T1, ..., Tn`
- * in a context where type parameters `U1,...,Un` are expected to
- *
- * LambdaXYZ { Apply = TC[hk$0, ..., hk$n] }
- *
- * Here, XYZ corresponds to the variances of
- * - `U1,...,Un` if the variances of `T1,...,Tn` are pairwise compatible with `U1,...,Un`,
- * - `T1,...,Tn` otherwise.
- * v1 is compatible with v2, if v1 = v2 or v2 is non-variant.
- */
- def EtaExpand(tparams: List[Symbol])(implicit ctx: Context): Type = {
- def varianceCompatible(actual: Symbol, formal: Symbol) =
- formal.variance == 0 || actual.variance == formal.variance
- val tparamsToUse =
- if (typeParams.corresponds(tparams)(varianceCompatible)) tparams else typeParams
- self.appliedTo(tparams map (_.typeRef)).LambdaAbstract(tparamsToUse)
- //.ensuring(res => res.EtaReduce =:= self, s"res = $res, core = ${res.EtaReduce}, self = $self, hc = ${res.hashCode}")
- }
-
- /** Eta expand if `bound` is a higher-kinded type */
- def EtaExpandIfHK(bound: Type)(implicit ctx: Context): Type =
- if (bound.isHK && !isHK && self.typeSymbol.isClass && typeParams.nonEmpty) EtaExpand(bound.typeParams)
- else self
-
- /** Eta expand the prefix in front of any refinements. */
- def EtaExpandCore(implicit ctx: Context): Type = self.stripTypeVar match {
- case self: RefinedType =>
- self.derivedRefinedType(self.parent.EtaExpandCore, self.refinedName, self.refinedInfo)
- case _ =>
- self.EtaExpand(self.typeParams)
- }
-
- /** If `self` is a (potentially partially instantiated) eta expansion of type T, return T,
- * otherwise NoType. More precisely if `self` is of the form
- *
- * T { type $apply = U[T1, ..., Tn] }
- *
- * where
- *
- * - hk$0, ..., hk${m-1} are the type parameters of T
- * - a sublist of the arguments Ti_k (k = 0,...,m_1) are of the form T{...}.this.hk$i_k
- *
- * rewrite `self` to
- *
- * U[T'1,...T'j]
- *
- * where
- *
- * T'j = _ >: Lj <: Uj if j is in the i_k list defined above
- * where Lj and Uj are the bounds of hk$j mapped using `fromHK`.
- * = fromHK(Tj) otherwise.
- *
- * `fromHK` is the function that replaces every occurrence of `<self>.this.hk$i` by the
- * corresponding parameter reference in `U[T'1,...T'j]`
- */
- def EtaReduce(implicit ctx: Context): Type = {
- def etaCore(tp: Type, tparams: List[Symbol]): Type = tparams match {
- case Nil => tp
- case tparam :: otherParams =>
- tp match {
- case tp: RefinedType =>
- tp.refinedInfo match {
- case TypeAlias(TypeRef(RefinedThis(rt), rname))
- if (rname == tparam.name) && (rt eq self) =>
- // we have a binding T = Lambda$XYZ{...}.this.hk$i where hk$i names the current `tparam`.
- val pcore = etaCore(tp.parent, otherParams)
- val hkBounds = self.member(rname).info.bounds
- if (TypeBounds.empty frozen_<:< hkBounds) pcore
- else tp.derivedRefinedType(pcore, tp.refinedName, hkBounds)
- case _ =>
- val pcore = etaCore(tp.parent, tparams)
- if (pcore.exists) tp.derivedRefinedType(pcore, tp.refinedName, tp.refinedInfo)
- else NoType
- }
- case _ =>
- NoType
- }
- }
- // Map references `Lambda$XYZ{...}.this.hk$i to corresponding parameter references of the reduced core.
- def fromHK(reduced: Type) = reduced match {
- case reduced: RefinedType =>
- new TypeMap {
- def apply(tp: Type): Type = tp match {
- case TypeRef(RefinedThis(binder), name) if binder eq self =>
- assert(name.isLambdaArgName)
- RefinedThis(reduced).select(reduced.typeParams.apply(name.LambdaArgIndex))
- case _ =>
- mapOver(tp)
- }
- }.apply(reduced)
- case _ =>
- reduced
- }
-
- self match {
- case self @ RefinedType(parent, tpnme.hkApply) =>
- val lc = parent.LambdaClass(forcing = false)
- self.refinedInfo match {
- case TypeAlias(alias) if lc.exists =>
- fromHK(etaCore(alias, lc.typeParams.reverse))
- case _ => NoType
- }
- case _ => NoType
- }
- }
-
- /** Test whether this type has a base type of the form `B[T1, ..., Tn]` where
- * the type parameters of `B` match one-by-one the variances of `tparams`,
- * and where the lambda abstracted type
- *
- * LambdaXYZ { type Apply = B[hk$0, ..., hk${n-1}] }
- * { type hk$0 = T1; ...; type hk${n-1} = Tn }
- *
- * satisfies predicate `p`. Try base types in the order of their occurrence in `baseClasses`.
- * A type parameter matches a variance V if it has V as its variance or if V == 0.
- * @param classBounds A hint to bound the search. Only types that derive from one of the
- * classes in classBounds are considered.
- */
- def testLifted(tparams: List[Symbol], p: Type => Boolean, classBounds: List[ClassSymbol])(implicit ctx: Context): Boolean = {
- def tryLift(bcs: List[ClassSymbol]): Boolean = bcs match {
- case bc :: bcs1 =>
- val tp = self.baseTypeWithArgs(bc)
- val targs = tp.argInfos
- val tycon = tp.withoutArgs(targs)
- def variancesMatch(param1: Symbol, param2: Symbol) =
- param2.variance == param2.variance || param2.variance == 0
- if (classBounds.exists(tycon.derivesFrom(_)) &&
- tycon.typeParams.corresponds(tparams)(variancesMatch)) {
- val expanded = tycon.EtaExpand(tparams)
- val lifted = (expanded /: targs) { (partialInst, targ) =>
- val tparam = partialInst.typeParams.head
- RefinedType(partialInst, tparam.name, targ.bounds.withVariance(tparam.variance))
- }
- ctx.traceIndented(i"eta lifting $self --> $lifted", hk) {
- p(lifted) || tryLift(bcs1)
- }
- }
- else tryLift(bcs1)
- case nil =>
- false
- }
- tparams.nonEmpty &&
- (typeParams.hasSameLengthAs(tparams) && p(EtaExpand(tparams)) ||
- classBounds.nonEmpty && tryLift(self.baseClasses))
- }
}
diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala
index 1d9928e2d..163fa4919 100644
--- a/src/dotty/tools/dotc/core/TypeComparer.scala
+++ b/src/dotty/tools/dotc/core/TypeComparer.scala
@@ -12,6 +12,7 @@ import util.{Stats, DotClass, SimpleMap}
import config.Config
import config.Printers._
import TypeErasure.{erasedLub, erasedGlb}
+import TypeApplications._
import scala.util.control.NonFatal
/** Provides methods to compare types.
@@ -143,27 +144,46 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
def compareNamed(tp1: Type, tp2: NamedType): Boolean = {
implicit val ctx: Context = this.ctx
tp2.info match {
- case info2: TypeAlias => firstTry(tp1, info2.alias)
+ case info2: TypeAlias if tp2.prefix.isStable =>
+ // If prefix is not stable (i.e. is not a path), then we have a true
+ // projection `T # A` which is treated as the existential type
+ // `ex(x: T)x.A`. We need to deal with the existential first before
+ // following the alias. If we did follow the alias we could be
+ // unsound as well as incomplete. An example of this was discovered in Iter2.scala.
+ // It failed to validate the subtype test
+ //
+ // (([+X] -> Seq[X]) & C)[SA] <: C[SA]
+ //
+ // Both sides are projections of $Apply. The left $Apply does have an
+ // aliased info, namely, Seq[SA]. But that is not a subtype of C[SA].
+ // The problem is that, with the prefix not being a path, an aliased info
+ // does not necessarily give all of the information of the original projection.
+ // So we can't follow the alias without a backup strategy. If the alias
+ // would appear on the right then I believe this can be turned into a case
+ // of unsoundness.
+ isSubType(tp1, info2.alias)
case _ => tp1 match {
case tp1: NamedType =>
tp1.info match {
- case info1: TypeAlias => compareNamed(info1.alias, tp2)
+ case info1: TypeAlias if tp1.prefix.isStable =>
+ isSubType(info1.alias, tp2)
case _ =>
val sym1 = tp1.symbol
- (if ((sym1 ne NoSymbol) && (sym1 eq tp2.symbol))
- ctx.erasedTypes ||
- sym1.isStaticOwner ||
- isSubType(tp1.prefix, tp2.prefix) ||
- thirdTryNamed(tp1, tp2)
- else
- (tp1.name eq tp2.name) &&
- isSubType(tp1.prefix, tp2.prefix) &&
- (tp1.signature == tp2.signature) &&
- !tp1.isInstanceOf[WithFixedSym] &&
- !tp2.isInstanceOf[WithFixedSym] ||
- compareHK(tp1, tp2, inOrder = true) ||
- compareHK(tp2, tp1, inOrder = false) ||
- thirdTryNamed(tp1, tp2))
+ if ((sym1 ne NoSymbol) && (sym1 eq tp2.symbol))
+ ctx.erasedTypes ||
+ sym1.isStaticOwner ||
+ isSubType(tp1.prefix, tp2.prefix) ||
+ thirdTryNamed(tp1, tp2)
+ else
+ ( (tp1.name eq tp2.name)
+ && isSubType(tp1.prefix, tp2.prefix)
+ && tp1.signature == tp2.signature
+ && !tp1.isInstanceOf[WithFixedSym]
+ && !tp2.isInstanceOf[WithFixedSym]
+ ) ||
+ compareHK(tp1, tp2, inOrder = true) ||
+ compareHK(tp2, tp1, inOrder = false) ||
+ thirdTryNamed(tp1, tp2)
}
case _ =>
compareHK(tp2, tp1, inOrder = false) ||
@@ -239,9 +259,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
case tp1: NamedType =>
tp1.info match {
case info1: TypeAlias => isSubType(info1.alias, tp2)
- case _ => compareHK(tp1, tp2, inOrder = true) || thirdTry(tp1, tp2)
- // Note: If we change the order here, doing compareHK first and following aliases second,
- // we get a -Ycheck error when compiling dotc/transform. Need to investigate.
+ case _ =>
+ compareHK(tp1, tp2, inOrder = true) ||
+ thirdTry(tp1, tp2)
}
case tp1: PolyParam =>
def flagNothingBound = {
@@ -336,21 +356,23 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
isSubType(tp1, tp2.parent) &&
(name2 == nme.WILDCARD || hasMatchingMember(name2, tp1, tp2))
}
+ def etaExpandedSubType(tp1: Type) =
+ isSubType(tp1.typeConstructor.EtaExpand(tp2.typeParams), tp2)
def compareRefined: Boolean = {
val tp1w = tp1.widen
val skipped2 = skipMatching(tp1w, tp2)
- if ((skipped2 eq tp2) || !Config.fastPathForRefinedSubtype) {
- val normalPath = tp1 match {
+ if ((skipped2 eq tp2) || !Config.fastPathForRefinedSubtype)
+ tp1 match {
case tp1: AndType =>
// Delay calling `compareRefinedSlow` because looking up a member
// of an `AndType` can lead to a cascade of subtyping checks
+ // This twist is needed to make collection/generic/ParFactory.scala compile
fourthTry(tp1, tp2) || compareRefinedSlow
case _ =>
- compareRefinedSlow || fourthTry(tp1, tp2)
+ compareRefinedSlow ||
+ fourthTry(tp1, tp2) ||
+ needsEtaLift(tp1, tp2) && testLifted(tp1, tp2, tp2.typeParams, etaExpandedSubType)
}
- normalPath ||
- needsEtaLift(tp1, tp2) && tp1.testLifted(tp2.typeParams, isSubType(_, tp2), classBounds(tp2))
- }
else // fast path, in particular for refinements resulting from parameterization.
isSubType(tp1, skipped2) &&
isSubRefinements(tp1w.asInstanceOf[RefinedType], tp2, skipped2)
@@ -412,8 +434,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
(tp2.variance > 0 && tp1.variance >= 0 || (lo2 eq NothingType) || isSubType(lo2, lo1)) &&
(tp2.variance < 0 && tp1.variance <= 0 || (hi2 eq AnyType) || isSubType(hi1, hi2))
case tp1: ClassInfo =>
- val tt = tp1.typeRef
- isSubType(lo2, tt) && isSubType(tt, hi2)
+ tp2 contains tp1
case _ =>
false
}
@@ -471,7 +492,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
isNewSubType(tp1.underlying.widenExpr, tp2) || comparePaths
case tp1: RefinedType =>
isNewSubType(tp1.parent, tp2) ||
- needsEtaLift(tp2, tp1) && tp2.testLifted(tp1.typeParams, isSubType(tp1, _), Nil)
+ needsEtaLift(tp2, tp1) &&
+ tp2.typeParams.length == tp1.typeParams.length &&
+ isSubType(tp1, tp2.EtaExpand(tp1.typeParams))
case AndType(tp11, tp12) =>
// Rewrite (T111 | T112) & T12 <: T2 to (T111 & T12) <: T2 and (T112 | T12) <: T2
// and analogously for T11 & (T121 | T122) & T12 <: T2
@@ -502,19 +525,89 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
false
}
- /** If `projection` is a hk projection T#$apply
- * and `other` is not a hk projection, then convert `other` to a hk projection `U`, and
- * continue with `T <:< U` if `inOrder` is true and `U <:< T` otherwise.
+ /** Does `tp` need to be eta lifted to be comparable to `target`?
+ * This is the case if:
+ * - target is a type lambda, and
+ * - `tp` is eta-expandable (i.e. is a non-lambda class ref)
*/
- def compareHK(projection: NamedType, other: Type, inOrder: Boolean) =
- projection.name == tpnme.hkApply &&
- !other.isHKApply &&
- other.testLifted(projection.prefix.LambdaClass(forcing = true).typeParams,
- if (inOrder) isSubType(projection.prefix, _) else isSubType(_, projection.prefix),
- if (inOrder) Nil else classBounds(projection.prefix))
+ private def needsEtaLift(tp: Type, target: RefinedType): Boolean =
+ target.refinedName == tpnme.hkApply && tp.isEtaExpandable
- /** The class symbols bounding the type of the `Apply` member of `tp` */
- private def classBounds(tp: Type) = tp.member(tpnme.hkApply).info.classSymbols
+ /** Test whether `tp1` has a base type of the form `B[T1, ..., Tn]` where
+ * - `B` derives from one of the class symbols of `tp2`,
+ * - the type parameters of `B` match one-by-one the variances of `tparams`,
+ * - `B` satisfies predicate `p`.
+ */
+ private def testLifted(tp1: Type, tp2: Type, tparams: List[TypeSymbol], p: Type => Boolean): Boolean = {
+ val classBounds = tp2.member(tpnme.hkApply).info.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))
+ case nil =>
+ false
+ }
+ recur(tp1.baseClasses)
+ }
+
+ /** If `projection` is a hk projection T#$apply with a constrainable poly param
+ * as type constructor and `other` is not a hk projection, then perform the following
+ * steps:
+ *
+ * (1) If not `inOrder` then perform the next steps until they all succeed
+ * for each base type of other which
+ * - derives from a class bound of `projection`,
+ * - has the same number of type parameters than `projection`
+ * - has type parameter variances which conform to those of `projection`.
+ * If `inOrder` then perform the same steps on the original `other` type.
+ *
+ * (2) Try to eta expand the constructor of `other`.
+ *
+ * (3a) In mode `TypevarsMissConetxt` replace the projection's hk constructor parameter
+ * by the eta expansion of step (2) reapplied to the projection's arguments.
+ * (3b) In normal mode, try to unify the projection's hk constructor parameter with
+ * the eta expansion of step(2)
+ *
+ * (4) If `inOrder`, test `projection <: other` else test `other <: projection`.
+ */
+ def compareHK(projection: NamedType, other: Type, inOrder: Boolean): Boolean = {
+ def tryInfer(tp: Type): Boolean = ctx.traceIndented(i"compareHK($projection, $other, inOrder = $inOrder, constr = $tp)", subtyping) {
+ tp match {
+ case tp: TypeVar => tryInfer(tp.underlying)
+ case param: PolyParam if canConstrain(param) =>
+
+ def unifyWith(liftedOther: Type): Boolean = {
+ subtyping.println(i"unify with $liftedOther")
+ liftedOther.typeConstructor.widen match {
+ case tycon: TypeRef if tycon.isEtaExpandable && tycon.typeParams.nonEmpty =>
+ val (ok, projection1) =
+ if (ctx.mode.is(Mode.TypevarsMissContext))
+ (true, EtaExpansion(tycon).appliedTo(projection.argInfos))
+ else
+ (tryInstantiate(param, EtaExpansion(tycon)), projection)
+ ok &&
+ (if (inOrder) isSubType(projection1, other) else isSubType(other, projection1))
+ case _ =>
+ false
+ }
+ }
+ val hkTypeParams = param.typeParams
+ subtyping.println(i"classBounds = ${projection.prefix.member(tpnme.hkApply).info.classSymbols}")
+ subtyping.println(i"base classes = ${other.baseClasses}")
+ subtyping.println(i"type params = $hkTypeParams")
+ if (inOrder) unifyWith(other)
+ else testLifted(other, projection.prefix, hkTypeParams, unifyWith)
+ case _ =>
+ false
+ }
+ }
+ projection.name == tpnme.hkApply && !other.isHKApply &&
+ tryInfer(projection.prefix.typeConstructor.dealias)
+ }
/** Returns true iff either `tp11 <:< tp21` or `tp12 <:< tp22`, trying at the same time
* to keep the constraint as wide as possible. Specifically, if
@@ -627,11 +720,19 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
* Further, no refinement refers back to the refined type via a refined this.
* The precondition is established by `skipMatching`.
*/
- private def isSubRefinements(tp1: RefinedType, tp2: RefinedType, limit: Type): Boolean =
- isSubType(tp1.refinedInfo, tp2.refinedInfo) && (
+ private def isSubRefinements(tp1: RefinedType, tp2: RefinedType, limit: Type): Boolean = {
+ def hasSubRefinement(tp1: RefinedType, refine2: Type): Boolean = {
+ isSubType(tp1.refinedInfo, refine2) || {
+ // last effort: try to adapt variances of higher-kinded types if this is sound.
+ val adapted2 = refine2.adaptHkVariances(tp1.parent.member(tp1.refinedName).symbol.info)
+ adapted2.ne(refine2) && hasSubRefinement(tp1, adapted2)
+ }
+ }
+ hasSubRefinement(tp1, tp2.refinedInfo) && (
(tp2.parent eq limit) ||
isSubRefinements(
tp1.parent.asInstanceOf[RefinedType], tp2.parent.asInstanceOf[RefinedType], limit))
+ }
/** A type has been covered previously in subtype checking if it
* is some combination of TypeRefs that point to classes, where the
@@ -665,14 +766,6 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
case _ => false
}
- /** Does `tp` need to be eta lifted to be comparable to `target`? */
- private def needsEtaLift(tp: Type, target: RefinedType): Boolean = {
- // if (tp.isLambda != tp.isHK) println(i"discrepancy for $tp, isLambda = ${tp.isLambda}, isHK = ${tp.isHK}")
- val name = target.refinedName
- (name.isLambdaArgName || (name eq tpnme.hkApply)) &&
- tp.exists && !tp.isLambda // we do encounter Lambda classes without any arguments here
- }
-
/** Narrow gadt.bounds for the type parameter referenced by `tr` to include
* `bound` as an upper or lower bound (which depends on `isUpper`).
* Test that the resulting bounds are still satisfiable.
@@ -969,13 +1062,13 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
case tp1: TypeBounds =>
tp2 match {
case tp2: TypeBounds => tp1 & tp2
- case tp2: ClassInfo if tp1 contains tp2.typeRef => tp2
+ case tp2: ClassInfo if tp1 contains tp2 => tp2
case _ => mergeConflict(tp1, tp2)
}
case tp1: ClassInfo =>
tp2 match {
case tp2: ClassInfo if tp1.cls eq tp2.cls => tp1.derivedClassInfo(tp1.prefix & tp2.prefix)
- case tp2: TypeBounds if tp2 contains tp1.typeRef => tp1
+ case tp2: TypeBounds if tp2 contains tp1 => tp1
case _ => mergeConflict(tp1, tp2)
}
case tp1 @ MethodType(names1, formals1) =>
@@ -1028,13 +1121,13 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
case tp1: TypeBounds =>
tp2 match {
case tp2: TypeBounds => tp1 | tp2
- case tp2: ClassInfo if tp1 contains tp2.typeRef => tp1
+ case tp2: ClassInfo if tp1 contains tp2 => tp1
case _ => mergeConflict(tp1, tp2)
}
case tp1: ClassInfo =>
tp2 match {
case tp2: ClassInfo if tp1.cls eq tp2.cls => tp1.derivedClassInfo(tp1.prefix | tp2.prefix)
- case tp2: TypeBounds if tp2 contains tp1.typeRef => tp2
+ case tp2: TypeBounds if tp2 contains tp1 => tp2
case _ => mergeConflict(tp1, tp2)
}
case tp1 @ MethodType(names1, formals1) =>
@@ -1074,7 +1167,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
case bounds: TypeBounds => i"type bounds $bounds"
case _ => tp.show
}
- throw new MergeError(s"cannot merge ${showType(tp1)} with ${showType(tp2)}")
+ throw new MergeError(s"cannot merge ${showType(tp1)} with ${showType(tp2)}", tp1, tp2)
}
/** Merge two lists of names. If names in corresponding positions match, keep them,
diff --git a/src/dotty/tools/dotc/core/TypeOps.scala b/src/dotty/tools/dotc/core/TypeOps.scala
index f3884e11a..3dfe698f0 100644
--- a/src/dotty/tools/dotc/core/TypeOps.scala
+++ b/src/dotty/tools/dotc/core/TypeOps.scala
@@ -61,18 +61,19 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
def toPrefix(pre: Type, cls: Symbol, thiscls: ClassSymbol): Type = /*>|>*/ ctx.conditionalTraceIndented(TypeOps.track, s"toPrefix($pre, $cls, $thiscls)") /*<|<*/ {
if ((pre eq NoType) || (pre eq NoPrefix) || (cls is PackageClass))
tp
- else if (thiscls.derivesFrom(cls) && pre.baseTypeRef(thiscls).exists) {
- if (theMap != null && theMap.currentVariance <= 0 && !isLegalPrefix(pre))
- theMap.unstable = true
- pre match {
- case SuperType(thispre, _) => thispre
- case _ => pre
- }
+ 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))
+ theMap.unstable = true
+ 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)
}
- 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)
}
/*>|>*/ ctx.conditionalTraceIndented(TypeOps.track, s"asSeen ${tp.show} from (${pre.show}, ${cls.show})", show = true) /*<|<*/ { // !!! DEBUG
@@ -384,10 +385,9 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
denot.info = info
}
}
- val typeArgFlag = if (formal is Local) TypeArgument else EmptyFlags
val sym = ctx.newSymbol(
cls, formal.name,
- formal.flagsUNSAFE & RetainedTypeArgFlags | typeArgFlag | Override,
+ formal.flagsUNSAFE & RetainedTypeArgFlags | BaseTypeArg | Override,
lazyInfo,
coord = cls.coord)
cls.enter(sym, decls)
@@ -463,14 +463,20 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
case to @ TypeBounds(lo1, hi1) if lo1 eq hi1 =>
for (pref <- prefs)
for (argSym <- pref.decls)
- if (argSym is TypeArgument)
+ if (argSym is BaseTypeArg)
forwardRef(argSym, from, to, cls, decls)
case _ =>
}
// println(s"normalizing $parents of $cls in ${cls.owner}") // !!! DEBUG
+
+ // A map consolidating all refinements arising from parent type parameters
var refinements: SimpleMap[TypeName, Type] = SimpleMap.Empty
- var formals: SimpleMap[TypeName, Symbol] = SimpleMap.Empty
+
+ // A map of all formal type parameters of base classes that get refined
+ var formals: SimpleMap[TypeName, Symbol] = SimpleMap.Empty // A map of all formal parent parameter
+
+ // Strip all refinements from parent type, populating `refinements` and `formals` maps.
def normalizeToRef(tp: Type): TypeRef = tp.dealias match {
case tp: TypeRef =>
tp
@@ -488,13 +494,17 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
throw new TypeError(s"unexpected parent type: $tp")
}
val parentRefs = parents map normalizeToRef
+
+ // Enter all refinements into current scope.
refinements foreachBinding { (name, refinedInfo) =>
assert(decls.lookup(name) == NoSymbol, // DEBUG
s"redefinition of ${decls.lookup(name).debugString} in ${cls.showLocated}")
enterArgBinding(formals(name), refinedInfo, cls, decls)
}
- // These two loops cannot be fused because second loop assumes that
- // all arguments have been entered in `decls`.
+ // Forward definitions in super classes that have one of the refined paramters
+ // as aliases directly to the refined info.
+ // Note that this cannot be fused bwith the previous loop because we now
+ // assume that all arguments have been entered in `decls`.
refinements foreachBinding { (name, refinedInfo) =>
forwardRefs(formals(name), refinedInfo, parentRefs)
}
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala
index 2558dcbb7..21b74e07b 100644
--- a/src/dotty/tools/dotc/core/Types.scala
+++ b/src/dotty/tools/dotc/core/Types.scala
@@ -100,7 +100,7 @@ object Types {
case _ => false
}
- /** Is this type a (possibly aliased and/or partially applied) type reference
+ /** Is this type a (possibly refined or applied or aliased) type reference
* to the given type symbol?
* @sym The symbol to compare to. It must be a class symbol or abstract type.
* It makes no sense for it to be an alias type because isRef would always
@@ -113,8 +113,7 @@ object Types {
case _ => this1.symbol eq sym
}
case this1: RefinedType =>
- // make sure all refinements are type arguments
- this1.parent.isRef(sym) && this.argInfos.nonEmpty
+ this1.parent.isRef(sym)
case _ =>
false
}
@@ -142,20 +141,23 @@ object Types {
}
/** Is this type an instance of a non-bottom subclass of the given class `cls`? */
- final def derivesFrom(cls: Symbol)(implicit ctx: Context): Boolean = this match {
- case tp: TypeRef =>
- val sym = tp.symbol
- if (sym.isClass) sym.derivesFrom(cls) else tp.underlying.derivesFrom(cls)
- case tp: TypeProxy =>
- tp.underlying.derivesFrom(cls)
- case tp: AndType =>
- tp.tp1.derivesFrom(cls) || tp.tp2.derivesFrom(cls)
- case tp: OrType =>
- tp.tp1.derivesFrom(cls) && tp.tp2.derivesFrom(cls)
- case tp: JavaArrayType =>
- cls == defn.ObjectClass
- case _ =>
- false
+ final def derivesFrom(cls: Symbol)(implicit ctx: Context): Boolean = {
+ def loop(tp: Type) = tp match {
+ case tp: TypeRef =>
+ val sym = tp.symbol
+ if (sym.isClass) sym.derivesFrom(cls) else tp.underlying.derivesFrom(cls)
+ case tp: TypeProxy =>
+ tp.underlying.derivesFrom(cls)
+ case tp: AndType =>
+ tp.tp1.derivesFrom(cls) || tp.tp2.derivesFrom(cls)
+ case tp: OrType =>
+ tp.tp1.derivesFrom(cls) && tp.tp2.derivesFrom(cls)
+ case tp: JavaArrayType =>
+ cls == defn.ObjectClass
+ case _ =>
+ false
+ }
+ cls == defn.AnyClass || loop(this)
}
/** Is this type guaranteed not to have `null` as a value?
@@ -448,7 +450,18 @@ object Types {
if (rinfo.isAlias) rinfo
else if (pdenot.info.isAlias) pdenot.info
else if (ctx.pendingMemberSearches.contains(name)) safeAnd(pdenot.info, rinfo)
- else pdenot.info & rinfo
+ else
+ try pdenot.info & rinfo
+ catch {
+ case ex: CyclicReference =>
+ // 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
+ // the special shortcut for Any in derivesFrom was as yet absent. To reproduce,
+ // remove the special treatment of Any in derivesFrom and compile
+ // sets.scala.
+ safeAnd(pdenot.info, rinfo)
+ }
pdenot.asSingleDenotation.derivedSingleDenotation(pdenot.symbol, jointInfo)
} else
pdenot & (new JointRefDenotation(NoSymbol, rinfo, Period.allInRun(ctx.runId)), pre)
@@ -856,7 +869,7 @@ object Types {
object instantiate extends TypeMap {
var isSafe = true
def apply(tp: Type): Type = tp match {
- case TypeRef(RefinedThis(`pre`), name) if name.isLambdaArgName =>
+ case TypeRef(RefinedThis(`pre`), name) if name.isHkArgName =>
member(name).info match {
case TypeAlias(alias) => alias
case _ => isSafe = false; tp
@@ -869,7 +882,7 @@ object Types {
}
}
def instArg(tp: Type): Type = tp match {
- case tp @ TypeAlias(TypeRef(RefinedThis(`pre`), name)) if name.isLambdaArgName =>
+ case tp @ TypeAlias(TypeRef(RefinedThis(`pre`), name)) if name.isHkArgName =>
member(name).info match {
case TypeAlias(alias) => tp.derivedTypeAlias(alias) // needed to keep variance
case bounds => bounds
@@ -884,19 +897,17 @@ object Types {
instantiate(tp)
}
/** Reduce rhs of $hkApply to make it stand alone */
- def betaReduce(tp: Type) =
- if (pre.parent.isSafeLambda) {
- val reduced = instTop(tp)
- if (instantiate.isSafe) reduced else NoType
- }
- else NoType
+ def betaReduce(tp: Type) = {
+ val reduced = instTop(tp)
+ if (instantiate.isSafe) reduced else NoType
+ }
pre.refinedInfo match {
case TypeAlias(alias) =>
if (pre.refinedName ne name) loop(pre.parent)
else if (!pre.refinementRefersToThis) alias
else alias match {
case TypeRef(RefinedThis(`pre`), aliasName) => lookupRefined(aliasName) // (1)
- case _ => if (name == tpnme.hkApply) betaReduce(alias) else NoType // (2)
+ case _ => if (name == tpnme.hkApply) betaReduce(alias) else NoType // (2) // ### use TypeApplication's betaReduce
}
case _ => loop(pre.parent)
}
@@ -1375,7 +1386,11 @@ object Types {
if (owner.isTerm) d else d.asSeenFrom(prefix)
}
- private def checkSymAssign(sym: Symbol)(implicit ctx: Context) =
+ private def checkSymAssign(sym: Symbol)(implicit ctx: Context) = {
+ def selfTypeOf(sym: Symbol) = sym.owner.info match {
+ case info: ClassInfo => info.givenSelfType
+ case _ => NoType
+ }
assert(
(lastSymbol eq sym) ||
(lastSymbol eq null) || {
@@ -1387,9 +1402,16 @@ object Types {
(lastDefRunId == NoRunId)
} ||
(lastSymbol.infoOrCompleter == ErrorType ||
- sym.owner.derivesFrom(lastSymbol.owner) && sym.owner != lastSymbol.owner
- ),
- s"data race? overwriting symbol of ${this.show} / $this / ${this.getClass} / ${lastSymbol.id} / ${sym.id} / ${sym.owner} / ${lastSymbol.owner} / ${ctx.phase} at run ${ctx.runId}")
+ sym.owner != lastSymbol.owner &&
+ (sym.owner.derivesFrom(lastSymbol.owner) ||
+ selfTypeOf(sym).derivesFrom(lastSymbol.owner) ||
+ selfTypeOf(lastSymbol).derivesFrom(sym.owner))),
+ s"""data race? overwriting symbol of type ${this.show},
+ |long form = $this of class ${this.getClass},
+ |last sym id = ${lastSymbol.id}, new sym id = ${sym.id},
+ |last owner = ${lastSymbol.owner}, new owner = ${sym.owner},
+ |period = ${ctx.phase} at run ${ctx.runId}""".stripMargin)
+ }
protected def sig: Signature = Signature.NotAMethod
@@ -1516,7 +1538,12 @@ object Types {
else {
val res = prefix.lookupRefined(name)
if (res.exists) res
- else if (name == tpnme.hkApply && prefix.noHK) derivedSelect(prefix.EtaExpandCore)
+ else if (name == tpnme.hkApply && prefix.classNotLambda) {
+ // After substitution we might end up with a type like
+ // `C { type hk$0 = T0; ...; type hk$n = Tn } # $Apply`
+ // where C is a class. In that case we eta expand `C`.
+ derivedSelect(prefix.EtaExpandCore(this.prefix.typeConstructor.typeParams))
+ }
else newLikeThis(prefix)
}
@@ -1560,6 +1587,15 @@ object Types {
case _ =>
false
}
+
+ /* A version of toString which also prints aliases. Can be used for debugging
+ override def toString =
+ if (isTerm) s"TermRef($prefix, $name)"
+ else s"TypeRef($prefix, $name)${
+ if (lastDenotation != null && lastDenotation.infoOrCompleter.isAlias)
+ s"@@@ ${lastDenotation.infoOrCompleter.asInstanceOf[TypeAlias].hi}"
+ else ""}"
+ */
}
abstract case class TermRef(override val prefix: Type, name: TermName) extends NamedType with SingletonType {
@@ -1756,8 +1792,8 @@ object Types {
object TypeRef {
def checkProjection(prefix: Type, name: TypeName)(implicit ctx: Context) =
- if (name == tpnme.hkApply && prefix.noHK)
- assert(false, s"bad type : $prefix.$name should not be $$applied")
+ if (name == tpnme.hkApply && prefix.classNotLambda)
+ assert(false, s"bad type : $prefix.$name does not allow $$Apply projection")
/** Create type ref with given prefix and name */
def apply(prefix: Type, name: TypeName)(implicit ctx: Context): TypeRef = {
@@ -1897,20 +1933,15 @@ object Types {
override def underlying(implicit ctx: Context) = parent
- private def checkInst(implicit ctx: Context): this.type = {
- if (Config.checkLambdaVariance)
- refinedInfo match {
- case refinedInfo: TypeBounds if refinedInfo.variance != 0 && refinedName.isLambdaArgName =>
- val cls = parent.LambdaClass(forcing = false)
- if (cls.exists)
- assert(refinedInfo.variance == cls.typeParams.apply(refinedName.LambdaArgIndex).variance,
- s"variance mismatch for $this, $cls, ${cls.typeParams}, ${cls.typeParams.apply(refinedName.LambdaArgIndex).variance}, ${refinedInfo.variance}")
- case _ =>
+ private def badInst =
+ throw new AssertionError(s"bad instantiation: $this")
+
+ def checkInst(implicit ctx: Context): this.type = {
+ if (refinedName == tpnme.hkApply)
+ parent.stripTypeVar match {
+ case RefinedType(_, name) if name.isHkArgName => // ok
+ case _ => badInst
}
- if (Config.checkProjections &&
- (refinedName == tpnme.hkApply || refinedName.isLambdaArgName) &&
- parent.noHK)
- assert(false, s"illegal refinement of first-order type: $this")
this
}
@@ -1932,7 +1963,7 @@ object Types {
false
}
override def computeHash = doHash(refinedName, refinedInfo, parent)
- override def toString = s"RefinedType($parent, $refinedName, $refinedInfo | $hashCode)" // !!! TODO: remove
+ override def toString = s"RefinedType($parent, $refinedName, $refinedInfo)"
}
class CachedRefinedType(parent: Type, refinedName: Name, infoFn: RefinedType => Type) extends RefinedType(parent, refinedName) {
@@ -2632,11 +2663,13 @@ object Types {
def clsDenot = if (prefix eq cls.owner.thisType) cls.denot else cls.denot.copySymDenotation(info = this)
if (typeRefCache == null)
typeRefCache =
- if ((cls is PackageClass) || cls.owner.isTerm) prefix select cls
- else prefix select (cls.name, clsDenot)
+ if ((cls is PackageClass) || cls.owner.isTerm) symbolicTypeRef
+ else TypeRef(prefix, cls.name, clsDenot)
typeRefCache
}
+ def symbolicTypeRef(implicit ctx: Context): Type = TypeRef(prefix, cls)
+
// cached because baseType needs parents
private var parentsCache: List[TypeRef] = null
@@ -2701,8 +2734,12 @@ object Types {
case _ => this
}
- def contains(tp: Type)(implicit ctx: Context) = tp match {
+ def contains(tp: Type)(implicit ctx: Context): Boolean = tp match {
case tp: TypeBounds => lo <:< tp.lo && tp.hi <:< hi
+ case tp: ClassInfo =>
+ // Note: Taking a normal typeRef does not work here. A normal ref might contain
+ // also other information about the named type (e.g. bounds).
+ contains(tp.symbolicTypeRef)
case _ => lo <:< tp && tp <:< hi
}
@@ -2746,9 +2783,9 @@ object Types {
abstract class TypeAlias(val alias: Type, override val variance: Int) extends TypeBounds(alias, alias) {
/** pre: this is a type alias */
- def derivedTypeAlias(tp: Type, variance: Int = this.variance)(implicit ctx: Context) =
- if ((lo eq tp) && (variance == this.variance)) this
- else TypeAlias(tp, variance)
+ def derivedTypeAlias(alias: Type, variance: Int = this.variance)(implicit ctx: Context) =
+ if ((alias eq this.alias) && (variance == this.variance)) this
+ else TypeAlias(alias, variance)
override def & (that: TypeBounds)(implicit ctx: Context): TypeBounds = {
val v = this commonVariance that
@@ -3287,7 +3324,7 @@ object Types {
}
}
- class MergeError(msg: String) extends TypeError(msg)
+ class MergeError(msg: String, val tp1: Type, val tp2: Type) extends TypeError(msg)
// ----- Debug ---------------------------------------------------------
diff --git a/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/src/dotty/tools/dotc/core/tasty/TreePickler.scala
index c0136538b..d11d6f4b7 100644
--- a/src/dotty/tools/dotc/core/tasty/TreePickler.scala
+++ b/src/dotty/tools/dotc/core/tasty/TreePickler.scala
@@ -9,6 +9,7 @@ import Contexts._, Symbols._, Types._, Names._, Constants._, Decorators._, Annot
import collection.mutable
import NameOps._
import TastyBuffer._
+import TypeApplications._
class TreePickler(pickler: TastyPickler) {
val buf = new TreeBuffer
@@ -142,6 +143,9 @@ class TreePickler(pickler: TastyPickler) {
}
def pickleNewType(tpe: Type, richTypes: Boolean): Unit = try { tpe match {
+ case AppliedType(tycon, args) =>
+ writeByte(APPLIEDtype)
+ withLength { pickleType(tycon); args.foreach(pickleType(_)) }
case ConstantType(value) =>
pickleConstant(value)
case tpe: TypeRef if tpe.info.isAlias && tpe.symbol.is(Flags.AliasPreferred) =>
@@ -181,15 +185,10 @@ class TreePickler(pickler: TastyPickler) {
pickleNameAndSig(tpe.name, tpe.signature); pickleType(tpe.prefix)
}
case tpe: NamedType =>
- if (tpe.name == tpnme.hkApply && tpe.prefix.argInfos.nonEmpty && tpe.prefix.isInstantiatedLambda)
- // instantiated lambdas are pickled as APPLIEDTYPE; #Apply will
- // be reconstituted when unpickling.
- pickleType(tpe.prefix)
- else if (isLocallyDefined(tpe.symbol)) {
+ if (isLocallyDefined(tpe.symbol)) {
writeByte(if (tpe.isType) TYPEREFsymbol else TERMREFsymbol)
pickleSymRef(tpe.symbol); pickleType(tpe.prefix)
- }
- else {
+ } else {
writeByte(if (tpe.isType) TYPEREF else TERMREF)
pickleName(tpe.name); pickleType(tpe.prefix)
}
@@ -211,18 +210,11 @@ class TreePickler(pickler: TastyPickler) {
case tpe: SkolemType =>
pickleType(tpe.info)
case tpe: RefinedType =>
- val args = tpe.argInfos
- if (args.isEmpty) {
- writeByte(REFINEDtype)
- withLength {
- pickleType(tpe.parent)
- pickleName(tpe.refinedName)
- pickleType(tpe.refinedInfo, richTypes = true)
- }
- }
- else {
- writeByte(APPLIEDtype)
- withLength { pickleType(tpe.withoutArgs(args)); args.foreach(pickleType(_)) }
+ writeByte(REFINEDtype)
+ withLength {
+ pickleType(tpe.parent)
+ pickleName(tpe.refinedName)
+ pickleType(tpe.refinedInfo, richTypes = true)
}
case tpe: TypeAlias =>
writeByte(TYPEALIAS)
diff --git a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala
index cf7b487bb..618e3ceea 100644
--- a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala
+++ b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala
@@ -21,6 +21,7 @@ import typer.Mode
import PickleBuffer._
import scala.reflect.internal.pickling.PickleFormat._
import Decorators._
+import TypeApplications._
import classfile.ClassfileParser
import scala.collection.{ mutable, immutable }
import scala.collection.mutable.ListBuffer
@@ -52,7 +53,7 @@ object Scala2Unpickler {
case TempPolyType(tparams, restpe) =>
if (denot.isType) {
assert(!denot.isClass)
- restpe.LambdaAbstract(tparams, cycleParanoid = true)
+ restpe.LambdaAbstract(tparams)
}
else
PolyType.fromSymbols(tparams, restpe)
@@ -142,51 +143,6 @@ object Scala2Unpickler {
denot.info = ClassInfo( // final info
denot.owner.thisType, denot.classSymbol, parentRefs, declsInRightOrder, ost)
}
-
- /** Adapt arguments to type parameters so that variance of type lambda arguments
- * agrees with variance of corresponding higherkinded type parameters. Example:
- *
- * class Companion[+CC[X]]
- * Companion[List]
- *
- * with adaptArgs, this will expand to
- *
- * Companion[[X] => List[X]]
- *
- * instead of
- *
- * Companion[[+X] => List[X]]
- *
- * even though `List` is covariant. This adaptation is necessary to ignore conflicting
- * variances in overriding members that have types of hk-type parameters such as `Companion[GenTraversable]`
- * or `Companion[ListBuffer]`. Without the adaptation we would end up with
- *
- * Companion[[+X] => GenTraversable[X]]
- * Companion[[X] => List[X]]
- *
- * and the second is not a subtype of the first. So if we have overridding memebrs of the two
- * types we get an error.
- */
- def adaptArgs(tparams: List[Symbol], args: List[Type])(implicit ctx: Context): List[Type] = tparams match {
- case tparam :: tparams1 =>
- val boundLambda = tparam.infoOrCompleter match {
- case TypeBounds(_, hi) => hi.LambdaClass(forcing = false)
- case _ => NoSymbol
- }
- def adaptArg(arg: Type): Type = arg match {
- case arg: TypeRef if arg.symbol.isLambdaTrait =>
- assert(arg.symbol.typeParams.length == boundLambda.typeParams.length)
- arg.prefix.select(boundLambda)
- case arg: RefinedType =>
- arg.derivedRefinedType(adaptArg(arg.parent), arg.refinedName, arg.refinedInfo)
- case _ =>
- arg
- }
- val arg = args.head
- val adapted = if (boundLambda.exists) adaptArg(arg) else arg
- adapted :: adaptArgs(tparams1, args.tail)
- case nil => args
- }
}
/** Unpickle symbol table information descending from a class and/or module root
@@ -759,8 +715,9 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas
else TypeRef(pre, sym.name.asTypeName)
val args = until(end, readTypeRef)
if (sym == defn.ByNameParamClass2x) ExprType(args.head)
- else if (args.isEmpty && sym.typeParams.nonEmpty) tycon.EtaExpand(sym.typeParams)
- else tycon.appliedTo(adaptArgs(sym.typeParams, args))
+ else if (args.nonEmpty) tycon.safeAppliedTo(etaExpandIfHK(sym.typeParams, args))
+ else if (sym.typeParams.nonEmpty) tycon.EtaExpand(sym.typeParams)
+ else tycon
case TYPEBOUNDStpe =>
TypeBounds(readTypeRef(), readTypeRef())
case REFINEDtpe =>