Skip to content

Commit ceccb9e

Browse files
authored
Merge pull request #9414 from dotty-staging/fix-#9413
2 parents 0f1a23e + b205858 commit ceccb9e

File tree

3 files changed

+57
-6
lines changed

3 files changed

+57
-6
lines changed

compiler/src/dotty/tools/dotc/transform/TreeExtractors.scala

+5-1
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,15 @@ object TreeExtractors {
1919
}
2020
}
2121

22-
/** Match new C(args) and extract (C, args) */
22+
/** Match new C(args) and extract (C, args).
23+
* Also admit new C(args): T and {new C(args)}.
24+
*/
2325
object NewWithArgs {
2426
def unapply(t: Tree)(using Context): Option[(Type, List[Tree])] = t match {
2527
case Apply(Select(New(_), nme.CONSTRUCTOR), args) =>
2628
Some((t.tpe, args))
29+
case Typed(expr, _) => unapply(expr)
30+
case Block(Nil, expr) => unapply(expr)
2731
case _ =>
2832
None
2933
}

compiler/src/dotty/tools/dotc/transform/VCElideAllocations.scala

+11-5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package transform
33

44
import ast.tpd
55
import core._
6-
import Contexts._, Symbols._
6+
import Contexts._, Symbols._, Types._, Flags._, Phases._
77
import DenotTransformers._, MegaPhase._
88
import TreeExtractors._, ValueClasses._
99

@@ -23,13 +23,19 @@ class VCElideAllocations extends MiniPhase with IdentityDenotTransformer {
2323
override def runsAfter: Set[String] = Set(ElimErasedValueType.name)
2424

2525
override def transformApply(tree: Apply)(using Context): Tree =
26+
def hasUserDefinedEquals(tp: Type): Boolean =
27+
val eql = atPhase(erasurePhase) {
28+
defn.Any_equals.matchingMember(tp.typeSymbol.thisType)
29+
}
30+
eql.owner != defn.AnyClass && !eql.is(Synthetic)
31+
2632
tree match {
27-
// new V(u1) == new V(u2) => u1 == u2
33+
// new V(u1) == new V(u2) => u1 == u2, unless V defines its own equals.
2834
// (We don't handle != because it has been eliminated by InterceptedMethods)
2935
case BinaryOp(NewWithArgs(tp1, List(u1)), op, NewWithArgs(tp2, List(u2)))
30-
if (tp1 eq tp2) && (op eq defn.Any_==) &&
31-
isDerivedValueClass(tp1.typeSymbol) &&
32-
!defn.Any_equals.overridingSymbol(tp1.typeSymbol.asClass).exists =>
36+
if (tp1 eq tp2) && (op eq defn.Any_==)
37+
&& isDerivedValueClass(tp1.typeSymbol)
38+
&& !hasUserDefinedEquals(tp1) =>
3339
// == is overloaded in primitive classes
3440
u1.equal(u2)
3541

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

+41
Original file line numberDiff line numberDiff line change
@@ -976,6 +976,47 @@ class TestBCode extends DottyBytecodeTest {
976976
assert((getMethod(c, "x_$eq").access & Opcodes.ACC_DEPRECATED) != 0)
977977
}
978978
}
979+
980+
@Test def vcElideAllocations = {
981+
val source =
982+
s"""class ApproxState(val bits: Int) extends AnyVal
983+
|class Foo {
984+
| val FreshApprox: ApproxState = new ApproxState(4)
985+
| var approx: ApproxState = FreshApprox
986+
| def meth1: Boolean = approx == FreshApprox
987+
| def meth2: Boolean = (new ApproxState(4): ApproxState) == FreshApprox
988+
|}
989+
""".stripMargin
990+
991+
checkBCode(source) { dir =>
992+
val clsIn = dir.lookupName("Foo.class", directory = false).input
993+
val clsNode = loadClassNode(clsIn)
994+
val meth1 = getMethod(clsNode, "meth1")
995+
val meth2 = getMethod(clsNode, "meth2")
996+
val instructions1 = instructionsFromMethod(meth1)
997+
val instructions2 = instructionsFromMethod(meth2)
998+
999+
val isFrameLine = (x: Instruction) => x.isInstanceOf[FrameEntry] || x.isInstanceOf[LineNumber]
1000+
1001+
// No allocations of ApproxState
1002+
1003+
assertSameCode(instructions1.filterNot(isFrameLine), List(
1004+
VarOp(ALOAD, 0), Invoke(INVOKEVIRTUAL, "Foo", "approx", "()I", false),
1005+
VarOp(ALOAD, 0), Invoke(INVOKEVIRTUAL, "Foo", "FreshApprox", "()I", false),
1006+
Jump(IF_ICMPNE, Label(7)), Op(ICONST_1),
1007+
Jump(GOTO, Label(10)),
1008+
Label(7), Op(ICONST_0),
1009+
Label(10), Op(IRETURN)))
1010+
1011+
assertSameCode(instructions2.filterNot(isFrameLine), List(
1012+
Op(ICONST_4),
1013+
VarOp(ALOAD, 0), Invoke(INVOKEVIRTUAL, "Foo", "FreshApprox", "()I", false),
1014+
Jump(IF_ICMPNE, Label(6)), Op(ICONST_1),
1015+
Jump(GOTO, Label(9)),
1016+
Label(6), Op(ICONST_0),
1017+
Label(9), Op(IRETURN)))
1018+
}
1019+
}
9791020
}
9801021

9811022
object invocationReceiversTestCode {

0 commit comments

Comments
 (0)