diff options
author | Adriaan Moors <adriaan.moors@epfl.ch> | 2011-01-20 09:43:18 +0000 |
---|---|---|
committer | Adriaan Moors <adriaan.moors@epfl.ch> | 2011-01-20 09:43:18 +0000 |
commit | f0bff86d31c0ce9548e595c4b4b7fda8efe038c0 (patch) | |
tree | a01a425900de3bca3202bcfca1749099cb74a235 /src/library | |
parent | f4f1738fe79193010c328371521ed554e7e8cf6d (diff) | |
download | scala-f0bff86d31c0ce9548e595c4b4b7fda8efe038c0.tar.gz scala-f0bff86d31c0ce9548e595c4b4b7fda8efe038c0.tar.bz2 scala-f0bff86d31c0ce9548e595c4b4b7fda8efe038c0.zip |
introduce NullaryMethodType to disambiguate Pol...
introduce NullaryMethodType to disambiguate PolyType
motivation:
given `def foo[T]: (T, T)` and `type Foo[T] = (T, T)`,
`foo.info` and `TypeRef(_, Foo, Nil).normalize` are both
`PolyType(List(T), Pair[T, T])`
uncurry has been relying on an ugly hack to distinguish these
cases based on ad-hoc kind inference without this distinction,
the type alias's info (a type function) would be transformed to
`PolyType(List(T), MethodType(Nil, Pair[T, T]))`
anonymous type functions are being used more often (see #2741, #4017,
#4079, #3443, #3106), which makes a proper treatment of PolyTypes more
pressing
change to type representation:
PolyType(Nil, tp) -> NullaryMethodType(tp)
PolyType(tps, tp) -> PolyType(tps, NullaryMethodType(tp)) (if the polytype denoted a polymorphic nullary method)
PolyType(Nil, tp) is now invalid
the kind of a PolyType is * iff its resulttype is a NullaryMethodType or a MethodType (i.e., it's a polymorphic value)
in all other cases a PolyType now denotes a type constructor
NullaryMethodType is eliminated during uncurry
pickling:
for backwards compatibility, a NullaryMethodType(tp) is still pickled as a PolyType(Nil, tp),
unpickling rewrites pre-2.9-pickled PolyTypes according to the expected kind of the unpickled type (similar to what we used to do in uncurry)
a pickled PolyType(Nil, restpe) is unpickled to NullaryMethodType(restpe)
a pickled PolyType(tps, restpe) is unpickled to PolyType(tps, NullaryMethodType(restpe)) when the type is expected to have kind *
the rewrite probably isn't complete, but was validated by compiling
against the old scalacheck jar (which has plenty of polymorphic nullary
methods) nevertheless, this commit includes a new scalacheck jar
summary of the refactoring:
* PolyType(List(), tp) or PolyType(Nil, tp) or PolyType(parms, tp) if params.isEmpty ==> NullaryMethodType(tp)
* whenever there was a case PolyType(tps, tp) (irrespective of tps isEmpty), now need to consider the
case PolyType(tps, NullaryMethodType(tp)); just add a case NullaryMethodType(tp), since usually:
- there already is a PolyType case that recurses on the result type,
- the polytype case applied to empty and non-empty type parameter lists alike
* tp.resultType, where tp was assumed to be a PolyType that represents a polymorphic nullary method type
before, tp == PolyType(tps, res), now tp == PolyType(tps, NullaryMethodType(res))
* got bitten again (last time was dependent-method types refactoring)
by a TypeMap not being the identity when dropNonConstraintAnnotations
is true (despite having an identity apply method). Since asSeenFrom
is skipped when isTrivial, the annotations aren't dropped. The
cps plugin relies on asSeenFrom dropping these annotations for
trivial types though. Therefore, NullaryMethodType pretends to
never be trivial. Better fix(?) in AsSeenFromMap: `if(tp.isTrivial)
dropNonContraintAnnotations(tp) else ...`
TODO: scalap and eclipse
review by odersky, rytz
Diffstat (limited to 'src/library')
-rw-r--r-- | src/library/scala/reflect/Print.scala | 2 | ||||
-rw-r--r-- | src/library/scala/reflect/Type.scala | 3 | ||||
-rwxr-xr-x | src/library/scala/reflect/generic/Types.scala | 8 | ||||
-rwxr-xr-x | src/library/scala/reflect/generic/UnPickler.scala | 29 |
4 files changed, 36 insertions, 6 deletions
diff --git a/src/library/scala/reflect/Print.scala b/src/library/scala/reflect/Print.scala index a84e024c36..1c51a8b2b1 100644 --- a/src/library/scala/reflect/Print.scala +++ b/src/library/scala/reflect/Print.scala @@ -101,6 +101,8 @@ object Print extends Function1[Any, String] { "[" + Print(lo) + " ... " + Print(hi) + "]" case reflect.MethodType(formals, resultType) => formals.map(Print).mkString("(", ", ", ")") + " => " + Print(resultType) + case reflect.NullaryMethodType(resultType) => + " => " + Print(resultType) case reflect.PolyType(typeParams, typeBounds, resultType) => val z = (typeParams, typeBounds).zipped map ((tp, tb) => "[" + Print(tb._1) + " :> " + Print(tp) + " :> " + Print(tb._2) + "]") z.mkString("[", ", ", "]") + " -> " + Print(resultType) diff --git a/src/library/scala/reflect/Type.scala b/src/library/scala/reflect/Type.scala index 029bb3966e..0ec0b77fad 100644 --- a/src/library/scala/reflect/Type.scala +++ b/src/library/scala/reflect/Type.scala @@ -49,6 +49,8 @@ case class TypeBounds(lo: Type, hi: Type) extends Type * <code>(formals1 ... formalsn) restpe</code> */ case class MethodType(formals: List[Symbol], restpe: Type) extends Type +/** This type is required by the compiler and <b>should not be used in client code</b>. */ +case class NullaryMethodType(resultType: Type) extends Type /** This type is required by the compiler and <b>should not be used in client code</b>. */ case class PolyType(typeParams: List[Symbol], typeBounds: List[(Type, Type)], resultType: Type) extends Type @@ -71,5 +73,6 @@ extends MethodType(formals, restpe) case reflect.AppliedType(tpe, args) => case reflect.TypeBounds(lo, hi) => case reflect.MethodType(formals, restpe) => //can also be ImplicitMethodType + case reflect.NullaryMethodType(restpe) => case reflect.PolyType(typeParams, typeBounds, resultType) => */ diff --git a/src/library/scala/reflect/generic/Types.scala b/src/library/scala/reflect/generic/Types.scala index 17e19715d7..6dcd90e66c 100755 --- a/src/library/scala/reflect/generic/Types.scala +++ b/src/library/scala/reflect/generic/Types.scala @@ -69,6 +69,9 @@ trait Types { self: Universe => type MethodType <: Type val MethodType: MethodTypeExtractor + type NullaryMethodType <: Type + val NullaryMethodType: NullaryMethodTypeExtractor + type PolyType <: Type val PolyType: PolyTypeExtractor @@ -132,6 +135,11 @@ trait Types { self: Universe => def unapply(tpe: MethodType): Option[(List[Symbol], Type)] } + abstract class NullaryMethodTypeExtractor { + def apply(resultType: Type): NullaryMethodType + def unapply(tpe: NullaryMethodType): Option[(Type)] + } + abstract class PolyTypeExtractor { def apply(typeParams: List[Symbol], resultType: Type): PolyType def unapply(tpe: PolyType): Option[(List[Symbol], Type)] diff --git a/src/library/scala/reflect/generic/UnPickler.scala b/src/library/scala/reflect/generic/UnPickler.scala index 7b7c34a767..86b73cf5fd 100755 --- a/src/library/scala/reflect/generic/UnPickler.scala +++ b/src/library/scala/reflect/generic/UnPickler.scala @@ -281,7 +281,7 @@ abstract class UnPickler { sym case MODULEsym => - val clazz = at(inforef, readType).typeSymbol + val clazz = at(inforef, () => readType()).typeSymbol // after the NMT_TRANSITION period, we can leave off the () => ... () if (isModuleRoot) moduleRoot else { val m = owner.newModule(name, clazz) @@ -298,8 +298,13 @@ abstract class UnPickler { }) } - /** Read a type */ - protected def readType(): Type = { + /** Read a type + * + * @param forceProperType is used to ease the transition to NullaryMethodTypes (commentmarker: NMT_TRANSITION) + * the flag say that a type of kind * is expected, so that PolyType(tps, restpe) can be disambiguated to PolyType(tps, NullaryMethodType(restpe)) + * (if restpe is not a ClassInfoType, a MethodType or a NullaryMethodType, which leaves TypeRef/SingletonType -- the latter would make the polytype a type constructor) + */ + protected def readType(forceProperType: Boolean = false): Type = { val tag = readByte() val end = readNat() + readIndex (tag: @switch) match { @@ -341,7 +346,19 @@ abstract class UnPickler { case POLYtpe => val restpe = readTypeRef() val typeParams = until(end, readSymbolRef) - PolyType(typeParams, restpe) + if(typeParams nonEmpty) { + // NMT_TRANSITION: old class files denoted a polymorphic nullary method as PolyType(tps, restpe), we now require PolyType(tps, NullaryMethodType(restpe)) + // when a type of kind * is expected (forceProperType is true), we know restpe should be wrapped in a NullaryMethodType (if it wasn't suitably wrapped yet) + def transitionNMT(restpe: Type) = { + val resTpeCls = restpe.getClass.toString // what's uglier than isInstanceOf? right! -- isInstanceOf does not work since the concrete types are defined in the compiler (not in scope here) + if(forceProperType /*&& pickleformat < 2.9 */ && !(resTpeCls.endsWith("MethodType"))) { assert(!resTpeCls.contains("ClassInfoType")) + NullaryMethodType(restpe) } + else restpe + } + PolyType(typeParams, transitionNMT(restpe)) + } + else + NullaryMethodType(restpe) case EXISTENTIALtpe => val restpe = readTypeRef() ExistentialType(until(end, readSymbolRef), restpe) @@ -352,7 +369,7 @@ abstract class UnPickler { typeRef = readNat() s } else NoSymbol // selfsym can go. - val tp = at(typeRef, readType) + val tp = at(typeRef, () => readType(forceProperType)) // NMT_TRANSITION val annots = until(end, readAnnotationRef) if (selfsym == NoSymbol) AnnotatedType(annots, tp, selfsym) else tp @@ -739,7 +756,7 @@ abstract class UnPickler { /* Read a reference to a pickled item */ protected def readNameRef(): Name = at(readNat(), readName) protected def readSymbolRef(): Symbol = at(readNat(), readSymbol) - protected def readTypeRef(): Type = at(readNat(), readType) + protected def readTypeRef(): Type = at(readNat(), () => readType()) // after the NMT_TRANSITION period, we can leave off the () => ... () protected def readConstantRef(): Constant = at(readNat(), readConstant) protected def readAnnotationRef(): AnnotationInfo = at(readNat(), readAnnotation) protected def readModifiersRef(): Modifiers = at(readNat(), readModifiers) |