summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/symtab/Types.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/scala/tools/nsc/symtab/Types.scala')
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Types.scala323
1 files changed, 281 insertions, 42 deletions
diff --git a/src/compiler/scala/tools/nsc/symtab/Types.scala b/src/compiler/scala/tools/nsc/symtab/Types.scala
index 1b3b93eb2c..9c5367b29d 100644
--- a/src/compiler/scala/tools/nsc/symtab/Types.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Types.scala
@@ -8,7 +8,6 @@ package scala.tools.nsc.symtab
import scala.collection.immutable
import scala.collection.mutable.{ListBuffer, HashMap, WeakHashMap}
-import scala.compat.Platform.currentTime
import scala.tools.nsc.ast.TreeGen
import scala.tools.nsc.util.{HashSet, Position, NoPosition}
import Flags._
@@ -70,9 +69,10 @@ trait Types {
var findMemberCount = 0
var noMemberCount = 0
var multMemberCount = 0
- var findMemberMillis = 0l
+ var findMemberNanos = 0l
var subtypeCount = 0
- var subtypeMillis = 0l
+ var sametypeCount = 0
+ var subtypeNanos = 0l
private var explainSwitch = false
@@ -511,14 +511,15 @@ trait Types {
/** Is this type a subtype of that type? */
def <:<(that: Type): Boolean = {
- if (util.Statistics.enabled) subtypeCount += 1
- val startTime = if (util.Statistics.enabled) currentTime else 0l
+// val startTime = if (util.Statistics.enabled) System.nanoTime() else 0l
val result =
((this eq that) ||
(if (explainSwitch) explain("<", isSubType, this, that)
else isSubType(this, that, AnyDepth)))
- if (util.Statistics.enabled)
- subtypeMillis = subtypeMillis + currentTime - startTime
+// if (util.Statistics.enabled) {
+// subtypeNanos += System.nanoTime() - startTime
+// subtypeCount += 1
+// }
result
}
@@ -686,7 +687,7 @@ trait Types {
if (!this.isGround)
return typeVarToOriginMap(this).findMember(name, excludedFlags, requiredFlags, stableOnly)(from)
if (util.Statistics.enabled) findMemberCount += 1
- val startTime = if (util.Statistics.enabled) currentTime else 0l
+// val startTime = if (util.Statistics.enabled) System.nanoTime() else 0l
//Console.println("find member " + name.decode + " in " + this + ":" + this.baseClasses)//DEBUG
var members: Scope = null
@@ -712,8 +713,7 @@ trait Types {
sym.getFlag(PRIVATE | LOCAL) != (PRIVATE | LOCAL) ||
(bcs0.head.hasTransOwner(bcs.head)))) {
if (name.isTypeName || stableOnly && sym.isStable) {
- if (util.Statistics.enabled)
- findMemberMillis = findMemberMillis + currentTime - startTime
+// if (util.Statistics.enabled) findMemberNanos += System.nanoTime() - startTime
return sym
} else if (member == NoSymbol) {
member = sym
@@ -753,8 +753,7 @@ trait Types {
} // while (!bcs.isEmpty)
excluded = excludedFlags
} // while (continue)
- if (util.Statistics.enabled)
- findMemberMillis = findMemberMillis + currentTime - startTime
+// if (util.Statistics.enabled) findMemberNanos += System.nanoTime() - startTime
if (members eq null) {
if (util.Statistics.enabled) if (member == NoSymbol) noMemberCount += 1;
member
@@ -1491,29 +1490,34 @@ A type's typeSymbol should never be inspected directly.
private var normalized: Type = null
- override def normalize: Type = {
- if (normalized == null) {
- normalized = if (sym.isAliasType) { // beta-reduce
- if (sym.info.typeParams.length == args.length || !isHigherKinded) {
- /* !isHigherKinded && sym.info.typeParams.length != args.length only happens when compiling e.g.,
- `val x: Class' with -Xgenerics, while `type Class = java.lang.Class' had already been compiled without -Xgenerics */
- val xform = transform(sym.info.resultType)
- assert(xform ne this, this)
- xform.normalize // cycles have been checked in typeRef
- } else PolyType(typeParams, transform(sym.info.resultType).normalize) // eta-expand
- // @M TODO: should not use PolyType, as that's the type of a polymorphic value -- we really want a type *function*
- } else if (isHigherKinded) {
- // @M TODO: should not use PolyType, as that's the type of a polymorphic value -- we really want a type *function*
- // @M: initialize needed (see test/files/pos/ticket0137.scala)
- PolyType(typeParams, typeRef(pre, sym.initialize, higherKindedArgs))
- } else if (sym.isRefinementClass) {
- sym.info.normalize // @MO to AM: OK?
- } else {
- super.normalize
- }
+ def normalize0: Type =
+ if (sym.isAliasType) { // beta-reduce
+ if (sym.info.typeParams.length == args.length || !isHigherKinded) {
+ /* !isHigherKinded && sym.info.typeParams.length != args.length only happens when compiling e.g.,
+ `val x: Class' with -Xgenerics, while `type Class = java.lang.Class' had already been compiled without -Xgenerics */
+ val xform = transform(sym.info.resultType)
+ assert(xform ne this, this)
+ xform.normalize // cycles have been checked in typeRef
+ } else {
+ PolyType(typeParams, transform(sym.info.resultType).normalize) // eta-expand
+ // @M TODO: should not use PolyType, as that's the type of a polymorphic value -- we really want a type *function*
+ }
+ } else if (isHigherKinded) {
+ // @M TODO: should not use PolyType, as that's the type of a polymorphic value -- we really want a type *function*
+ // @M: initialize needed (see test/files/pos/ticket0137.scala)
+ PolyType(typeParams, typeRef(pre, sym.initialize, higherKindedArgs))
+ } else if (sym.isRefinementClass) {
+ sym.info.normalize // @MO to AM: OK?
+ } else {
+ super.normalize
+ }
+
+ override def normalize: Type =
+ if (phase.erasedTypes) normalize0
+ else {
+ if (normalized == null) normalized = normalize0
+ normalized
}
- normalized
- }
override def decls: Scope = {
sym.info match {
@@ -2325,7 +2329,7 @@ A type's typeSymbol should never be inspected directly.
// Hash consing --------------------------------------------------------------
- private var uniques: HashSet[AnyRef] = _
+ var uniques: HashSet[AnyRef] = _
private var uniqueRunId = NoRunId
def uniqueTypeCount = uniques.size // for statistics
@@ -2649,6 +2653,17 @@ A type's typeSymbol should never be inspected directly.
eparams
}
+ def isRaw(sym: Symbol, args: List[Type]) =
+ !phase.erasedTypes && !sym.typeParams.isEmpty && sym.hasFlag(JAVA) && args.isEmpty
+ // note: it's important to write the two first tests in this order,
+ // as only typeParams forces the classfile to be read. See #400
+
+ /** Is type tp a ``raw type''? */
+ def isRawType(tp: Type) = tp match {
+ case TypeRef(_, sym, args) => isRaw(sym, args)
+ 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
* parameterized Java class from a class file. At the time we read the type
@@ -3446,6 +3461,7 @@ A type's typeSymbol should never be inspected directly.
subsametypeRecursions += 1
val lastUndoLog = undoLog
val result = isSameType0(tp1, tp2)
+ sametypeCount += 1
if (!result) undoTo(lastUndoLog)
result
} finally {
@@ -3464,11 +3480,9 @@ A type's typeSymbol should never be inspected directly.
if (subsametypeRecursions == 0) undoLog = List()
}
- def normalizePlus(tp: Type) = tp match {
- case TypeRef(pre, sym, List()) if (sym.isInitialized && sym.hasFlag(JAVA) && !sym.typeParams.isEmpty) =>
- rawToExistential(tp)
- case _ => tp.normalize
- }
+ def normalizePlus(tp: Type) =
+ if (isRawType(tp)) rawToExistential(tp)
+ else tp.normalize
/*
todo: change to:
@@ -3623,7 +3637,6 @@ A type's typeSymbol should never be inspected directly.
if (subsametypeRecursions == 0) undoLog = List()
}
-
/** Does this type have a prefix that begins with a type variable */
def beginsWithTypeVar(tp: Type): Boolean = tp match {
case SingleType(pre, sym) =>
@@ -3645,9 +3658,235 @@ A type's typeSymbol should never be inspected directly.
tp
}
+ def isErrorOrWildcard(tp: Type) = (tp eq ErrorType) || (tp eq WildcardType)
+
+ def isSingleType(tp: Type) = tp match {
+ case ThisType(_) | SuperType(_, _) | SingleType(_, _) => true
+ case _ => false
+ }
+
+ def isConstantType(tp: Type) = tp match {
+ case ConstantType(_) => true
+ case _ => false
+ }
+
+ def isHKSubType0(tp1: Type, tp2: Type, depth: Int): Boolean = (
+ tp1.typeSymbol == NothingClass
+ ||
+ tp2.typeSymbol == AnyClass // @M Any and Nothing are super-type resp. subtype of every well-kinded type
+ || // @M! normalize reduces higher-kinded case to PolyType's
+ ((tp1.normalize, tp2.normalize) match {
+ case (PolyType(tparams1, res1), PolyType(tparams2, res2)) =>
+ tparams1.length == tparams2.length &&
+ List.forall2(tparams1, tparams2) (
+ (p1, p2) => p2.info.substSym(tparams2, tparams1) <:< p1.info) &&
+ res1 <:< res2.substSym(tparams2, tparams1)
+
+ case (tp1a, tp2a) =>
+ (isDifferentType(tp1a, tp1) || isDifferentType(tp2a, tp2)) &&
+ isSubType(tp1a, tp2a, depth)
+ }))
+
+ def isSubArgs(tps1: List[Type], tps2: List[Type], tparams: List[Symbol]): Boolean = (
+ tps1.isEmpty && tps2.isEmpty
+ ||
+ !tps1.isEmpty && !tps2.isEmpty &&
+ (tparams.head.isCovariant || (tps2.head <:< tps1.head)) &&
+ (tparams.head.isContravariant || (tps1.head <:< tps2.head)) &&
+ isSubArgs(tps1.tail, tps2.tail, tparams.tail)
+ )
+
+ def isSubType0(tp1: Type, tp2: Type, depth: Int): Boolean = {
+ isSubType1(tp1, tp2, depth)
+ }
+
+ def differentOrNone(tp1: Type, tp2: Type) = if (tp1 eq tp2) NoType else tp1
+
+ /** Does type `tp1' conform to `tp2'?
+ */
+ private def isSubType2(tp1: Type, tp2: Type, depth: Int): Boolean = {
+ if (isErrorOrWildcard(tp1)) return true
+ if (isErrorOrWildcard(tp2)) return true
+ if (tp1 eq NoType) return false
+ if (tp2 eq NoType) return false
+ if (tp1 eq NoPrefix) return (tp2 eq NoPrefix) || tp2.typeSymbol.isPackageClass
+ if (tp2 eq NoPrefix) return (tp1 eq NoPrefix) || 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)
+
+ /** First try, on the right:
+ * - unwrap Annotated types, BoundedWildcardTypes,
+ * - bind TypeVars on the right, if lhs is not Annotated nor BoundedWildcard
+ * - handle common cases for first-kind TypeRefs on both sides as a fast path.
+ */
+ def firstTry = tp2 match {
+ // fast path: two typerefs, none of them HK
+ case tr2: TypeRef =>
+ tp1 match {
+ case tr1: TypeRef =>
+ val sym1 = tr1.sym
+ val sym2 = tr2.sym
+ val pre1 = tr1.pre
+ val pre2 = tr2.pre
+ ((if (sym1 == sym2) phase.erasedTypes || pre1 <:< pre2
+ else (sym1.name == sym2.name && isUnifiable(pre1, pre2))) &&
+ isSubArgs(tr1.args, tr2.args, sym1.typeParams)
+ ||
+ sym2.isClass && {
+ val base = tr1 baseType sym2
+ (base ne tr1) && base <:< tr2
+ }
+ ||
+ thirdTryRef(tr1, tr2))
+ case _ =>
+ secondTry
+ }
+ case AnnotatedType(_, _, _) =>
+ tp1.withoutAnnotations <:< tp2.withoutAnnotations && annotationsConform(tp1, tp2)
+ case BoundedWildcardType(bounds) =>
+ tp1 <:< bounds.hi
+ case tv2 @ TypeVar(_, constr2) =>
+ tp1 match {
+ case AnnotatedType(_, _, _) | BoundedWildcardType(_) =>
+ secondTry
+ case _ =>
+ if (constr2.inst != NoType) tp1 <:< constr2.inst
+ else isRelatable(tv2, tp1) && { constr2.lobounds = tp1 :: constr2.lobounds; true }
+ }
+ case _ =>
+ secondTry
+ }
+
+ /** Second try, on the left:
+ * - unwrap AnnotatedTypes, BoundedWildcardTypes,
+ * - bind typevars,
+ * - handle existential types by skolemization.
+ */
+ def secondTry = tp1 match {
+ case AnnotatedType(_, _, _) =>
+ tp1.withoutAnnotations <:< tp2.withoutAnnotations && annotationsConform(tp1, tp2)
+ case BoundedWildcardType(bounds) =>
+ tp1.bounds.lo <:< tp2
+ case tv1 @ TypeVar(_, constr1) =>
+ if (constr1.inst != NoType) constr1.inst <:< tp2
+ else isRelatable(tv1, tp2) && { constr1.hibounds = tp2 :: constr1.hibounds; true }
+ case ExistentialType(_, _) =>
+ try {
+ skolemizationLevel += 1
+ tp1.skolemizeExistential(NoSymbol, null) <:< tp2
+ } finally {
+ skolemizationLevel -= 1
+ }
+ case _ =>
+ thirdTry
+ }
+
+ def thirdTryRef(tp1: Type, tp2: TypeRef): Boolean = {
+ val sym2 = tp2.sym
+ if (sym2.isAliasType) {
+ isSubType(tp1.normalize, tp2.normalize, depth)
+ } else if (sym2.isAbstractType) {
+ val tp2a = tp2.bounds.lo
+ isDifferentType(tp2, tp2a) && tp1 <:< tp2a || fourthTry
+ } else if (sym2 == NotNullClass) {
+ tp1.isNotNull
+ } else if (sym2 == SingletonClass) {
+ tp1.isStable
+ } else if (isRaw(sym2, tp2.args)) {
+ isSubType(tp1, rawToExistential(tp2), depth)
+ } else if (sym2.isRefinementClass) {
+ isSubType(tp1, sym2.info, depth)
+ } else {
+ fourthTry
+ }
+ }
+
+ /** Third try, on the right:
+ * - decompose refined types.
+ * - handle typerefs, existentials, and notnull types.
+ * - handle left+right method types, polytypes, typebounds
+ */
+ def thirdTry = tp2 match {
+ case tr2: TypeRef =>
+ thirdTryRef(tp1, tr2)
+ case RefinedType(parents2, ref2) =>
+ (parents2 forall (tp1 <:< _)) &&
+ (ref2.toList forall tp1.specializes)
+ case et: ExistentialType =>
+ et.withTypeVars(tp1 <:< _, depth) || fourthTry
+ case NotNullType(ntp2) =>
+ tp1.isNotNull && tp1 <:< ntp2
+ case MethodType(params2, res2) =>
+ tp1 match {
+ case MethodType(params1, res1) =>
+ (params1.length == params2.length &&
+ matchingParams(tp1.paramTypes, tp2.paramTypes, tp1.isInstanceOf[JavaMethodType], tp2.isInstanceOf[JavaMethodType]) &&
+ (res1 <:< res2) &&
+ tp1.isInstanceOf[ImplicitMethodType] == tp2.isInstanceOf[ImplicitMethodType])
+ case _ =>
+ false
+ }
+ case PolyType(List(), res2) =>
+ tp1 match {
+ case PolyType(List(), res1) =>
+ // other polytypes were already checked in isHKSubType
+ res1 <:< res2
+ case _ =>
+ false
+ }
+ case TypeBounds(lo2, hi2) =>
+ tp1 match {
+ case TypeBounds(lo1, hi1) =>
+ lo2 <:< lo1 && hi1 <:< hi2
+ case _ =>
+ false
+ }
+ case _ =>
+ fourthTry
+ }
+
+ /** Fourth try, on the left:
+ * - handle typerefs, refined types, notnull and singleton types.
+ */
+ def fourthTry = tp1 match {
+ case TypeRef(pre1, sym1, args1) =>
+ if (sym1.isAliasType) {
+ isSubType(tp1.normalize, tp2.normalize, depth)
+ } else if (sym1.isAbstractType) {
+ val tp1a = tp1.bounds.hi
+ isDifferentType(tp1, tp1a) && tp1a <:< tp2
+ } else if (sym1 == NothingClass) {
+ true
+ } else if (sym1 == NullClass) {
+ tp2 match {
+ case TypeRef(_, sym2, _) =>
+ (sym2 isNonBottomSubClass ObjectClass) &&
+ !(tp2.normalize.typeSymbol isNonBottomSubClass NotNullClass)
+ case _ =>
+ isSingleType(tp2) && tp1 <:< tp2.widen
+ }
+ } else if (isRaw(sym1, args1)) {
+ isSubType(rawToExistential(tp1), tp2, depth)
+ } else if (sym1.isRefinementClass) {
+ isSubType(sym1.info, tp2, depth)
+ } else {
+ false
+ }
+ case RefinedType(parents1, ref1) =>
+ parents1 exists (_ <:< tp2)
+ case _: SingletonType | _: NotNullType =>
+ tp1.underlying <:< tp2
+ case _ =>
+ false
+ }
+
+ firstTry
+ }
+
/** Does type `tp1' conform to `tp2'?
*/
- private def isSubType0(tp1: Type, tp2: Type, depth: Int): Boolean = {
+ private def isSubType1(tp1: Type, tp2: Type, depth: Int): Boolean = {
((tp1, tp2) match {
case (ErrorType, _) => true
case (WildcardType, _) => true