diff options
author | Paul Phillips <paulp@improving.org> | 2012-03-31 12:16:30 -0700 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2012-04-01 08:27:08 -0700 |
commit | 17356bf8a3eddb5b6652884ee5b96970d3ddb6cf (patch) | |
tree | 1216e71d03e0695d8064d2b526fb6b1984013073 | |
parent | 4f24d01d226608aaf6d92cc20659f59e5d29b299 (diff) | |
download | scala-17356bf8a3eddb5b6652884ee5b96970d3ddb6cf.tar.gz scala-17356bf8a3eddb5b6652884ee5b96970d3ddb6cf.tar.bz2 scala-17356bf8a3eddb5b6652884ee5b96970d3ddb6cf.zip |
More principled unwrapping of types.
Made generic type unwrapper for use by the many methods which
need various types to be transparent with respect to the operation
being performed. AnnotatedType, PolyType, NullaryMethodType,
and ExistentialType all commonly match this description.
-rw-r--r-- | src/compiler/scala/reflect/internal/Types.scala | 17 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 68 |
2 files changed, 53 insertions, 32 deletions
diff --git a/src/compiler/scala/reflect/internal/Types.scala b/src/compiler/scala/reflect/internal/Types.scala index f70e9760f7..e062e875cd 100644 --- a/src/compiler/scala/reflect/internal/Types.scala +++ b/src/compiler/scala/reflect/internal/Types.scala @@ -3616,6 +3616,23 @@ trait Types extends api.Types { self: SymbolTable => } } + class TypeUnwrapper(poly: Boolean, existential: Boolean, annotated: Boolean, nullary: Boolean) extends (Type => Type) { + def apply(tp: Type): Type = tp match { + case AnnotatedType(_, underlying, _) if annotated => apply(underlying) + case ExistentialType(_, underlying) if existential => apply(underlying) + case PolyType(_, underlying) if poly => apply(underlying) + case NullaryMethodType(underlying) if nullary => apply(underlying) + case tp => tp + } + } + class ClassUnwrapper(existential: Boolean) extends TypeUnwrapper(poly = true, existential, annotated = true, nullary = false) { + override def apply(tp: Type) = super.apply(tp.normalize) + } + + object unwrapToClass extends ClassUnwrapper(existential = true) { } + object unwrapToStableClass extends ClassUnwrapper(existential = false) { } + object unwrapWrapperTypes extends TypeUnwrapper(true, true, true, true) { } + trait AnnotationFilter extends TypeMap { def keepAnnotation(annot: AnnotationInfo): Boolean diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 7d2e587b3f..ae184d2677 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -235,32 +235,40 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { result } } + def isNonRefinementClassType(tpe: Type) = tpe match { + case SingleType(_, sym) => sym.isModuleClass + case TypeRef(_, sym, _) => sym.isClass && !sym.isRefinementClass + case ErrorType => true + case _ => false + } + private def errorNotClass(tpt: Tree, found: Type) = { ClassTypeRequiredError(tpt, found); false } + private def errorNotStable(tpt: Tree, found: Type) = { TypeNotAStablePrefixError(tpt, found); false } /** Check that `tpt` refers to a non-refinement class type */ - def checkClassType(tpt: Tree, existentialOK: Boolean, stablePrefix: Boolean): Boolean = { - def errorNotClass(found: AnyRef) = { ClassTypeRequiredError(tpt, found); false } - def check(tpe: Type): Boolean = tpe.normalize match { - case TypeRef(pre, sym, _) if sym.isClass && !sym.isRefinementClass => - if (stablePrefix && !isPastTyper) - if (!pre.isStable) { - TypeNotAStablePrefixError(tpt, pre) - false - } else - // A type projection like X#Y can get by the stable check if the - // prefix is singleton-bounded, so peek at the tree too. - tpt match { - case SelectFromTypeTree(qual, _) if !isSingleType(qual.tpe) => errorNotClass(tpt) - case _ => true - } - else - true - case ErrorType => true - case PolyType(_, restpe) => check(restpe) - case ExistentialType(_, restpe) if existentialOK => check(restpe) - case AnnotatedType(_, underlying, _) => check(underlying) - case t => errorNotClass(t) + def checkClassType(tpt: Tree): Boolean = { + val tpe = unwrapToClass(tpt.tpe) + isNonRefinementClassType(tpe) || errorNotClass(tpt, tpe) + } + + /** Check that `tpt` refers to a class type with a stable prefix. */ + def checkStablePrefixClassType(tpt: Tree): Boolean = { + val tpe = unwrapToStableClass(tpt.tpe) + + def prefixIsStable = { + def checkPre = tpe match { + case TypeRef(pre, _, _) => pre.isStable || errorNotStable(tpt, pre) + case _ => true + } + // A type projection like X#Y can get by the stable check if the + // prefix is singleton-bounded, so peek at the tree too. + def checkTree = tpt match { + case SelectFromTypeTree(qual, _) => isSingleType(qual.tpe) || errorNotStable(tpt, qual.tpe) + case _ => true + } + checkPre && checkTree } - check(tpt.tpe) + + isNonRefinementClassType(tpe) && (isPastTyper || prefixIsStable) } /** Check that type <code>tp</code> is not a subtype of itself. @@ -643,13 +651,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } } - private def isNarrowable(tpe: Type): Boolean = tpe match { + private def isNarrowable(tpe: Type): Boolean = unwrapWrapperTypes(tpe) match { case TypeRef(_, _, _) | RefinedType(_, _) => true - case ExistentialType(_, tpe1) => isNarrowable(tpe1) - case AnnotatedType(_, tpe1, _) => isNarrowable(tpe1) - case PolyType(_, tpe1) => isNarrowable(tpe1) - case NullaryMethodType(tpe1) => isNarrowable(tpe1) - case _ => !phase.erasedTypes + case _ => !phase.erasedTypes } /** @@ -1438,7 +1442,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def validateParentClass(parent: Tree, superclazz: Symbol) { if (!parent.isErrorTyped) { val psym = parent.tpe.typeSymbol.initialize - checkClassType(parent, false, true) + checkStablePrefixClassType(parent) if (psym != superclazz) { if (psym.isTrait) { val ps = psym.info.parents @@ -3353,7 +3357,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } def typedClassOf(tree: Tree, tpt: Tree, noGen: Boolean = false) = - if (!checkClassType(tpt, true, false) && noGen) tpt + if (!checkClassType(tpt) && noGen) tpt else atPos(tree.pos)(gen.mkClassOf(tpt.tpe)) protected def typedExistentialTypeTree(tree: ExistentialTypeTree, mode: Int): Tree = { @@ -3665,7 +3669,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def typedNew(tpt: Tree) = { val tpt1 = { val tpt0 = typedTypeConstructor(tpt) - if (checkClassType(tpt0, false, true)) + if (checkStablePrefixClassType(tpt0)) if (tpt0.hasSymbol && !tpt0.symbol.typeParams.isEmpty) { context.undetparams = cloneSymbols(tpt0.symbol.typeParams) TypeTree().setOriginal(tpt0) |