aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2014-04-18 11:31:11 +0200
committerDmitry Petrashko <dmitry.petrashko@gmail.com>2014-05-08 21:47:59 +0200
commit92c02ee9d514c197221fd4c4a027fe0e70e081f2 (patch)
treef67fca6c3ee91658257b960e3e4a39c1bb50d4c3 /src
parent5c11da2148412f36416ad6998909138323ae4894 (diff)
downloaddotty-92c02ee9d514c197221fd4c4a027fe0e70e081f2.tar.gz
dotty-92c02ee9d514c197221fd4c4a027fe0e70e081f2.tar.bz2
dotty-92c02ee9d514c197221fd4c4a027fe0e70e081f2.zip
Removing duplication between Any and Object methods
We cannot have same named methods defined in Object and Any because after erasure the Any references get remapped to the Object methods which would result in a double binding assertion failure. Instead we do the following: - Have some methods exist only in Any, and remap them with the Erasure denotation transformer to be owned by Object. - Have other methods exist only in Object. To achieve this, we synthesize all Any and Object methods; Objetc methods no longer get loaded from a classfile. There's a complication with getClass. We need to reconsider what the best treatment of getClass is. Right now there's too much magic going on for my taste. It might be better to leave getClass on Object only as it is in Java, forget about the special treatement of its type, and have another getClass like method in an decorator on class Any. That could produce the right types and could also work for primitive types.
Diffstat (limited to 'src')
-rw-r--r--src/dotty/tools/dotc/core/Definitions.scala134
-rw-r--r--src/dotty/tools/dotc/core/StdNames.scala2
-rw-r--r--src/dotty/tools/dotc/core/pickling/ClassfileParser.scala6
-rw-r--r--src/dotty/tools/dotc/core/transform/Erasure.scala4
-rw-r--r--src/dotty/tools/dotc/transform/Erasure.scala7
-rw-r--r--src/dotty/tools/dotc/transform/InterceptedMethods.scala20
-rw-r--r--src/dotty/tools/dotc/transform/TypeTestsCasts.scala8
7 files changed, 92 insertions, 89 deletions
diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala
index 9497438f2..211d9e9cd 100644
--- a/src/dotty/tools/dotc/core/Definitions.scala
+++ b/src/dotty/tools/dotc/core/Definitions.scala
@@ -30,6 +30,12 @@ class Definitions {
private def newCompleteClassSymbol(owner: Symbol, name: TypeName, flags: FlagSet, parents: List[TypeRef], decls: Scope = newScope) =
ctx.newCompleteClassSymbol(owner, name, flags | Permanent, parents, decls).entered
+ private def newTopClassSymbol(name: TypeName, flags: FlagSet, parents: List[TypeRef]) = {
+ val cls = newCompleteClassSymbol(ScalaPackageClass, name, flags, parents)
+ ensureConstructor(cls, EmptyScope)
+ cls
+ }
+
private def newTypeParam(cls: ClassSymbol, name: TypeName, flags: FlagSet, scope: MutableScope) =
scope.enter(newSymbol(cls, name, flags | TypeParamCreationFlags, TypeBounds.empty))
@@ -74,7 +80,7 @@ class Definitions {
newPolyMethod(cls, name, 1, resultTypeFn, flags)
private def newT1EmptyParamsMethod(cls: ClassSymbol, name: TermName, resultTypeFn: PolyType => Type, flags: FlagSet) =
- newPolyMethod(cls, name, 1, pt => MethodType(Nil, Nil, resultTypeFn(pt)), flags)
+ newPolyMethod(cls, name, 1, pt => MethodType(Nil, resultTypeFn(pt)), flags)
private def mkArityArray(name: String, arity: Int, countFrom: Int): Array[ClassSymbol] = {
val arr = new Array[ClassSymbol](arity + 1)
@@ -95,58 +101,69 @@ class Definitions {
lazy val JavaPackageVal = ctx.requiredPackage("java")
lazy val JavaLangPackageVal = ctx.requiredPackage("java.lang")
- lazy val ObjectClass = ctx.requiredClass("java.lang.Object")
- lazy val AnyRefAlias: TypeSymbol = newAliasType(tpnme.AnyRef, ObjectType)
-
- lazy val Object_## = newMethod(ObjectClass, nme.HASHHASH, ExprType(IntType), Final)
- lazy val Object_== = newMethod(ObjectClass, nme.EQ, methOfAny(BooleanType), Final)
- lazy val Object_!= = newMethod(ObjectClass, nme.NE, methOfAny(BooleanType), Final)
- lazy val Object_eq = newMethod(ObjectClass, nme.eq, methOfAnyRef(BooleanType), Final)
- lazy val Object_ne = newMethod(ObjectClass, nme.ne, methOfAnyRef(BooleanType), Final)
- lazy val Object_isInstanceOf = newT1ParameterlessMethod(ObjectClass, nme.isInstanceOf_Ob, _ => BooleanType, Final | Synthetic)
- lazy val Object_asInstanceOf = newT1ParameterlessMethod(ObjectClass, nme.asInstanceOf_Ob, PolyParam(_, 0), Final | Synthetic)
- lazy val Object_synchronized = newPolyMethod(ObjectClass, nme.synchronized_, 1,
- pt => MethodType(List(PolyParam(pt, 0)), PolyParam(pt, 0)), Final)
-
- def Object_getClass = objMethod(nme.getClass_)
- def Object_clone = objMethod(nme.clone_)
- def Object_finalize = objMethod(nme.finalize_)
- def Object_notify = objMethod(nme.notify_)
- def Object_notifyAll = objMethod(nme.notifyAll_)
- def Object_equals = objMethod(nme.equals_)
- def Object_hashCode = objMethod(nme.hashCode_)
- def Object_toString = objMethod(nme.toString_)
- private def objMethod(name: PreName) = ObjectClass.requiredMethod(name)
-
- lazy val AnyClass: ClassSymbol = {
- val cls = newCompleteClassSymbol(ScalaPackageClass, tpnme.Any, Abstract, Nil)
- ensureConstructor(cls, EmptyScope)
- cls
- }
-
- lazy val AnyValClass: ClassSymbol = ctx.requiredClass("scala.AnyVal")
+ /** Note: We cannot have same named methods defined in Object and Any (and AnyVal, for that matter)
+ * because after erasure the Any and AnyVal references get remapped to the Object methods
+ * which would result in a double binding assertion failure.
+ * Instead we do the following:
+ *
+ * - Have some methods exist only in Any, and remap them with the Erasure denotation
+ * transformer to be owned by Object.
+ * - Have other methods exist only in Object.
+ * To achieve this, we synthesize all Any and Object methods; Object methods no longer get
+ * loaded from a classfile.
+ *
+ * There's a remaining question about `getClass`. In Scala2.x `getClass` was handled by compiler magic.
+ * This is deemed too cumersome for Dotty and therefore right now `getClass` gets no special treatment;
+ * it's just a method on `Any` which returns the raw type `java.lang.Class`. An alternative
+ * way to get better `getClass` typing would be to treat `getClass` as a method of a generic
+ * decorator which gets remapped in a later phase to Object#getClass. Then we could give it
+ * the right type without changing the typechecker:
+ *
+ * implicit class AnyGetClass[T](val x: T) extends AnyVal {
+ * def getClass: java.lang.Class[T] = ???
+ * }
+ */
+ lazy val AnyClass: ClassSymbol = newTopClassSymbol(tpnme.Any, Abstract, Nil)
+ lazy val AnyValClass: ClassSymbol = newTopClassSymbol(tpnme.AnyVal, Abstract, AnyClass.typeRef :: Nil)
- lazy val AnyVal_getClass = AnyValClass.requiredMethod(nme.getClass_)
lazy val Any_== = newMethod(AnyClass, nme.EQ, methOfAny(BooleanType), Final)
lazy val Any_!= = newMethod(AnyClass, nme.NE, methOfAny(BooleanType), Final)
lazy val Any_equals = newMethod(AnyClass, nme.equals_, methOfAny(BooleanType))
- lazy val Any_hashCode = newMethod(AnyClass, nme.hashCode_, ExprType(IntType))
- lazy val Any_toString = newMethod(AnyClass, nme.toString_, ExprType(StringType))
+ lazy val Any_hashCode = newMethod(AnyClass, nme.hashCode_, MethodType(Nil, IntType))
+ lazy val Any_toString = newMethod(AnyClass, nme.toString_, MethodType(Nil, StringType))
lazy val Any_## = newMethod(AnyClass, nme.HASHHASH, ExprType(IntType), 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.
- lazy val Any_getClass = newMethod(AnyClass, nme.getClass_, ExprType(Object_getClass.info.resultType), Deferred)
+ lazy val Any_getClass = newMethod(AnyClass, nme.getClass_, MethodType(Nil, ClassClass.typeRef), Final)
lazy val Any_isInstanceOf = newT1ParameterlessMethod(AnyClass, nme.isInstanceOf_, _ => BooleanType, Final)
lazy val Any_asInstanceOf = newT1ParameterlessMethod(AnyClass, nme.asInstanceOf_, PolyParam(_, 0), Final)
+ def AnyMethods = List(Any_==, Any_!=, Any_equals, Any_hashCode,
+ Any_toString, Any_##, Any_getClass, Any_isInstanceOf, Any_asInstanceOf)
+
+ lazy val ObjectClass: ClassSymbol = {
+ val cls = ctx.requiredClass("java.lang.Object")
+ assert(!cls.isCompleted, "race for completing java.lang.Object")
+ cls.info = ClassInfo(cls.owner.thisType, cls, AnyClass.typeRef :: Nil, newScope)
+ cls.linkedClass.info = NoType
+ ensureConstructor(cls, cls.decls)
+ cls
+ }
+ lazy val AnyRefAlias: TypeSymbol = newAliasType(tpnme.AnyRef, ObjectType)
+
+ lazy val Object_eq = newMethod(ObjectClass, nme.eq, methOfAnyRef(BooleanType), Final)
+ lazy val Object_ne = newMethod(ObjectClass, nme.ne, methOfAnyRef(BooleanType), Final)
+ lazy val Object_synchronized = newPolyMethod(ObjectClass, nme.synchronized_, 1,
+ pt => MethodType(List(PolyParam(pt, 0)), PolyParam(pt, 0)), Final)
+ lazy val Object_clone = newMethod(ObjectClass, nme.clone_, MethodType(Nil, ObjectType), Protected)
+ lazy val Object_finalize = newMethod(ObjectClass, nme.finalize_, MethodType(Nil, UnitType), Protected)
+ lazy val Object_notify = newMethod(ObjectClass, nme.notify_, MethodType(Nil, UnitType))
+ lazy val Object_notifyAll = newMethod(ObjectClass, nme.notifyAll_, MethodType(Nil, UnitType))
+ lazy val Object_wait = newMethod(ObjectClass, nme.wait_, MethodType(Nil, UnitType))
+ lazy val Object_waitL = newMethod(ObjectClass, nme.wait_, MethodType(LongType :: Nil, UnitType))
+ lazy val Object_waitLI = newMethod(ObjectClass, nme.wait_, MethodType(LongType :: IntType :: Nil, UnitType))
+
+ def ObjectMethods = List(Object_eq, Object_ne, Object_synchronized, Object_clone,
+ Object_finalize, Object_notify, Object_notifyAll, Object_wait, Object_waitL, Object_waitLI)
+
lazy val NotNullClass = ctx.requiredClass("scala.NotNull")
lazy val NothingClass: ClassSymbol = newCompleteClassSymbol(
@@ -380,8 +397,8 @@ class Definitions {
lazy val PhantomClasses = Set[Symbol](AnyClass, AnyValClass, NullClass, NothingClass)
- lazy val asInstanceOfMethods = Set[Symbol](Any_asInstanceOf, Object_asInstanceOf)
- lazy val isInstanceOfMethods = Set[Symbol](Any_isInstanceOf, Object_isInstanceOf)
+ lazy val asInstanceOfMethods = Set[Symbol](Any_asInstanceOf)
+ lazy val isInstanceOfMethods = Set[Symbol](Any_isInstanceOf)
lazy val typeTestsOrCasts = asInstanceOfMethods ++ isInstanceOfMethods
lazy val RootImports = List[Symbol](JavaLangPackageVal, ScalaPackageVal, ScalaPredefModule, DottyPredefModule)
@@ -518,11 +535,11 @@ class Definitions {
/** Lists core classes that don't have underlying bytecode, but are synthesized on-the-fly in every reflection universe */
lazy val syntheticCoreClasses = List(
+ AnyClass,
AnyRefAlias,
RepeatedParamClass,
JavaRepeatedParamClass,
ByNameParamClass2x,
- AnyClass,
AnyValClass,
NullClass,
NothingClass,
@@ -531,26 +548,7 @@ class Definitions {
EmptyPackageVal)
/** Lists core methods that don't have underlying bytecode, but are synthesized on-the-fly in every reflection universe */
- lazy val syntheticCoreMethods = List(
- Any_==,
- Any_!=,
- Any_equals,
- Any_hashCode,
- Any_toString,
- Any_getClass,
- Any_isInstanceOf,
- Any_asInstanceOf,
- Any_##,
- Object_eq,
- Object_ne,
- Object_==,
- Object_!=,
- Object_##,
- Object_synchronized,
- Object_isInstanceOf,
- Object_asInstanceOf,
- String_+
- )
+ lazy val syntheticCoreMethods = AnyMethods ++ ObjectMethods ++ List(String_+)
private[this] var _isInitialized = false
def isInitialized = _isInitialized
diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala
index cb4272f7a..593feb909 100644
--- a/src/dotty/tools/dotc/core/StdNames.scala
+++ b/src/dotty/tools/dotc/core/StdNames.scala
@@ -331,7 +331,6 @@ object StdNames {
val asType: N = "asType"
val asClass: N = "asClass"
val asInstanceOf_ : N = "asInstanceOf"
- val asInstanceOf_Ob : N = "$asInstanceOf"
val assert_ : N = "assert"
val assume_ : N = "assume"
val box: N = "box"
@@ -388,7 +387,6 @@ object StdNames {
val isDefinedAt: N = "isDefinedAt"
val isEmpty: N = "isEmpty"
val isInstanceOf_ : N = "isInstanceOf"
- val isInstanceOf_Ob : N = "$isInstanceOf"
val java: N = "java"
val keepUnions: N = "keepUnions"
val key: N = "key"
diff --git a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala
index f5942dac2..0ed301732 100644
--- a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala
+++ b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala
@@ -891,8 +891,10 @@ class ClassfileParser(
def getType(index: Int)(implicit ctx: Context): Type =
sigToType(getExternalName(index))
- def getSuperClass(index: Int)(implicit ctx: Context): Symbol =
- if (index == 0) defn.AnyClass else getClassSymbol(index)
+ def getSuperClass(index: Int)(implicit ctx: Context): Symbol = {
+ assert(index != 0, "attempt to parse java.lang.Object from classfile")
+ getClassSymbol(index)
+ }
def getConstant(index: Int)(implicit ctx: Context): Constant = {
if (index <= 0 || len <= index) errorBadIndex(index)
diff --git a/src/dotty/tools/dotc/core/transform/Erasure.scala b/src/dotty/tools/dotc/core/transform/Erasure.scala
index a7981b70f..f2e3355fb 100644
--- a/src/dotty/tools/dotc/core/transform/Erasure.scala
+++ b/src/dotty/tools/dotc/core/transform/Erasure.scala
@@ -61,8 +61,8 @@ object Erasure {
*/
def transformInfo(sym: Symbol, tp: Type)(implicit ctx: Context): Type = {
val erase = erasureFn(sym is JavaDefined, isSemi = true, sym.isConstructor, wildcardOK = false)
- if ((sym eq defn.Object_asInstanceOf) ||
- (sym eq defn.Object_isInstanceOf) ||
+ if ((sym eq defn.Any_asInstanceOf) ||
+ (sym eq defn.Any_isInstanceOf) ||
(sym.owner eq defn.ArrayClass) && (sym.isType || sym.isConstructor)) sym.info
else if (sym.isAbstractType) TypeAlias(WildcardType)
else erase(tp)
diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala
index 791df341a..9473bf10a 100644
--- a/src/dotty/tools/dotc/transform/Erasure.scala
+++ b/src/dotty/tools/dotc/transform/Erasure.scala
@@ -33,7 +33,10 @@ class Erasure extends Phase with DenotTransformer {
def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ref match {
case ref: SymDenotation =>
assert(ctx.phase == this, s"transforming $ref at ${ctx.phase}")
- ref.copySymDenotation(info = transformInfo(ref.symbol, ref.info))
+ val owner = ref.owner
+ ref.copySymDenotation(
+ owner = if (owner eq defn.AnyClass) defn.ObjectClass else owner,
+ info = transformInfo(ref.symbol, ref.info))
case ref =>
ref.derivedSingleDenotation(ref.symbol, erasure(ref.info))
}
@@ -136,7 +139,7 @@ object Erasure {
cast(runtimeCall(nme.toObjectArray, tree :: Nil), pt)
case _ =>
ctx.log(s"casting from ${tree.showSummary}: ${tree.tpe.show} to ${pt.show}")
- TypeApply(Select(tree, defn.Object_asInstanceOf), TypeTree(pt) :: Nil)
+ TypeApply(Select(tree, defn.Any_asInstanceOf), TypeTree(pt) :: Nil)
}
/** Adaptation of an expression `e` to an expected type `PT`, applying the following
diff --git a/src/dotty/tools/dotc/transform/InterceptedMethods.scala b/src/dotty/tools/dotc/transform/InterceptedMethods.scala
index d5a4377d0..f5fed6fda 100644
--- a/src/dotty/tools/dotc/transform/InterceptedMethods.scala
+++ b/src/dotty/tools/dotc/transform/InterceptedMethods.scala
@@ -28,6 +28,10 @@ import dotty.tools.dotc.core.Denotations.SingleDenotation
import dotty.tools.dotc.core.SymDenotations.SymDenotation
import StdNames._
+// @DarkDimius The getClass scheme changed. We no longer can have
+// two different methods in Any and Object. The tests pass but I
+// am not sure Intercepted methods treats getClass right now.
+// Please check and delete comment when done.
/** Replace member references as follows:
*
* - `x == y` for == in class Any becomes `x equals y` with equals in class Object.
@@ -50,12 +54,10 @@ class InterceptedMethods extends TreeTransform {
/** perform context-dependant initialization */
override def init(implicit ctx: Context, info: TransformerInfo): Unit = {
- getClassMethods = Set(defn.Any_getClass, defn.AnyVal_getClass)
- poundPoundMethods = Set(defn.Any_##, defn.Object_##)
+ poundPoundMethods = Set(defn.Any_##)
Any_comparisons = Set(defn.Any_==, defn.Any_!=)
- interceptedMethods = getClassMethods ++ poundPoundMethods ++ Any_comparisons
- primitiveGetClassMethods = Set[Symbol](defn.Any_getClass, defn.AnyVal_getClass) ++
- defn.ScalaValueClasses.map(x => x.requiredMethod(nme.getClass_))
+ interceptedMethods = poundPoundMethods ++ Any_comparisons
+ primitiveGetClassMethods = Set[Symbol]() ++ defn.ScalaValueClasses.map(x => x.requiredMethod(nme.getClass_))
}
// this should be removed if we have guarantee that ## will get Apply node
@@ -97,7 +99,7 @@ class InterceptedMethods extends TreeTransform {
override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = {
def unknown = {
- assert(false, s"The symbol '${tree.fun.symbol}' was interecepted but didn't match any cases, " +
+ assert(false, s"The symbol '${tree.fun.symbol.showLocated}' was intercepted but didn't match any cases, " +
s"that means the intercepted methods set doesn't match the code")
tree
}
@@ -109,9 +111,9 @@ class InterceptedMethods extends TreeTransform {
PoundPoundValue(qual)
} else if (Any_comparisons contains tree.fun.symbol.asTerm) {
if (tree.fun.symbol eq defn.Any_==) {
- Apply(Select(qual, defn.Object_equals.termRef), tree.args)
+ Apply(Select(qual, defn.Any_equals), tree.args)
} else if (tree.fun.symbol eq defn.Any_!=) {
- Select(Apply(Select(qual, defn.Object_equals.termRef), tree.args), defn.Boolean_!.termRef)
+ Select(Apply(Select(qual, defn.Any_equals), tree.args), defn.Boolean_!)
} else unknown
} /* else if (isPrimitiveValueClass(qual.tpe.typeSymbol)) {
// todo: this is needed to support value classes
@@ -128,7 +130,7 @@ class InterceptedMethods extends TreeTransform {
// we get a primitive form of _getClass trying to target a boxed value
// so we need replace that method name with Object_getClass to get correct behavior.
// See SI-5568.
- Apply(Select(qual, defn.Object_getClass.termRef), Nil)
+ Apply(Select(qual, defn.Any_getClass), Nil)
} else {
unknown
}
diff --git a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala
index a36bf6500..5f65ee414 100644
--- a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala
+++ b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala
@@ -68,10 +68,10 @@ class TypeTestsCasts extends TreeTransform {
runtimeCall(nme.isArray, arg :: Literal(Constant(ndims)) :: Nil)
if (ndims == 1) isArrayTest(qual)
else evalOnce(qual) { qual1 =>
- mkAnd(derivedTree(qual1, defn.Object_isInstanceOf, qual1.tpe), isArrayTest(qual1))
+ mkAnd(derivedTree(qual1, defn.Any_isInstanceOf, qual1.tpe), isArrayTest(qual1))
}
case _ =>
- derivedTree(expr, defn.Object_isInstanceOf, argType)
+ derivedTree(expr, defn.Any_isInstanceOf, argType)
}
}
@@ -81,10 +81,10 @@ class TypeTestsCasts extends TreeTransform {
else if (qualCls.isPrimitiveValueClass) {
val argCls = argType.classSymbol
if (argCls.isPrimitiveValueClass) primitiveConversion(qual, argCls)
- else derivedTree(box(qual), defn.Object_asInstanceOf, argType)
+ else derivedTree(box(qual), defn.Any_asInstanceOf, argType)
}
else
- derivedTree(qual, defn.Object_asInstanceOf, argType)
+ derivedTree(qual, defn.Any_asInstanceOf, argType)
}
if (sym eq defn.Any_isInstanceOf)