diff options
author | Martin Odersky <odersky@gmail.com> | 2015-09-21 12:46:35 +0200 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2015-09-21 12:50:05 +0200 |
commit | 71e3133ef65b06a5bce605cd4f0ebf879cc05118 (patch) | |
tree | 1faffde9a7ef4e882ef8ba5c8dcd59719a4aa6a0 /src | |
parent | 154f3511d52c6b748c03d97dd035f0ad79f9a355 (diff) | |
download | dotty-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.scala | 5 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/TypeApplications.scala | 29 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Types.scala | 20 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Checking.scala | 14 |
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 |