Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Cleaning up dummy applied types and friends.

Incorporates feedback from dealias/widen PR.
  • Loading branch information...
commit 46e8eceecc5b5f6b49075550e54f035126fc256a 1 parent 901ac16
Paul Phillips authored
View
6 src/compiler/scala/tools/nsc/interpreter/IMain.scala
@@ -521,8 +521,8 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends
Right(buildRequest(line, trees))
}
- // normalize non-public types so we don't see protected aliases like Self
- def normalizeNonPublic(tp: Type) = tp match {
+ // dealias non-public types so we don't see protected aliases like Self
+ def dealiasNonPublic(tp: Type) = tp match {
case TypeRef(_, sym, _) if sym.isAliasType && !sym.isPublic => tp.dealias
case _ => tp
}
@@ -980,7 +980,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends
def cleanTypeAfterTyper(sym: => Symbol): Type = {
exitingTyper(
- normalizeNonPublic(
+ dealiasNonPublic(
dropNullaryMethod(
sym.tpe_*
)
View
16 src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
@@ -29,8 +29,8 @@ abstract class ClassfileParser {
protected var in: AbstractFileReader = _ // the class file reader
protected var clazz: Symbol = _ // the class symbol containing dynamic members
protected var staticModule: Symbol = _ // the module symbol containing static members
- protected var instanceScope: Scope = _ // the scope of all instance definitions
- protected var staticScope: Scope = _ // the scope of all static definitions
+ protected var instanceScope: Scope = _ // the scope of all instance definitions
+ protected var staticScope: Scope = _ // the scope of all static definitions
protected var pool: ConstantPool = _ // the classfile's constant pool
protected var isScala: Boolean = _ // does class file describe a scala class?
protected var isScalaAnnot: Boolean = _ // does class file describe a scala class with its pickled info in an annotation?
@@ -739,15 +739,9 @@ abstract class ClassfileParser {
// isMonomorphicType is false if the info is incomplete, as it usually is here
// so have to check unsafeTypeParams.isEmpty before worrying about raw type case below,
// or we'll create a boatload of needless existentials.
- else if (classSym.isMonomorphicType || classSym.unsafeTypeParams.isEmpty) {
- tp
- }
- else {
- // raw type - existentially quantify all type parameters
- val eparams = typeParamsToExistentials(classSym, classSym.unsafeTypeParams)
- val t = typeRef(pre, classSym, eparams.map(_.tpeHK))
- logResult(s"raw type from $classSym")(newExistentialType(eparams, t))
- }
+ else if (classSym.isMonomorphicType || classSym.unsafeTypeParams.isEmpty) tp
+ // raw type - existentially quantify all type parameters
+ else logResult(s"raw type from $classSym")(definitions.unsafeClassExistentialType(classSym))
case tp =>
assert(sig.charAt(index) != '<', tp)
tp
View
7 src/compiler/scala/tools/nsc/typechecker/Checkable.scala
@@ -62,6 +62,9 @@ trait Checkable {
bases foreach { bc =>
val tps1 = (from baseType bc).typeArgs
val tps2 = (tvarType baseType bc).typeArgs
+ if (tps1.size != tps2.size)
+ devWarning(s"Unequally sized type arg lists in propagateKnownTypes($from, $to): ($tps1, $tps2)")
+
(tps1, tps2).zipped foreach (_ =:= _)
// Alternate, variance respecting formulation causes
// neg/unchecked3.scala to fail (abstract types). TODO -
@@ -78,7 +81,7 @@ trait Checkable {
val resArgs = tparams zip tvars map {
case (_, tvar) if tvar.instValid => tvar.constr.inst
- case (tparam, _) => tparam.tpe
+ case (tparam, _) => tparam.tpeHK
}
appliedType(to, resArgs: _*)
}
@@ -108,7 +111,7 @@ trait Checkable {
private class CheckabilityChecker(val X: Type, val P: Type) {
def Xsym = X.typeSymbol
def Psym = P.typeSymbol
- def XR = propagateKnownTypes(X, Psym)
+ def XR = if (Xsym == AnyClass) classExistentialType(Psym) else propagateKnownTypes(X, Psym)
// sadly the spec says (new java.lang.Boolean(true)).isInstanceOf[scala.Boolean]
def P1 = X matchesPattern P
def P2 = !Psym.isPrimitiveValueClass && isNeverSubType(X, P)
View
6 src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -1222,8 +1222,6 @@ trait Infer extends Checkable {
}
}
- def widen(tp: Type): Type = abstractTypesToBounds(tp)
-
/** Substitute free type variables `undetparams` of type constructor
* `tree` in pattern, given prototype `pt`.
*
@@ -1232,7 +1230,7 @@ trait Infer extends Checkable {
* @param pt the expected result type of the instance
*/
def inferConstructorInstance(tree: Tree, undetparams: List[Symbol], pt0: Type) {
- val pt = widen(pt0)
+ val pt = abstractTypesToBounds(pt0)
val ptparams = freeTypeParamsOfTerms(pt)
val ctorTp = tree.tpe
val resTp = ctorTp.finalResultType
@@ -1371,7 +1369,7 @@ trait Infer extends Checkable {
}
def inferTypedPattern(tree0: Tree, pattp: Type, pt0: Type, canRemedy: Boolean): Type = {
- val pt = widen(pt0)
+ val pt = abstractTypesToBounds(pt0)
val ptparams = freeTypeParamsOfTerms(pt)
val tpparams = freeTypeParamsOfTerms(pattp)
View
16 src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -3360,7 +3360,12 @@ trait Typers extends Adaptations with Tags {
// return the corresponding extractor (an instance of ClassTag[`pt`])
def extractorForUncheckedType(pos: Position, pt: Type): Option[Tree] = if (isPastTyper) None else {
// only look at top-level type, can't (reliably) do anything about unchecked type args (in general)
- pt.normalize.typeConstructor match {
+ // but at least make a proper type before passing it elsewhere
+ val pt1 = pt.dealias match {
+ case tr @ TypeRef(pre, sym, args) if args.nonEmpty => copyTypeRef(tr, pre, sym, sym.typeParams map (_.tpeHK)) // replace actual type args with dummies
+ case pt1 => pt1
+ }
+ pt1 match {
// if at least one of the types in an intersection is checkable, use the checkable ones
// this avoids problems as in run/matchonseq.scala, where the expected type is `Coll with scala.collection.SeqLike`
// Coll is an abstract type, but SeqLike of course is not
@@ -3706,7 +3711,8 @@ trait Typers extends Adaptations with Tags {
else containsDef(owner, sym) || isRawParameter(sym) || isCapturedExistential(sym)
def containsLocal(tp: Type): Boolean =
tp exists (t => isLocal(t.typeSymbol) || isLocal(t.termSymbol))
- val normalizeLocals = new TypeMap {
+
+ val dealiasLocals = new TypeMap {
def apply(tp: Type): Type = tp match {
case TypeRef(pre, sym, args) =>
if (sym.isAliasType && containsLocal(tp)) apply(tp.dealias)
@@ -3759,9 +3765,9 @@ trait Typers extends Adaptations with Tags {
for (sym <- remainingSyms) addLocals(sym.existentialBound)
}
- val normalizedTpe = normalizeLocals(tree.tpe)
- addLocals(normalizedTpe)
- packSymbols(localSyms.toList, normalizedTpe)
+ val dealiasedType = dealiasLocals(tree.tpe)
+ addLocals(dealiasedType)
+ packSymbols(localSyms.toList, dealiasedType)
}
def typedClassOf(tree: Tree, tpt: Tree, noGen: Boolean = false) =
View
5 src/reflect/scala/reflect/internal/Definitions.scala
@@ -714,7 +714,10 @@ trait Definitions extends api.StandardDefinitions {
* C[E1, ..., En] forSome { E1 >: LB1 <: UB1 ... en >: LBn <: UBn }.
*/
def classExistentialType(clazz: Symbol): Type =
- newExistentialType(clazz.typeParams, clazz.tpe_*)
+ existentialAbstraction(clazz.typeParams, clazz.tpe_*)
+
+ def unsafeClassExistentialType(clazz: Symbol): Type =
+ existentialAbstraction(clazz.unsafeTypeParams, clazz.tpe_*)
// members of class scala.Any
lazy val Any_== = enterNewMethod(AnyClass, nme.EQ, anyparam, booltype, FINAL)
View
130 src/reflect/scala/reflect/internal/Types.scala
@@ -835,23 +835,27 @@ trait Types extends api.Types { self: SymbolTable =>
}
/** Is this type a subtype of that type in a pattern context?
- * Any type arguments on the right hand side are replaced with
+ * Dummy type arguments on the right hand side are replaced with
* fresh existentials, except for Arrays.
*
* See bug1434.scala for an example of code which would fail
* if only a <:< test were applied.
*/
- def matchesPattern(that: Type): Boolean = {
- (this <:< that) || ((this, that) match {
- case (TypeRef(_, ArrayClass, List(arg1)), TypeRef(_, ArrayClass, List(arg2))) if arg2.typeSymbol.typeParams.nonEmpty =>
- arg1 matchesPattern arg2
- case (_, TypeRef(_, _, args)) =>
- val newtp = existentialAbstraction(args map (_.typeSymbol), that)
- !(that =:= newtp) && (this <:< newtp)
- case _ =>
- false
- })
- }
+ def matchesPattern(that: Type): Boolean = (this <:< that) || (that match {
+ case ArrayTypeRef(elem2) if elem2.typeConstructor.isHigherKinded =>
+ this match {
+ case ArrayTypeRef(elem1) => elem1 matchesPattern elem2
+ case _ => false
+ }
+ case TypeRef(_, sym, args) =>
+ val that1 = existentialAbstraction(args map (_.typeSymbol), that)
+ (that ne that1) && (this <:< that1) && {
+ log(s"$this.matchesPattern($that) depended on discarding args and testing <:< $that1")
+ true
+ }
+ case _ =>
+ false
+ })
def stat_<:<(that: Type): Boolean = {
if (Statistics.canEnable) Statistics.incCounter(subtypeCount)
@@ -2867,6 +2871,13 @@ trait Types extends api.Types { self: SymbolTable =>
}
}
+ object ArrayTypeRef {
+ def unapply(tp: Type) = tp match {
+ case TypeRef(_, ArrayClass, arg :: Nil) => Some(arg)
+ case _ => None
+ }
+ }
+
//@M
// a TypeVar used to be a case class with only an origin and a constr
// then, constr became mutable (to support UndoLog, I guess),
@@ -2936,27 +2947,6 @@ trait Types extends api.Types { self: SymbolTable =>
createTypeVar(tparam.tpeHK, deriveConstraint(tparam), Nil, tparam.typeParams, untouchable)
}
- /** Repack existential types, otherwise they sometimes get unpacked in the
- * wrong location (type inference comes up with an unexpected skolem)
- */
- def repackExistential(tp: Type): Type = (
- if (tp == NoType) tp
- else existentialAbstraction(existentialsInType(tp), tp)
- )
-
- def containsDummyTypeArg(tp: Type) = tp exists isDummyTypeArg
- def isDummyTypeArg(tp: Type) = tp.typeSymbol.isTypeParameter
- def isDummyAppliedType(tp: Type) = tp match {
- case TypeRef(_, _, args) if args.nonEmpty => args exists isDummyTypeArg
- case _ => false
- }
-
- def existentialsInType(tpe: Type) =
- tpe withFilter typeIsExistentiallyBound map (_.typeSymbol)
-
- def containsExistential(tpe: Type) =
- tpe exists typeIsExistentiallyBound
-
/** Precondition: params.nonEmpty. (args.nonEmpty enforced structurally.)
*/
class HKTypeVar(
@@ -3742,17 +3732,14 @@ trait Types extends api.Types { self: SymbolTable =>
}
/** Type with all top-level occurrences of abstract types replaced by their bounds */
- def abstractTypesToBounds(tp: Type): Type = tp match { // @M don't normalize here (compiler loops on pos/bug1090.scala )
- case TypeRef(_, sym, _) if sym.isAbstractType =>
- abstractTypesToBounds(tp.bounds.hi)
- case TypeRef(_, sym, _) if sym.isAliasType =>
- abstractTypesToBounds(tp.normalize)
- case rtp @ RefinedType(parents, decls) =>
- copyRefinedType(rtp, parents mapConserve abstractTypesToBounds, decls)
- case AnnotatedType(_, underlying, _) =>
- abstractTypesToBounds(underlying)
- case _ =>
- tp
+ object abstractTypesToBounds extends TypeMap {
+ def apply(tp: Type): Type = tp match {
+ case TypeRef(_, sym, _) if sym.isAliasType => apply(tp.dealias)
+ case TypeRef(_, sym, _) if sym.isAbstractType => apply(tp.bounds.hi)
+ case rtp @ RefinedType(parents, decls) => copyRefinedType(rtp, parents mapConserve this, decls)
+ case AnnotatedType(_, _, _) => mapOver(tp)
+ case _ => tp // no recursion - top level only
+ }
}
// Set to true for A* => Seq[A]
@@ -3923,7 +3910,7 @@ trait Types extends api.Types { self: SymbolTable =>
}
}
class ClassUnwrapper(existential: Boolean) extends TypeUnwrapper(poly = true, existential, annotated = true, nullary = false) {
- override def apply(tp: Type) = super.apply(tp.normalize)
+ override def apply(tp: Type) = super.apply(tp.normalize) // normalize is required here
}
object unwrapToClass extends ClassUnwrapper(existential = true) { }
@@ -4170,6 +4157,26 @@ trait Types extends api.Types { self: SymbolTable =>
}
}
+ /** Repack existential types, otherwise they sometimes get unpacked in the
+ * wrong location (type inference comes up with an unexpected skolem)
+ */
+ def repackExistential(tp: Type): Type = (
+ if (tp == NoType) tp
+ else existentialAbstraction(existentialsInType(tp), tp)
+ )
+
+ def containsExistential(tpe: Type) = tpe exists typeIsExistentiallyBound
+ def existentialsInType(tpe: Type) = tpe withFilter typeIsExistentiallyBound map (_.typeSymbol)
+
+ private def isDummyOf(tpe: Type)(targ: Type) = {
+ val sym = targ.typeSymbol
+ sym.isTypeParameter && sym.owner == tpe.typeSymbol
+ }
+ def isDummyAppliedType(tp: Type) = tp.dealias match {
+ case tr @ TypeRef(_, _, args) => args exists isDummyOf(tr)
+ case _ => false
+ }
+
def typeParamsToExistentials(clazz: Symbol, tparams: List[Symbol]): List[Symbol] = {
val eparams = mapWithIndex(tparams)((tparam, i) =>
clazz.newExistential(newTypeName("?"+i), clazz.pos) setInfo tparam.info.bounds)
@@ -4179,19 +4186,14 @@ trait Types extends api.Types { self: SymbolTable =>
def typeParamsToExistentials(clazz: Symbol): List[Symbol] =
typeParamsToExistentials(clazz, clazz.typeParams)
+ def isRawIfWithoutArgs(sym: Symbol) = sym.isClass && sym.typeParams.nonEmpty && sym.isJavaDefined
+ /** Is type tp a ''raw type''? */
// note: it's important to write the two tests in this order,
// as only typeParams forces the classfile to be read. See #400
- private def isRawIfWithoutArgs(sym: Symbol) =
- sym.isClass && sym.typeParams.nonEmpty && sym.isJavaDefined
-
- def isRaw(sym: Symbol, args: List[Type]) =
- !phase.erasedTypes && isRawIfWithoutArgs(sym) && args.isEmpty
-
- /** Is type tp a ''raw type''? */
- def isRawType(tp: Type) = tp match {
- case TypeRef(_, sym, args) => isRaw(sym, args)
- case _ => false
- }
+ def isRawType(tp: Type) = !phase.erasedTypes && (tp match {
+ case TypeRef(_, sym, Nil) => isRawIfWithoutArgs(sym)
+ case _ => false
+ })
/** The raw to existential map converts a ''raw type'' to an existential type.
* It is necessary because we might have read a raw type of a
@@ -5624,7 +5626,11 @@ trait Types extends api.Types { self: SymbolTable =>
// for (tpFresh <- tpsFresh) tpFresh.setInfo(tpFresh.info.substSym(tparams1, tpsFresh))
}
} && annotationsConform(tp1.normalize, tp2.normalize)
- case (_, _) => false // @assume !tp1.isHigherKinded || !tp2.isHigherKinded
+ case (ntp1, ntp2) =>
+ if (isDummyAppliedType(ntp1) || isDummyAppliedType(ntp2)) {
+ devWarning(s"isHKSubType0($tp1, $tp2, _) contains dummy typeref: ($ntp1, $ntp2)")
+ }
+ false // @assume !tp1.isHigherKinded || !tp2.isHigherKinded
// --> thus, cannot be subtypes (Any/Nothing has already been checked)
}))
@@ -5644,7 +5650,11 @@ trait Types extends api.Types { self: SymbolTable =>
if (tp1 eq NoPrefix) return (tp2 eq NoPrefix) || tp2.typeSymbol.isPackageClass // !! I do not see how the "isPackageClass" would be warranted by the spec
if (tp2 eq NoPrefix) return tp1.typeSymbol.isPackageClass
if (isSingleType(tp1) && isSingleType(tp2) || isConstantType(tp1) && isConstantType(tp2)) return tp1 =:= tp2
- if (tp1.isHigherKinded || tp2.isHigherKinded) return isHKSubType0(tp1, tp2, depth)
+ if (tp1.isHigherKinded && tp2.isHigherKinded) return isHKSubType0(tp1, tp2, depth)
+ if (tp1.isHigherKinded || tp2.isHigherKinded) {
+ devWarning(s"isSubType2($tp1, $tp2, _) compares HK type with proper type")
+ return isHKSubType0(tp1, tp2, depth)
+ }
/** First try, on the right:
* - unwrap Annotated types, BoundedWildcardTypes,
@@ -5722,7 +5732,7 @@ trait Types extends api.Types { self: SymbolTable =>
case NotNullClass => tp1.isNotNull
case SingletonClass => tp1.isStable || fourthTry
case _: ClassSymbol =>
- if (isRaw(sym2, tp2.args))
+ if (isRawType(tp2))
isSubType(tp1, rawToExistential(tp2), depth)
else if (sym2.name == tpnme.REFINE_CLASS_NAME)
isSubType(tp1, sym2.info, depth)
@@ -5804,7 +5814,7 @@ trait Types extends api.Types { self: SymbolTable =>
isSingleType(tp2) && isSubType(tp1, tp2.widen, depth)
}
case _: ClassSymbol =>
- if (isRaw(sym1, tr1.args))
+ if (isRawType(tp1))
isSubType(rawToExistential(tp1), tp2, depth)
else if (sym1.isModuleClass) tp2 match {
case SingleType(pre2, sym2) => equalSymsAndPrefixes(sym1.sourceModule, pre1, sym2, pre2)

0 comments on commit 46e8ece

Please sign in to comment.
Something went wrong with that request. Please try again.