summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2010-09-28 22:13:38 +0000
committerPaul Phillips <paulp@improving.org>2010-09-28 22:13:38 +0000
commit54f661510422cae577f92b2915c2296dbabf007e (patch)
tree2c0d3e222dcfacf885f5edd152ef3958c45d1af8
parenteac72bbee3bfa6c24f8b32400a136c085312030e (diff)
downloadscala-54f661510422cae577f92b2915c2296dbabf007e.tar.gz
scala-54f661510422cae577f92b2915c2296dbabf007e.tar.bz2
scala-54f661510422cae577f92b2915c2296dbabf007e.zip
Modified typekinds to offer a more general lub ...
Modified typekinds to offer a more general lub when it encounters interfaces so it does not end up in a disagreement with the jvm. References #3872, but modifying the compiler lubs is not yet done. Review by dragos.
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala85
1 files changed, 42 insertions, 43 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala b/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala
index e72db4abc2..bf5a364515 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala
@@ -63,12 +63,26 @@ trait TypeKinds { self: ICodes =>
}
def toTypeAt(ph: Phase): Type = atPhase(ph)(toType)
- def isReferenceType = false
- def isArrayType = false
- def isValueType = false
- final def isRefOrArrayType = isReferenceType || isArrayType
- final def isNothingType = this == REFERENCE(NothingClass)
+ def isReferenceType = false
+ def isArrayType = false
+ def isValueType = false
+ def isBoxedType = false
+ final def isRefOrArrayType = isReferenceType || isArrayType
+ final def isRefArrayOrBoxType = isRefOrArrayType || isBoxedType
+ final def isNothingType = this == REFERENCE(NothingClass)
+ final def isInterfaceType = this match {
+ case REFERENCE(cls) if cls.isInterface => true
+ case _ => false
+ }
+ final def isBoxOrInterfaceType = isBoxedType || isInterfaceType
+ /** On the JVM, these types are like Ints for the
+ * purposes of calculating the lub.
+ */
+ def isIntSizedType: Boolean = this match {
+ case BOOL | CHAR | BYTE | SHORT | INT => true
+ case _ => false
+ }
def isIntegralType: Boolean = this match {
case BYTE | SHORT | INT | LONG | CHAR => true
case _ => false
@@ -117,48 +131,32 @@ trait TypeKinds { self: ICodes =>
* The lub is based on the lub of scala types.
*/
def lub(a: TypeKind, b: TypeKind): TypeKind = {
- def lub0(tk1: TypeKind, tk2: TypeKind): Type = {
- /** Returning existing implementation unless flag given. See #3872. */
- if (!isCheckerDebug)
- return global.lub(List(tk1.toType, tk2.toType))
-
- /** PP: Obviously looking for " with " is a bit short of the ideal robustness,
- * but that's why it's only used under -Ycheck-debug. Correct fix is likely
- * to change compound types to lead with the class type.
- */
- atPhase(currentRun.typerPhase) {
- val t1 = tk1.toType
- val t2 = tk2.toType
- val calculated = global.lub(List(t1, t2))
- checkerDebug("at Phase %s, lub0(%s, %s) == %s".format(global.globalPhase, t1, t2, calculated))
- calculated match {
- case x: CompoundType =>
- val tps = x.baseTypeSeq.toList filterNot (_.toString contains " with ")
- val id = global.erasure.erasure.intersectionDominator(tps)
- checkerDebug("intersectionDominator(%s) == %s".format(tps.mkString(", "), id))
- id
- case x => x
- }
- }
- }
+ /** The compiler will be altered so this returns the right thing for our
+ * purposes here.
+ */
+ def lub0(tk1: TypeKind, tk2: TypeKind): Type =
+ global.lub(List(tk1.toType, tk2.toType))
+
+ // Approximate the JVM view of subtyping by collapsing boxed
+ // values and interfaces into AnyRef. If we try to be more precise
+ // with intersections we run into post-erasure issues where a type
+ // like A with B may be used as an A in one place and a B in another.
+ def isBoxLub = (
+ (a.isBoxOrInterfaceType && b.isRefArrayOrBoxType) ||
+ (b.isBoxOrInterfaceType && a.isRefArrayOrBoxType)
+ )
+ def isIntLub = (
+ (a == INT && b.isIntSizedType) ||
+ (b == INT && a.isIntSizedType)
+ )
if (a == b) a
else if (a.isNothingType) b
else if (b.isNothingType) a
- else (a, b) match {
- case (BOXED(a1), BOXED(b1)) => if (a1 == b1) a else REFERENCE(AnyRefClass)
- case (BOXED(_), REFERENCE(_)) | (REFERENCE(_), BOXED(_)) => REFERENCE(AnyRefClass)
- case (BOXED(_), ARRAY(_)) | (ARRAY(_), BOXED(_)) => REFERENCE(AnyRefClass)
- case (BYTE, INT) | (INT, BYTE) => INT
- case (SHORT, INT) | (INT, SHORT) => INT
- case (CHAR, INT) | (INT, CHAR) => INT
- case (BOOL, INT) | (INT, BOOL) => INT
- case _ =>
- if (a.isRefOrArrayType && b.isRefOrArrayType)
- toTypeKind(lub0(a, b))
- else
- throw new CheckerException("Incompatible types: " + a + " with " + b)
- }
+ else if (isBoxLub) REFERENCE(AnyRefClass)
+ else if (isIntLub) INT
+ else if (a.isRefOrArrayType && b.isRefOrArrayType) toTypeKind(lub0(a, b))
+ else throw new CheckerException("Incompatible types: " + a + " with " + b)
}
/** The unit value */
@@ -312,6 +310,7 @@ trait TypeKinds { self: ICodes =>
/** A boxed value. */
case class BOXED(kind: TypeKind) extends TypeKind {
+ override def isBoxedType = true
/**
* Approximate `lub'. The common type of two references is
* always AnyRef. For 'real' least upper bound wrt to subclassing