diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 4886bfe0eb32..c95201086200 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -153,10 +153,12 @@ class Definitions { tl => op(tl.paramRefs(0), tl.paramRefs(1)))) private def enterPolyMethod(cls: ClassSymbol, name: TermName, typeParamCount: Int, - resultTypeFn: PolyType => Type, flags: FlagSet = EmptyFlags, + resultTypeFn: PolyType => Type, + flags: FlagSet = EmptyFlags, + bounds: TypeBounds = TypeBounds.empty, useCompleter: Boolean = false) = { val tparamNames = PolyType.syntheticParamNames(typeParamCount) - val tparamInfos = tparamNames map (_ => TypeBounds.empty) + val tparamInfos = tparamNames map (_ => bounds) def ptype = PolyType(tparamNames)(_ => tparamInfos, resultTypeFn) val info = if (useCompleter) @@ -244,36 +246,32 @@ class Definitions { * - 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] = ??? - * } */ @tu lazy val AnyClass: ClassSymbol = completeClass(enterCompleteClassSymbol(ScalaPackageClass, tpnme.Any, Abstract, Nil), ensureCtor = false) def AnyType: TypeRef = AnyClass.typeRef @tu lazy val AnyValClass: ClassSymbol = completeClass(enterCompleteClassSymbol(ScalaPackageClass, tpnme.AnyVal, Abstract, List(AnyClass.typeRef))) def AnyValType: TypeRef = AnyValClass.typeRef - @tu lazy val Any_== : TermSymbol = enterMethod(AnyClass, nme.EQ, methOfAny(BooleanType), Final) - @tu lazy val Any_!= : TermSymbol = enterMethod(AnyClass, nme.NE, methOfAny(BooleanType), Final) - @tu lazy val Any_equals: TermSymbol = enterMethod(AnyClass, nme.equals_, methOfAny(BooleanType)) - @tu lazy val Any_hashCode: TermSymbol = enterMethod(AnyClass, nme.hashCode_, MethodType(Nil, IntType)) - @tu lazy val Any_toString: TermSymbol = enterMethod(AnyClass, nme.toString_, MethodType(Nil, StringType)) - @tu lazy val Any_## : TermSymbol = enterMethod(AnyClass, nme.HASHHASH, ExprType(IntType), Final) - @tu lazy val Any_getClass: TermSymbol = enterMethod(AnyClass, nme.getClass_, MethodType(Nil, ClassClass.typeRef.appliedTo(TypeBounds.empty)), Final) + @tu lazy val Any_== : TermSymbol = enterMethod(AnyClass, nme.EQ, methOfAny(BooleanType), Final) + @tu lazy val Any_!= : TermSymbol = enterMethod(AnyClass, nme.NE, methOfAny(BooleanType), Final) + @tu lazy val Any_equals: TermSymbol = enterMethod(AnyClass, nme.equals_, methOfAny(BooleanType)) + @tu lazy val Any_hashCode: TermSymbol = enterMethod(AnyClass, nme.hashCode_, MethodType(Nil, IntType)) + @tu lazy val Any_toString: TermSymbol = enterMethod(AnyClass, nme.toString_, MethodType(Nil, StringType)) + @tu lazy val Any_## : TermSymbol = enterMethod(AnyClass, nme.HASHHASH, ExprType(IntType), Final) @tu lazy val Any_isInstanceOf: TermSymbol = enterT1ParameterlessMethod(AnyClass, nme.isInstanceOf_, _ => BooleanType, Final) @tu lazy val Any_asInstanceOf: TermSymbol = enterT1ParameterlessMethod(AnyClass, nme.asInstanceOf_, _.paramRefs(0), Final) - @tu lazy val Any_typeTest: TermSymbol = enterT1ParameterlessMethod(AnyClass, nme.isInstanceOfPM, _ => BooleanType, Final | Synthetic | Artifact) - @tu lazy val Any_typeCast: TermSymbol = enterT1ParameterlessMethod(AnyClass, nme.asInstanceOfPM, _.paramRefs(0), Final | Synthetic | Artifact | StableRealizable) + @tu lazy val Any_typeTest: TermSymbol = enterT1ParameterlessMethod(AnyClass, nme.isInstanceOfPM, _ => BooleanType, Final | Synthetic | Artifact) + @tu lazy val Any_typeCast: TermSymbol = enterT1ParameterlessMethod(AnyClass, nme.asInstanceOfPM, _.paramRefs(0), Final | Synthetic | Artifact | StableRealizable) // generated by pattern matcher, eliminated by erasure + /** def getClass[A >: this.type](): Class[? <: A] */ + @tu lazy val Any_getClass: TermSymbol = + enterPolyMethod( + AnyClass, nme.getClass_, 1, + pt => MethodType(Nil, ClassClass.typeRef.appliedTo(TypeBounds.upper(pt.paramRefs(0)))), + Final, + bounds = TypeBounds.lower(AnyClass.thisType)) + def AnyMethods: List[TermSymbol] = List(Any_==, Any_!=, Any_equals, Any_hashCode, Any_toString, Any_##, Any_getClass, Any_isInstanceOf, Any_asInstanceOf, Any_typeTest, Any_typeCast) diff --git a/compiler/src/dotty/tools/dotc/transform/InterceptedMethods.scala b/compiler/src/dotty/tools/dotc/transform/InterceptedMethods.scala index 5b3ba45e1850..be456b9b8e27 100644 --- a/compiler/src/dotty/tools/dotc/transform/InterceptedMethods.scala +++ b/compiler/src/dotty/tools/dotc/transform/InterceptedMethods.scala @@ -21,7 +21,6 @@ object InterceptedMethods { * - `x.##` for ## in NullClass becomes `0` * - `x.##` for ## in Any becomes calls to ScalaRunTime.hash, * using the most precise overload available - * - `x.getClass` for getClass in primitives becomes `x.getClass` with getClass in class Object. */ class InterceptedMethods extends MiniPhase { import tpd._ @@ -62,11 +61,6 @@ class InterceptedMethods extends MiniPhase { } override def transformApply(tree: Apply)(implicit ctx: Context): Tree = { - def unknown = { - 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 - } lazy val qual = tree.fun match { case Select(qual, _) => qual case ident @ Ident(_) => @@ -78,32 +72,9 @@ class InterceptedMethods extends MiniPhase { } } - val Any_!= = defn.Any_!= - val rewritten: Tree = tree.fun.symbol match { - case Any_!= => - qual.select(defn.Any_==).appliedToArgs(tree.args).select(defn.Boolean_!).withSpan(tree.span) - /* - /* else if (isPrimitiveValueClass(qual.tpe.typeSymbol)) { - // todo: this is needed to support value classes - // Rewrite 5.getClass to ScalaRunTime.anyValClass(5) - global.typer.typed(gen.mkRuntimeCall(nme.anyValClass, - List(qual, typer.resolveClassTag(tree.pos, qual.tpe.widen)))) - }*/ - */ - case t if t.name == nme.getClass_ && defn.ScalaValueClasses().contains(t.owner) => - // if we got here then we're trying to send a primitive getClass method to either - // a) an Any, in which cage Object_getClass works because Any erases to object. Or - // - // b) a non-primitive, e.g. because the qualifier's type is a refinement type where one parent - // of the refinement is a primitive and another is AnyRef. In that case - // 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. - qual.selectWithSig(defn.Any_getClass).appliedToNone.withSpan(tree.span) - case _ => - tree - } - ctx.log(s"$phaseName rewrote $tree to $rewritten") - rewritten + if tree.fun.symbol == defn.Any_!= then + qual.select(defn.Any_==).appliedToArgs(tree.args).select(defn.Boolean_!).withSpan(tree.span) + else + tree } } diff --git a/compiler/test-resources/repl/getClass b/compiler/test-resources/repl/getClass index 7b3d0bcedd09..6591129a0be9 100644 --- a/compiler/test-resources/repl/getClass +++ b/compiler/test-resources/repl/getClass @@ -1,4 +1,5 @@ scala> val xs = List(1) val xs: List[Int] = List(1) scala> xs.getClass -val res0: Class[?] = class scala.collection.immutable.$colon$colon +val res0: Class[? <: List[Int]] = class scala.collection.immutable.$colon$colon + diff --git a/tests/pending/run/t5568.check b/tests/pending/run/t5568.check deleted file mode 100644 index 67aaf16e079f..000000000000 --- a/tests/pending/run/t5568.check +++ /dev/null @@ -1,9 +0,0 @@ -void -int -class scala.runtime.BoxedUnit -class scala.runtime.BoxedUnit -class java.lang.Integer -class java.lang.Integer -5 -5 -5 diff --git a/tests/pending/run/t5568.flags b/tests/pending/run/t5568.flags deleted file mode 100644 index ad51758c3932..000000000000 --- a/tests/pending/run/t5568.flags +++ /dev/null @@ -1 +0,0 @@ --nowarn diff --git a/tests/pos/i3495.scala b/tests/pos/i3495.scala new file mode 100644 index 000000000000..e77bcc5e1630 --- /dev/null +++ b/tests/pos/i3495.scala @@ -0,0 +1,13 @@ +class A + +object Test { + val x: A = new A() + + def y = x.getClass + + val z: Class[? <: A] = y + + 1.getClass + +} + diff --git a/tests/pos/t1107b/T.scala b/tests/pos/t1107b/T.scala index 0dff0b94fdc3..5889d43548a0 100644 --- a/tests/pos/t1107b/T.scala +++ b/tests/pos/t1107b/T.scala @@ -2,6 +2,6 @@ sealed trait Top sealed trait Sub extends Top trait C { private object P extends Sub - def bob() = P.getClass + def bob(): Class[_] = P.getClass def bob2() = O.d(P) } diff --git a/tests/run/t5568.check b/tests/run/t5568.check new file mode 100644 index 000000000000..90c75aafaa43 --- /dev/null +++ b/tests/run/t5568.check @@ -0,0 +1,9 @@ +void +int +void +void +int +int +5 +5 +5 diff --git a/tests/pending/run/t5568.scala b/tests/run/t5568.scala similarity index 100% rename from tests/pending/run/t5568.scala rename to tests/run/t5568.scala