summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/reflect/internal/Definitions.scala21
-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
-rw-r--r--src/library-aux/scala/Any.scala6
-rwxr-xr-xsrc/library/scala/Boolean.scala2
-rw-r--r--src/library/scala/Byte.scala1
-rw-r--r--src/library/scala/Char.scala1
-rw-r--r--src/library/scala/Double.scala1
-rw-r--r--src/library/scala/Float.scala1
-rw-r--r--src/library/scala/Int.scala1
-rw-r--r--src/library/scala/Long.scala1
-rw-r--r--src/library/scala/Short.scala1
-rwxr-xr-xsrc/library/scala/Unit.scala5
-rw-r--r--src/library/scala/runtime/ScalaRunTime.scala16
-rw-r--r--test/files/run/getClassTest.check18
-rw-r--r--test/files/run/getClassTest.scala66
17 files changed, 223 insertions, 24 deletions
diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala
index 967bea38b4..ce869cfd3d 100644
--- a/src/compiler/scala/reflect/internal/Definitions.scala
+++ b/src/compiler/scala/reflect/internal/Definitions.scala
@@ -248,6 +248,7 @@ trait Definitions /*extends reflect.generic.StandardDefinitions*/ {
def arrayCloneMethod = getMember(ScalaRunTimeModule, "array_clone")
def ensureAccessibleMethod = getMember(ScalaRunTimeModule, "ensureAccessible")
def scalaRuntimeHash = getMember(ScalaRunTimeModule, "hash")
+ def scalaRuntimeAnyValClass = getMember(ScalaRunTimeModule, "anyValClass")
def scalaRuntimeSameElements = getMember(ScalaRunTimeModule, nme.sameElements)
// classes with special meanings
@@ -526,6 +527,7 @@ trait Definitions /*extends reflect.generic.StandardDefinitions*/ {
var Any_equals : Symbol = _
var Any_hashCode : Symbol = _
var Any_toString : Symbol = _
+ var Any_getClass : Symbol = _
var Any_isInstanceOf: Symbol = _
var Any_asInstanceOf: Symbol = _
var Any_## : Symbol = _
@@ -816,11 +818,24 @@ trait Definitions /*extends reflect.generic.StandardDefinitions*/ {
// members of class scala.Any
Any_== = newMethod(AnyClass, nme.EQ, anyparam, booltype) setFlag FINAL
Any_!= = newMethod(AnyClass, nme.NE, anyparam, booltype) setFlag FINAL
- Any_equals = newMethod(AnyClass, nme.equals_, anyparam, booltype)
+ Any_equals = newMethod(AnyClass, nme.equals_, anyparam, booltype)
Any_hashCode = newMethod(AnyClass, nme.hashCode_, Nil, inttype)
Any_toString = newMethod(AnyClass, nme.toString_, Nil, stringtype)
- Any_## = newMethod(AnyClass, nme.HASHHASH, Nil, inttype) setFlag FINAL
-
+ Any_## = newMethod(AnyClass, nme.HASHHASH, Nil, inttype) setFlag FINAL
+
+ // Any_getClass requires special handling. The return type is determined on
+ // a per-call-site basis as if the function being called were actually:
+ //
+ // // Assuming `target.getClass()`
+ // def getClass[T](target: T): Class[_ <: T]
+ //
+ // Since getClass is not actually a polymorphic method, this requires compiler
+ // participation. At the "Any" level, the return type is Class[_] as it is in
+ // java.lang.Object. Java also special cases the return type.
+ Any_getClass = (
+ newMethod(AnyClass, nme.getClass_, Nil, getMember(ObjectClass, nme.getClass_).tpe.resultType)
+ setFlag DEFERRED
+ )
Any_isInstanceOf = newPolyMethod(
AnyClass, nme.isInstanceOf_, tparam => NullaryMethodType(booltype)) setFlag FINAL
Any_asInstanceOf = newPolyMethod(
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
}
}
diff --git a/src/library-aux/scala/Any.scala b/src/library-aux/scala/Any.scala
index 2feab8f836..490c546bbf 100644
--- a/src/library-aux/scala/Any.scala
+++ b/src/library-aux/scala/Any.scala
@@ -54,6 +54,12 @@ abstract class Any {
*/
def toString: String
+ /** Returns the runtime class representation of the object.
+ *
+ * @return a class object corresponding to the static type of the receiver
+ */
+ def getClass(): Class[_]
+
/** Test two objects for equality.
*
* @param that the object to compare against this object for equality.
diff --git a/src/library/scala/Boolean.scala b/src/library/scala/Boolean.scala
index 7719065ae7..f77bdd2ea0 100755
--- a/src/library/scala/Boolean.scala
+++ b/src/library/scala/Boolean.scala
@@ -29,6 +29,8 @@ final class Boolean extends AnyVal {
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")
}
object Boolean extends AnyValCompanion {
diff --git a/src/library/scala/Byte.scala b/src/library/scala/Byte.scala
index 540e05c19f..8c598e044a 100644
--- a/src/library/scala/Byte.scala
+++ b/src/library/scala/Byte.scala
@@ -144,6 +144,7 @@ final class Byte extends AnyVal {
def %(x: Float): Float = sys.error("stub")
def %(x: Double): Double = sys.error("stub")
+ def getClass(): Class[Byte] = sys.error("stub")
}
object Byte extends AnyValCompanion {
diff --git a/src/library/scala/Char.scala b/src/library/scala/Char.scala
index a5d1cb019b..a8f15125bf 100644
--- a/src/library/scala/Char.scala
+++ b/src/library/scala/Char.scala
@@ -144,6 +144,7 @@ final class Char extends AnyVal {
def %(x: Float): Float = sys.error("stub")
def %(x: Double): Double = sys.error("stub")
+ def getClass(): Class[Char] = sys.error("stub")
}
object Char extends AnyValCompanion {
diff --git a/src/library/scala/Double.scala b/src/library/scala/Double.scala
index 5f2e01063f..108c6207bb 100644
--- a/src/library/scala/Double.scala
+++ b/src/library/scala/Double.scala
@@ -118,6 +118,7 @@ final class Double extends AnyVal {
def %(x: Float): Double = sys.error("stub")
def %(x: Double): Double = sys.error("stub")
+ def getClass(): Class[Double] = sys.error("stub")
}
object Double extends AnyValCompanion {
diff --git a/src/library/scala/Float.scala b/src/library/scala/Float.scala
index f403e5ccab..9ef7181806 100644
--- a/src/library/scala/Float.scala
+++ b/src/library/scala/Float.scala
@@ -118,6 +118,7 @@ final class Float extends AnyVal {
def %(x: Float): Float = sys.error("stub")
def %(x: Double): Double = sys.error("stub")
+ def getClass(): Class[Float] = sys.error("stub")
}
object Float extends AnyValCompanion {
diff --git a/src/library/scala/Int.scala b/src/library/scala/Int.scala
index c53be1241d..4546934149 100644
--- a/src/library/scala/Int.scala
+++ b/src/library/scala/Int.scala
@@ -144,6 +144,7 @@ final class Int extends AnyVal {
def %(x: Float): Float = sys.error("stub")
def %(x: Double): Double = sys.error("stub")
+ def getClass(): Class[Int] = sys.error("stub")
}
object Int extends AnyValCompanion {
diff --git a/src/library/scala/Long.scala b/src/library/scala/Long.scala
index 3124130663..12b8a25b8a 100644
--- a/src/library/scala/Long.scala
+++ b/src/library/scala/Long.scala
@@ -144,6 +144,7 @@ final class Long extends AnyVal {
def %(x: Float): Float = sys.error("stub")
def %(x: Double): Double = sys.error("stub")
+ def getClass(): Class[Long] = sys.error("stub")
}
object Long extends AnyValCompanion {
diff --git a/src/library/scala/Short.scala b/src/library/scala/Short.scala
index 8bf6f09154..81953505b7 100644
--- a/src/library/scala/Short.scala
+++ b/src/library/scala/Short.scala
@@ -144,6 +144,7 @@ final class Short extends AnyVal {
def %(x: Float): Float = sys.error("stub")
def %(x: Double): Double = sys.error("stub")
+ def getClass(): Class[Short] = sys.error("stub")
}
object Short extends AnyValCompanion {
diff --git a/src/library/scala/Unit.scala b/src/library/scala/Unit.scala
index 58b024273b..c5d12afeba 100755
--- a/src/library/scala/Unit.scala
+++ b/src/library/scala/Unit.scala
@@ -15,8 +15,9 @@ package scala
* not represented as objects by the underlying host system. There is
* only one value of type Unit: `()`.
*/
-final class Unit extends AnyVal { }
-
+final class Unit extends AnyVal {
+ def getClass(): Class[Unit] = sys.error("stub")
+}
object Unit extends AnyValCompanion {
diff --git a/src/library/scala/runtime/ScalaRunTime.scala b/src/library/scala/runtime/ScalaRunTime.scala
index 7a8224298b..031f15f10a 100644
--- a/src/library/scala/runtime/ScalaRunTime.scala
+++ b/src/library/scala/runtime/ScalaRunTime.scala
@@ -32,6 +32,22 @@ object ScalaRunTime {
def isValueClass(clazz: Class[_]) = clazz.isPrimitive()
+ /** Return the class object representing an unboxed value type,
+ * e.g. classOf[int], not classOf[java.lang.Integer]. The compiler
+ * rewrites expressions like 5.getClass to come here.
+ */
+ def anyValClass[T <: AnyVal](value: T): Class[T] = (value match {
+ case x: Byte => java.lang.Byte.TYPE
+ case x: Short => java.lang.Short.TYPE
+ case x: Char => java.lang.Character.TYPE
+ case x: Int => java.lang.Integer.TYPE
+ case x: Long => java.lang.Long.TYPE
+ case x: Float => java.lang.Float.TYPE
+ case x: Double => java.lang.Double.TYPE
+ case x: Boolean => java.lang.Boolean.TYPE
+ case x: Unit => java.lang.Void.TYPE
+ }).asInstanceOf[Class[T]]
+
/** Retrieve generic array element */
def array_apply(xs: AnyRef, idx: Int): Any = xs match {
case x: Array[AnyRef] => x(idx).asInstanceOf[Any]
diff --git a/test/files/run/getClassTest.check b/test/files/run/getClassTest.check
new file mode 100644
index 0000000000..94e86c3889
--- /dev/null
+++ b/test/files/run/getClassTest.check
@@ -0,0 +1,18 @@
+f1: java.lang.Class<?>
+f2: java.lang.Class<?>
+f3: java.lang.Class<java.lang.Object>
+f4: java.lang.Class<? extends java.lang.Integer>
+f5: java.lang.Class<?>
+f0: T
+f1: class java.lang.Object
+f2: class java.lang.Object
+f3: class AnyRefs$A
+f4: class AnyRefs$B
+f5: class java.lang.Object
+f6: class java.lang.Object
+f7: class AnyRefs$A
+f8: class AnyRefs$B
+f1: java.lang.Class<? extends MoreAnyRefs$A>
+f2: java.lang.Class<? extends MoreAnyRefs$B>
+f3: java.lang.Class<?>
+f4: java.lang.Class<? extends MoreAnyRefs$A>
diff --git a/test/files/run/getClassTest.scala b/test/files/run/getClassTest.scala
new file mode 100644
index 0000000000..951cc8d931
--- /dev/null
+++ b/test/files/run/getClassTest.scala
@@ -0,0 +1,66 @@
+class AnyVals {
+ def f1 = (5: Any).getClass
+ def f2 = (5: AnyVal).getClass
+ def f3 = 5.getClass
+ def f4 = (5: java.lang.Integer).getClass
+ def f5 = (5.asInstanceOf[AnyRef]).getClass
+
+ // scalap says:
+ //
+ // def f1 : java.lang.Class[?0] forSome {type ?0} = { /* compiled code */ }
+ // def f2 : java.lang.Class[?0] forSome {type ?0} = { /* compiled code */ }
+ // def f3 : java.lang.Class[scala.Int] = { /* compiled code */ }
+ // def f4 : java.lang.Class[?0] forSome {type ?0 <: java.lang.Integer} = { /* compiled code */ }
+ // def f5 : java.lang.Class[?0] forSome {type ?0 <: scala.AnyRef} = { /* compiled code */ }
+ //
+ // java generic signature says:
+ //
+ // f1: java.lang.Class<?>
+ // f2: java.lang.Class<?>
+ // f3: java.lang.Class<java.lang.Object>
+ // f4: java.lang.Class<? extends java.lang.Integer>
+ // f5: java.lang.Class<?>
+}
+
+class AnyRefs {
+ class A
+ class B extends A
+
+ def f1 = (new B: Any).getClass().newInstance()
+ def f2 = (new B: AnyRef).getClass().newInstance()
+ def f3 = (new B: A).getClass().newInstance()
+ def f4 = (new B: B).getClass().newInstance()
+
+ def f0[T >: B] = (new B: T).getClass().newInstance()
+
+ def f5 = f0[Any]
+ def f6 = f0[AnyRef]
+ def f7 = f0[A]
+ def f8 = f0[B]
+}
+
+class MoreAnyRefs {
+ trait A
+ trait B
+
+ // don't leak anon/refinements
+ def f1 = (new A with B { }).getClass()
+ def f2 = (new B with A { }).getClass()
+ def f3 = (new { def bippy() = 5 }).getClass()
+ def f4 = (new A { def bippy() = 5 }).getClass()
+}
+
+object Test {
+ def returnTypes[T: Manifest] = (
+ manifest[T].erasure.getMethods.toList
+ filter (_.getName startsWith "f")
+ sortBy (_.getName)
+ map (m => m.getName + ": " + m.getGenericReturnType.toString)
+ )
+
+ def main(args: Array[String]): Unit = {
+ returnTypes[AnyVals] foreach println
+ returnTypes[AnyRefs] foreach println
+ returnTypes[MoreAnyRefs] foreach println
+ }
+}