Skip to content

Commit 468ff9c

Browse files
committed
Fix #1209: Skip redundant superclasses\supertraits.
1 parent bef40b4 commit 468ff9c

File tree

6 files changed

+97
-5
lines changed

6 files changed

+97
-5
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package dotty.tools.backend.jvm
2+
3+
import dotty.tools.dotc.ast.tpd._
4+
import dotty.tools.dotc.ast.Trees
5+
import dotty.tools.dotc.core.Contexts.Context
6+
import dotty.tools.dotc.core.Symbols._
7+
import dotty.tools.dotc.transform.TreeTransforms.{MiniPhaseTransform, TransformerInfo}
8+
9+
/** Collect all super calls except to the parent class.
10+
*
11+
* This information is used to know if it is safe to remove a redundant mixin class.
12+
* A redundant mixin class is one that is implemented by another mixin class. As the
13+
* methods in a redundant mixin class could be implemented with a default abstract method,
14+
* the redundant mixin class could be required as a parent by the JVM.
15+
*/
16+
class CollectSuperCalls extends MiniPhaseTransform {
17+
18+
def phaseName: String = "collectSuperCalls"
19+
20+
override def transformSuper(tree: Super)(implicit ctx: Context, info: TransformerInfo): Tree = {
21+
tree match {
22+
case Trees.Super(qual: This, mix) if mix.nonEmpty =>
23+
val classSymbol = qual.symbol.asClass.classSymbol
24+
registerSuperCall(classSymbol, tree.tpe.baseClasses.head)
25+
case _ =>
26+
}
27+
super.transformSuper(tree)
28+
}
29+
30+
private def registerSuperCall(sym: ClassSymbol, calls: ClassSymbol)(implicit ctx: Context) = {
31+
ctx.genBCodePhase match {
32+
case genBCodePhase: GenBCode =>
33+
genBCodePhase.registerSuperCall(sym, calls)
34+
case _ =>
35+
}
36+
}
37+
}

src/dotty/tools/backend/jvm/DottyBackendInterface.scala

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,11 @@ import NameOps._
3434
import StdNames.nme
3535
import NameOps._
3636
import dotty.tools.dotc.core
37+
import dotty.tools.dotc.core.Names.TypeName
3738

