aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2015-09-21 12:46:35 +0200
committerMartin Odersky <odersky@gmail.com>2015-09-21 12:50:05 +0200
commit71e3133ef65b06a5bce605cd4f0ebf879cc05118 (patch)
tree1faffde9a7ef4e882ef8ba5c8dcd59719a4aa6a0 /src
parent154f3511d52c6b748c03d97dd035f0ad79f9a355 (diff)
downloaddotty-71e3133ef65b06a5bce605cd4f0ebf879cc05118.tar.gz
dotty-71e3133ef65b06a5bce605cd4f0ebf879cc05118.tar.bz2
dotty-71e3133ef65b06a5bce605cd4f0ebf879cc05118.zip
Eta expand $apply projected types if needed
It turns out that asSeenFrom can produce types that get projected with $apply but that are not higher-kinded. An exampple failure is in Iter3, andother in scala.collection.immutable.Map (which is now part of the test suite). We now detect that situation, and eta expand the projected type in `derivedSelect`, this will force a subssequent `lookupRefined` which will give the desired normalized type. Also added is a configurable test that checks that $apply projected tyeps are in fact higher-kinded.
Diffstat (limited to 'src')
-rw-r--r--src/dotty/tools/dotc/config/Config.scala5
-rw-r--r--src/dotty/tools/dotc/core/TypeApplications.scala29
-rw-r--r--src/dotty/tools/dotc/core/Types.scala20
-rw-r--r--src/dotty/tools/dotc/typer/Checking.scala14
4 files changed, 52 insertions, 16 deletions
diff --git a/src/dotty/tools/dotc/config/Config.scala b/src/dotty/tools/dotc/config/Config.scala
index 97893647c..d66d1ecdb 100644
--- a/src/dotty/tools/dotc/config/Config.scala
+++ b/src/dotty/tools/dotc/config/Config.scala
@@ -71,6 +71,11 @@ object Config {
/** If this flag is set, take the fast path when comparing same-named type-aliases and types */
final val fastPathForRefinedSubtype = true
+ /** If this flag is set, $apply projections are checked that they apply to a
+ * higher-kinded type.
+ */
+ final val checkProjections = false
+
/** When set, use new signature-based matching.
* Advantage of doing so: It's supposed to be faster
* Disadvantage: It might hide inconsistencies, so while debugging it's better to turn it off
diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala
index d6cb3dc15..d7d205be6 100644
--- a/src/dotty/tools/dotc/core/TypeApplications.scala
+++ b/src/dotty/tools/dotc/core/TypeApplications.scala
@@ -155,6 +155,27 @@ class TypeApplications(val self: Type) extends AnyVal {
case _ => false
}
+ /** True if it can be determined without forcing that the class symbol
+ * of this application exists and is not a lambda trait.
+ * Equivalent to
+ *
+ * self.classSymbol.exists && !self.classSymbol.isLambdaTrait
+ *
+ * but without forcing anything.
+ */
+ def noHK(implicit ctx: Context): Boolean = self.stripTypeVar match {
+ case self: RefinedType =>
+ self.parent.noHK
+ case self: TypeRef =>
+ (self.denot.exists) && {
+ val sym = self.symbol
+ if (sym.isClass) !sym.isLambdaTrait
+ else sym.isCompleted && self.info.isAlias && self.info.bounds.hi.noHK
+ }
+ 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 {
@@ -510,6 +531,14 @@ class TypeApplications(val self: Type) extends AnyVal {
if (bound.isHK && !isHK && self.typeSymbol.isClass && typeParams.nonEmpty) EtaExpand
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
+ }
+
/** If `self` is a (potentially partially instantiated) eta expansion of type T, return T,
* otherwise NoType. More precisely if `self` is of the form
*
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala
index 312d6b290..e545066af 100644
--- a/src/dotty/tools/dotc/core/Types.scala
+++ b/src/dotty/tools/dotc/core/Types.scala
@@ -1487,7 +1487,9 @@ object Types {
if (prefix eq this.prefix) this
else {
val res = prefix.lookupRefined(name)
- if (res.exists) res else newLikeThis(prefix)
+ if (res.exists) res
+ else if (name == tpnme.hkApply && prefix.noHK) derivedSelect(prefix.EtaExpandCore)
+ else newLikeThis(prefix)
}
/** Create a NamedType of the same kind as this type, but with a new prefix.
@@ -1725,9 +1727,15 @@ 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")
+
/** Create type ref with given prefix and name */
- def apply(prefix: Type, name: TypeName)(implicit ctx: Context): TypeRef =
+ def apply(prefix: Type, name: TypeName)(implicit ctx: Context): TypeRef = {
+ if (Config.checkProjections) checkProjection(prefix, name)
ctx.uniqueNamedTypes.enterIfNew(prefix, name).asInstanceOf[TypeRef]
+ }
/** Create type ref to given symbol */
def apply(prefix: Type, sym: TypeSymbol)(implicit ctx: Context): TypeRef =
@@ -1736,8 +1744,10 @@ object Types {
/** Create a non-member type ref (which cannot be reloaded using `member`),
* with given prefix, name, and symbol.
*/
- def withFixedSym(prefix: Type, name: TypeName, sym: TypeSymbol)(implicit ctx: Context): TypeRef =
+ def withFixedSym(prefix: Type, name: TypeName, sym: TypeSymbol)(implicit ctx: Context): TypeRef = {
+ if (Config.checkProjections) checkProjection(prefix, name)
unique(new TypeRefWithFixedSym(prefix, name, sym))
+ }
/** Create a type ref referring to given symbol with given name.
* This is very similar to TypeRef(Type, Symbol),
@@ -3198,7 +3208,9 @@ object Types {
class MissingType(pre: Type, name: Name)(implicit ctx: Context) extends TypeError(
i"""cannot resolve reference to type $pre.$name
- |the classfile defining the type might be missing from the classpath${otherReason(pre)}""".stripMargin)
+ |the classfile defining the type might be missing from the classpath${otherReason(pre)}""".stripMargin) {
+ printStackTrace()
+ }
private def otherReason(pre: Type)(implicit ctx: Context): String = pre match {
case pre: ThisType if pre.givenSelfType.exists =>
diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala
index 3847cb5be..8376dd4e9 100644
--- a/src/dotty/tools/dotc/typer/Checking.scala
+++ b/src/dotty/tools/dotc/typer/Checking.scala
@@ -115,18 +115,8 @@ object Checking {
val parent1 = this(parent)
val saved = cycleOK
cycleOK = nestedCycleOK
-
- /** A derived refined type with two possible tweaks:
- * (1) LazyRefs in parents are pulled out,
- * (2) #Apply is added if the type is a fully applied type lambda.
- */
- def derivedType(p: Type): Type = p match {
- case p: LazyRef => LazyRef(() => derivedType(p.ref))
- case _ =>
- val res = tp.derivedRefinedType(p, name, this(tp.refinedInfo))
- if (res.isSafeLambda && res.typeParams.isEmpty) res.select(tpnme.Apply) else res
- }
- try derivedType(parent1) finally cycleOK = saved
+ try tp.derivedRefinedType(parent1, name, this(tp.refinedInfo))
+ finally cycleOK = saved
case tp @ TypeRef(pre, name) =>
try {
// A prefix is interesting if it might contain (transitively) a reference