summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2011-06-22 21:10:33 +0000
committerPaul Phillips <paulp@improving.org>2011-06-22 21:10:33 +0000
commit9853b5b8292650d8e192b500e636dc441550eef7 (patch)
treeec3a85b9b7af29c9fbd3bba17389791f083394cc /src/compiler/scala/tools
parente49ec10e93eeec1af775096de07dd46a7d0a1fad (diff)
downloadscala-9853b5b8292650d8e192b500e636dc441550eef7.tar.gz
scala-9853b5b8292650d8e192b500e636dc441550eef7.tar.bz2
scala-9853b5b8292650d8e192b500e636dc441550eef7.zip
A total rewrite of "runtimeClass", discarding t...
A total rewrite of "runtimeClass", discarding the user-space approach in favor of simply fixing getClass. def f1 = 5.getClass // Class[Int] def f2 = (5: AnyVal).getClass // Class[_ <: AnyVal] def f3 = (5: java.lang.Integer).getClass // Class[_ <: java.lang.Integer] class A class B extends A def f1 = (new B: Any).getClass().newInstance() // Any def f2 = (new B: AnyRef).getClass().newInstance() // AnyRef def f3 = (new B: A).getClass().newInstance() // A def f4 = (new B: B).getClass().newInstance() // B But that's not all! def f0[T >: B] = (new B: T).getClass().newInstance() def f5 = f0[Any] // Any def f6 = f0[AnyRef] // AnyRef def f7 = f0[A] // A def f8 = f0[B] // B Closes #490, #896, #4696. Review by moors. (Note: I think this is pretty good, but picky review requested.)
Diffstat (limited to 'src/compiler/scala/tools')
-rw-r--r--src/compiler/scala/tools/cmd/gen/AnyVals.scala23
-rw-r--r--src/compiler/scala/tools/nsc/transform/Erasure.scala66
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala17
3 files changed, 87 insertions, 19 deletions
diff --git a/src/compiler/scala/tools/cmd/gen/AnyVals.scala b/src/compiler/scala/tools/cmd/gen/AnyVals.scala
index 9f8e488c43..31b44744da 100644
--- a/src/compiler/scala/tools/cmd/gen/AnyVals.scala
+++ b/src/compiler/scala/tools/cmd/gen/AnyVals.scala
@@ -12,12 +12,12 @@ trait AnyValReps {
self: AnyVals =>
sealed abstract class AnyValNum(name: String) extends AnyValRep(name) {
- def isCardinal: Boolean = isIntegerType(this)
- def unaryOps = if (isCardinal) List("+", "-", "~") else List("+", "-")
- def bitwiseOps = if (isCardinal) List("|", "&", "^") else Nil
- def shiftOps = if (isCardinal) List("<<", ">>>", ">>") else Nil
- def comparisonOps = List("==", "!=", "<", "<=", ">", ">=")
- def otherOps = List("+", "-" ,"*", "/", "%")
+ def isCardinal: Boolean = isIntegerType(this)
+ def unaryOps = if (isCardinal) List("+", "-", "~") else List("+", "-")
+ def bitwiseOps = if (isCardinal) List("|", "&", "^") else Nil
+ def shiftOps = if (isCardinal) List("<<", ">>>", ">>") else Nil
+ def comparisonOps = List("==", "!=", "<", "<=", ">", ">=")
+ def otherOps = List("+", "-" ,"*", "/", "%")
// Given two numeric value types S and T , the operation type of S and T is defined as follows:
// If both S and T are subrange types then the operation type of S and T is Int.
@@ -49,7 +49,7 @@ trait AnyValReps {
)
xs1 ++ xs2
}
- def classLines = clumps.foldLeft(List[String]()) {
+ def classLines = (clumps :+ commonClassLines).foldLeft(List[String]()) {
case (res, Nil) => res
case (res, lines) =>
val xs = lines map {
@@ -80,6 +80,9 @@ trait AnyValReps {
sealed abstract class AnyValRep(val name: String) {
def classLines: List[String]
def objectLines: List[String]
+ def commonClassLines = List(
+ "def getClass(): Class[@name@]"
+ )
def lcname = name.toLowerCase
def boxedName = this match {
@@ -243,6 +246,8 @@ def &&(x: Boolean): Boolean = sys.error("stub")
def |(x: Boolean): Boolean = sys.error("stub")
def &(x: Boolean): Boolean = sys.error("stub")
def ^(x: Boolean): Boolean = sys.error("stub")
+
+def getClass(): Class[Boolean] = sys.error("stub")
""".trim.lines.toList
def objectLines = interpolate(allCompanions).lines.toList
@@ -254,7 +259,9 @@ def ^(x: Boolean): Boolean = sys.error("stub")
* only one value of type Unit: `()`.
*/
"""
- def classLines = Nil
+ def classLines = List(
+ """def getClass(): Class[Unit] = sys.error("stub")"""
+ )
def objectLines = interpolate(allCompanions).lines.toList
override def boxUnboxImpls = Map(
diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala
index 1dc36633db..a2fd3ebbb4 100644
--- a/src/compiler/scala/tools/nsc/transform/Erasure.scala
+++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala
@@ -66,6 +66,39 @@ abstract class Erasure extends AddInterfaces
}
}
+ // A type function from T => Class[U], used to determine the return
+ // type of getClass calls. The returned type is:
+ //
+ // 1. If T is a value type, Class[T].
+ // 2. If T is anonymous or a refinement type, calculate the intersection
+ // dominator of the parents T', and Class[_ <: T'].
+ // 3. If T is a phantom type (Any or AnyVal), Class[_].
+ // 4. Otherwise, Class[_ <: T].
+ //
+ // Note: AnyVal cannot be Class[_ <: AnyVal] because if the static type of the
+ // receiver is AnyVal, it implies the receiver is boxed, so the correct
+ // class object is that of java.lang.Integer, not Int.
+ //
+ // TODO: If T is final, return type could be Class[T]. Should it?
+ def getClassReturnType(tp: Type): Type = {
+ def mkClass(targs: List[Type]) = typeRef(ClassClass.tpe.prefix, ClassClass, targs)
+ val tparams = ClassClass.typeParams
+ val sym = tp.typeSymbol
+
+ if (tparams.isEmpty) mkClass(Nil) // call must be coming post-erasure
+ else if (isValueClass(sym)) mkClass(List(tp.widen))
+ else if (sym.isLocalClass) getClassReturnType(erasure.intersectionDominator(tp.parents))
+ else {
+ val eparams = typeParamsToExistentials(ClassClass, tparams)
+ val upperBound = if (isPhantomClass(sym)) AnyClass.tpe else tp.widen
+
+ existentialAbstraction(
+ eparams,
+ mkClass(List(eparams.head setInfo TypeBounds.upper(upperBound) tpe))
+ )
+ }
+ }
+
private def unboundedGenericArrayLevel(tp: Type): Int = tp match {
case GenericArray(level, core) if !(core <:< AnyRefClass.tpe) => level
case _ => 0
@@ -491,6 +524,12 @@ abstract class Erasure extends AddInterfaces
case _ => tp.deconst
}
}
+ // Methods on Any/Object which we rewrite here while we still know what
+ // is a primitive and what arrived boxed.
+ private lazy val interceptedMethods = Set[Symbol](Any_##, Object_##, Any_getClass) ++ (
+ // Each value class has its own getClass for ultra-precise class object typing.
+ ScalaValueClasses map (_.tpe member nme.getClass_)
+ )
// -------- erasure on trees ------------------------------------------
@@ -974,17 +1013,24 @@ abstract class Erasure extends AddInterfaces
SelectFromArray(qual, name, erasure(qual.tpe)).copyAttrs(fn),
args)
- case Apply(fn @ Select(qual, _), Nil) if fn.symbol == Any_## || fn.symbol == Object_## =>
- // This is unattractive, but without it we crash here on ().## because after
- // erasure the ScalaRunTime.hash overload goes from Unit => Int to BoxedUnit => Int.
- // This must be because some earlier transformation is being skipped on ##, but so
- // far I don't know what. For null we now define null.## == 0.
- val arg = qual.tpe.typeSymbolDirect match {
- case UnitClass => BLOCK(qual, REF(BoxedUnit_UNIT)) // ({ expr; UNIT }).##
- case NullClass => LIT(0) // (null: Object).##
- case _ => qual
+ case Apply(fn @ Select(qual, _), Nil) if interceptedMethods(fn.symbol) =>
+ if (fn.symbol == Any_## || fn.symbol == Object_##) {
+ // This is unattractive, but without it we crash here on ().## because after
+ // erasure the ScalaRunTime.hash overload goes from Unit => Int to BoxedUnit => Int.
+ // This must be because some earlier transformation is being skipped on ##, but so
+ // far I don't know what. For null we now define null.## == 0.
+ val arg = qual.tpe.typeSymbolDirect match {
+ case UnitClass => BLOCK(qual, REF(BoxedUnit_UNIT)) // ({ expr; UNIT }).##
+ case NullClass => LIT(0) // (null: Object).##
+ case _ => qual
+ }
+ Apply(gen.mkAttributedRef(scalaRuntimeHash), List(arg))
}
- Apply(gen.mkAttributedRef(scalaRuntimeHash), List(arg))
+ // Rewrite 5.getClass to ScalaRunTime.anyValClass(5)
+ else if (isValueClass(qual.tpe.typeSymbol))
+ Apply(gen.mkAttributedRef(scalaRuntimeAnyValClass), List(qual))
+ else
+ tree
case Apply(fn, args) =>
if (fn.symbol == Any_asInstanceOf)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 7edb624b8e..dd92418af7 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -3611,7 +3611,7 @@ trait Typers extends Modes {
if (settings.Xchecknull.value && isPotentialNullDeference && unit != null)
unit.warning(tree.pos, "potential null pointer dereference: "+tree)
- result match {
+ val selection = result match {
// could checkAccessible (called by makeAccessible) potentially have skipped checking a type application in qual?
case SelectFromTypeTree(qual@TypeTree(), name) if qual.tpe.typeArgs nonEmpty => // TODO: somehow the new qual is not checked in refchecks
treeCopy.SelectFromTypeTree(
@@ -3631,6 +3631,21 @@ trait Typers extends Modes {
case _ =>
result
}
+ // To fully benefit from special casing the return type of
+ // getClass, we have to catch it immediately so expressions
+ // like x.getClass().newInstance() are typed with the type of x.
+ val isRefinableGetClass = (
+ selection.symbol.name == nme.getClass_
+ && selection.tpe.params.isEmpty
+ // TODO: If the type of the qualifier is inaccessible, we can cause private types
+ // to escape scope here, e.g. pos/t1107. I'm not sure how to properly handle this
+ // so for now it requires the type symbol be public.
+ && qual.tpe.typeSymbol.isPublic
+ )
+ if (isRefinableGetClass)
+ selection setType MethodType(Nil, erasure.getClassReturnType(qual.tpe))
+ else
+ selection
}
}