38-
class DottyBackendInterface(outputDirectory: AbstractFile)(implicit ctx: Context) extends BackendInterface{
39+
import scala.annotation.tailrec
40+
41+
class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Map[Symbol, Set[ClassSymbol]])(implicit ctx: Context) extends BackendInterface{
3942
type Symbol = Symbols.Symbol
4043
type Type = Types.Type
4144
type Tree = tpd.Tree
@@ -738,9 +741,14 @@ class DottyBackendInterface(outputDirectory: AbstractFile)(implicit ctx: Context
738741

739742
/**
740743
* All interfaces implemented by a class, except for those inherited through the superclass.
741-
*
744+
* Redundant interfaces are removed unless there is a super call to them.
742745
*/
743-
def superInterfaces: List[Symbol] = decorateSymbol(sym).directlyInheritedTraits
746+
def superInterfaces: List[Symbol] = {
747+
val directlyInheritedTraits = decorateSymbol(sym).directlyInheritedTraits
748+
val allBaseClasses = directlyInheritedTraits.iterator.flatMap(_.symbol.asClass.baseClasses.drop(1)).toSet
749+
val superCalls = superCallsMap.getOrElse(sym, Set.empty)
750+
directlyInheritedTraits.filter(t => !allBaseClasses(t) || superCalls(t))
751+
}
744752

745753
/**
746754
* True for module classes of package level objects. The backend will generate a mirror class for

src/dotty/tools/backend/jvm/GenBCode.scala

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import dotty.tools.dotc.CompilationUnit
44
import dotty.tools.dotc.ast.Trees.{ValDef, PackageDef}
55
import dotty.tools.dotc.ast.tpd
66
import dotty.tools.dotc.core.Phases.Phase
7+
import dotty.tools.dotc.core.Names.TypeName
78

89
import scala.collection.mutable
910
import scala.tools.asm.{CustomAttr, ClassVisitor, MethodVisitor, FieldVisitor}
@@ -41,11 +42,18 @@ class GenBCode extends Phase {
4142
private val entryPoints = new mutable.HashSet[Symbol]()
4243
def registerEntryPoint(sym: Symbol) = entryPoints += sym
4344

45+
private val superCallsMap = new mutable.HashMap[Symbol, Set[ClassSymbol]]()
46+
def registerSuperCall(sym: Symbol, calls: ClassSymbol) = {
47+
val old = superCallsMap.getOrElse(sym, Set.empty)
48+
superCallsMap.put(sym, old + calls)
49+
}
50+
4451
def outputDir(implicit ctx: Context): AbstractFile =
4552
new PlainDirectory(new Directory(new JFile(ctx.settings.d.value)))
4653

4754
def run(implicit ctx: Context): Unit = {
48-
new GenBCodePipeline(entryPoints.toList, new DottyBackendInterface(outputDir)(ctx))(ctx).run(ctx.compilationUnit.tpdTree)
55+
new GenBCodePipeline(entryPoints.toList,
56+
new DottyBackendInterface(outputDir, superCallsMap.toMap)(ctx))(ctx).run(ctx.compilationUnit.tpdTree)
4957
entryPoints.clear()
5058
}
5159
}

src/dotty/tools/dotc/Compiler.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import transform.TreeTransforms.{TreeTransform, TreeTransformer}
1515
import core.DenotTransformers.DenotTransformer
1616
import core.Denotations.SingleDenotation
1717

18-
import dotty.tools.backend.jvm.{LabelDefs, GenBCode}
18+
import dotty.tools.backend.jvm.{LabelDefs, GenBCode, CollectSuperCalls}
1919
import dotty.tools.backend.sjs.GenSJSIR
2020

2121
/** The central class of the dotc compiler. The job of a compiler is to create
@@ -92,6 +92,7 @@ class Compiler {
9292
new RestoreScopes), // Repair scopes rendered invalid by moving definitions in prior phases of the group
9393
List(new ExpandPrivate, // Widen private definitions accessed from nested classes
9494
new CollectEntryPoints, // Find classes with main methods
95+
new CollectSuperCalls, // Find classes that are called with super
9596
new MoveStatics, // Move static methods to companion classes
9697
new LabelDefs), // Converts calls to labels to jumps
9798
List(new GenSJSIR), // Generate .js code

tests/run/redundantParents.check

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
C1 super class: class java.lang.Object
2+
C1 interfaces: List(interface T2)
3+
C2 super class: class C1
4+
C2 interfaces: List(interface T4, interface T5)
5+
C3 super class: class C1
6+
C3 interfaces: List(interface T5)
7+
C4 super class: class C2
8+
C4 interfaces: List()

tests/run/redundantParents.scala

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
2+
trait T1
3+
trait T2 extends T1
4+
trait T3 extends T2
5+
trait T4 extends T3
6+
7+
trait T5
8+
9+
class C1 extends T2
10+
class C2 extends C1 with T4 with T5 with T1 with T2
11+
class C3 extends C1 with T5
12+
class C4 extends C2 with T5
13+
14+
object Test {
15+
def main(args: Array[String]): Unit = {
16+
val c1 = (new C1).getClass
17+
val c2 = (new C2).getClass
18+
val c3 = (new C3).getClass
19+
val c4 = (new C4).getClass
20+
21+
println("C1 super class: " + c1.getSuperclass)
22+
println("C1 interfaces: " + c1.getInterfaces.toList)
23+
println("C2 super class: " + c2.getSuperclass)
24+
println("C2 interfaces: " + c2.getInterfaces.toList)
25+
println("C3 super class: " + c3.getSuperclass)
26+
println("C3 interfaces: " + c3.getInterfaces.toList)
27+
println("C4 super class: " + c4.getSuperclass)
28+
println("C4 interfaces: " + c4.getInterfaces.toList)
29+
}
30+
}

0 commit comments

Comments
 (0)