Skip to content

Commit a3f6671

Browse files
committed
Handle constant-folding applied getClass
Any#getClass is a polymorphic nilary method, handle this properly in ConstFold instead of folding just the selection, this could not be done when the constant-folding of getClass was first introduced two commits ago due to the weird overloading of getClass, but that has been take care of by the previous commit.
1 parent b6af494 commit a3f6671

File tree

5 files changed

+47
-18
lines changed

5 files changed

+47
-18
lines changed

compiler/src/dotty/tools/dotc/ast/TreeInfo.scala

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,11 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
513513
*
514514
* { pre; v }
515515
*
516+
* (3) An expression `pre.getClass[..]()` that has a constant type `ConstantType(v)` but where
517+
* `pre` has side-effects is translated to:
518+
*
519+
* { pre; v }
520+
*
516521
* This avoids the situation where we have a Select node that does not have a symbol.
517522
*/
518523
def constToLiteral(tree: Tree)(implicit ctx: Context): Tree = {
@@ -524,12 +529,19 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
524529
tree1
525530
case ConstantType(value) =>
526531
if (isIdempotentExpr(tree1)) Literal(value).withSpan(tree.span)
527-
else tree1 match {
528-
case Select(qual, _) if tree1.tpe.isInstanceOf[ConstantType] =>
529-
// it's a primitive unary operator; Simplify `pre.op` to `{ pre; v }` where `v` is the value of `pre.op`
530-
Block(qual :: Nil, Literal(value)).withSpan(tree.span)
531-
case _ =>
532-
tree1
532+
else {
533+
def keepPrefix(pre: Tree) =
534+
Block(pre :: Nil, Literal(value)).withSpan(tree.span)
535+
536+
tree1 match {
537+
case Select(pre, _) if tree1.tpe.isInstanceOf[ConstantType] =>
538+
// it's a primitive unary operator; Simplify `pre.op` to `{ pre; v }` where `v` is the value of `pre.op`
539+
keepPrefix(pre)
540+
case Apply(TypeApply(Select(pre, nme.getClass_), _), Nil) =>
541+
keepPrefix(pre)
542+
case _ =>
543+
tree1
544+
}
533545
}
534546
case _ => tree1
535547
}

compiler/src/dotty/tools/dotc/typer/ConstFold.scala

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,15 @@ object ConstFold {
2929
case _ => null
3030
case _ => null
3131
case Select(xt, op) =>
32-
if (op eq nme.getClass_) && xt.tpe.widen.isPrimitiveValueType then
33-
Constant(xt.tpe.widen)
34-
else
35-
xt.tpe.widenTermRefExpr match {
36-
case ConstantType(x) => foldUnop(op, x)
37-
case _ => null
38-
}
32+
xt.tpe.widenTermRefExpr match {
33+
case ConstantType(x) => foldUnop(op, x)
34+
case _ => null
35+
}
3936
case TypeApply(_, List(targ)) if tree.symbol eq defn.Predef_classOf =>
4037
Constant(targ.tpe)
38+
case Apply(TypeApply(Select(qual, nme.getClass_), _), Nil)
39+
if qual.tpe.widen.isPrimitiveValueType =>
40+
Constant(qual.tpe.widen)
4141
case _ => null
4242
}
4343
}

compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -827,20 +827,25 @@ class TestBCode extends DottyBytecodeTest {
827827
val source = """
828828
|class Foo {
829829
| def sideEffect(): Int = { println("hi"); 1 }
830-
| def before: Class[Int] = sideEffect().getClass
830+
| def before1: Class[Int] = sideEffect().getClass
831+
| def before2: Class[Int] = sideEffect().getClass[Int]
831832
| def after: Class[Int] = { sideEffect(); classOf[Int] }
832833
|}
833834
""".stripMargin
834835

835836
checkBCode(source) { dir =>
836837
val clsIn = dir.lookupName("Foo.class", directory = false).input
837838
val clsNode = loadClassNode(clsIn)
838-
val before = instructionsFromMethod(getMethod(clsNode, "before"))
839+
val before1 = instructionsFromMethod(getMethod(clsNode, "before1"))
840+
val before2 = instructionsFromMethod(getMethod(clsNode, "before2"))
839841
val after = instructionsFromMethod(getMethod(clsNode, "after"))
840842

841-
assert(before == after,
843+
assert(before1 == after,
842844
"`before1` was not translated to the same bytecode as `after`\n" +
843-
diffInstructions(before, after))
845+
diffInstructions(before1, after))
846+
assert(before2 == after,
847+
"`before2` was not translated to the same bytecode as `after`\n" +
848+
diffInstructions(before2, after))
844849
}
845850
}
846851

tests/run/t5568.check

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
void
2+
void
3+
int
24
int
35
class scala.runtime.BoxedUnit
46
class scala.runtime.BoxedUnit
7+
class scala.runtime.BoxedUnit
8+
class scala.runtime.BoxedUnit
9+
class java.lang.Integer
10+
class java.lang.Integer
511
class java.lang.Integer
612
class java.lang.Integer
713
5

tests/run/t5568.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,18 @@ object Test {
22
def main(args: Array[String]): Unit = {
33
// these should give unboxed results
44
println(().getClass)
5+
println(().getClass[Unit])
56
println(5.getClass)
6-
// these should give boxed results
7+
println(5.getClass[Int])
8+
// these should give boxed results
79
println(().asInstanceOf[AnyRef with Unit].getClass)
10+
println(().asInstanceOf[AnyRef with Unit].getClass[Any])
811
println(().asInstanceOf[Unit with AnyRef].getClass)
12+
println(().asInstanceOf[Unit with AnyRef].getClass[Any])
913
println(5.asInstanceOf[AnyRef with Int].getClass)
14+
println(5.asInstanceOf[AnyRef with Int].getClass[Any])
1015
println(5.asInstanceOf[Int with AnyRef].getClass)
16+
println(5.asInstanceOf[Int with AnyRef].getClass[Any])
1117
//make sure ## wasn't broken
1218
println(5.##)
1319
println((5.asInstanceOf[AnyRef]).##)

0 commit comments

Comments
 (0)