Skip to content

Commit 668860c

Browse files
committed
Fix deterministically adding additional interfaces
When a class contains calls to 'super' for traits it does not directly implement, these are added to the list of interfaces of the generated class. Previously, because these interfaces were determined using set logic, the ordering of that list was not deterministic. This change makes the order deterministic (assuming the order in which these calls are registered using `registerSuperCall` in the `CollectSuperCalls` phase is deterministic within each class) Fixes #20496
1 parent c1b25d6 commit 668860c

File tree

4 files changed

+33
-7
lines changed

4 files changed

+33
-7
lines changed

Diff for: compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala

+5-3
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,13 @@ class BTypesFromSymbols[I <: DottyBackendInterface](val int: I, val frontendAcce
113113
val directlyInheritedTraits = sym.directlyInheritedTraits
114114
val directlyInheritedTraitsSet = directlyInheritedTraits.toSet
115115
val allBaseClasses = directlyInheritedTraits.iterator.flatMap(_.asClass.baseClasses.drop(1)).toSet
116-
val superCalls = superCallsMap.getOrElse(sym, Set.empty)
117-
val additional = (superCalls -- directlyInheritedTraitsSet).filter(_.is(Trait))
116+
val superCalls = superCallsMap.getOrElse(sym, List.empty)
117+
val superCallsSet = superCalls.toSet
118+
val additional = superCalls.foldLeft(List())((acc, t) =>
119+
if !acc.contains(t) && !directlyInheritedTraitsSet(t) && t.is(Trait) then acc :+ t else acc)
118120
// if (additional.nonEmpty)
119121
// println(s"$fullName: adding supertraits $additional")
120-
directlyInheritedTraits.filter(t => !allBaseClasses(t) || superCalls(t)) ++ additional
122+
directlyInheritedTraits.filter(t => !allBaseClasses(t) || superCallsSet(t)) ++ additional
121123
}
122124

123125
val interfaces = classSym.superInterfaces.map(classBTypeFromSymbol)

Diff for: compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import StdNames.nme
2525
import NameKinds.{LazyBitMapName, LazyLocalName}
2626
import Names.Name
2727

28-
class DottyBackendInterface(val superCallsMap: ReadOnlyMap[Symbol, Set[ClassSymbol]])(using val ctx: Context) {
28+
class DottyBackendInterface(val superCallsMap: ReadOnlyMap[Symbol, List[ClassSymbol]])(using val ctx: Context) {
2929

3030
private val desugared = new java.util.IdentityHashMap[Type, tpd.Select]
3131

Diff for: compiler/src/dotty/tools/backend/jvm/GenBCode.scala

+3-3
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ class GenBCode extends Phase { self =>
2323

2424
override def isRunnable(using Context) = super.isRunnable && !ctx.usedBestEffortTasty
2525

26-
private val superCallsMap = new MutableSymbolMap[Set[ClassSymbol]]
26+
private val superCallsMap = new MutableSymbolMap[List[ClassSymbol]]
2727
def registerSuperCall(sym: Symbol, calls: ClassSymbol): Unit = {
28-
val old = superCallsMap.getOrElse(sym, Set.empty)
29-
superCallsMap.update(sym, old + calls)
28+
val old = superCallsMap.getOrElse(sym, List.empty)
29+
superCallsMap.update(sym, old :+ calls)
3030
}
3131

3232
private val entryPoints = new mutable.HashSet[String]()

Diff for: compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala

+24
Original file line numberDiff line numberDiff line change
@@ -1963,6 +1963,30 @@ class DottyBytecodeTests extends DottyBytecodeTest {
19631963
assertSameCode(instructions, expected)
19641964
}
19651965
}
1966+
1967+
/**
1968+
* Test 'additional' imports are generated in deterministic order
1969+
* https://github.com/scala/scala3/issues/20496
1970+
*/
1971+
@Test def deterministicAdditionalImports = {
1972+
val source =
1973+
"""trait Actor:
1974+
| def receive() = ()
1975+
|trait Timers:
1976+
| def timers() = ()
1977+
|abstract class ShardCoordinator extends Actor with Timers
1978+
|class PersistentShardCoordinator extends ShardCoordinator:
1979+
| def foo =
1980+
| super.receive()
1981+
| super.timers()""".stripMargin
1982+
checkBCode(source) { dir =>
1983+
val clsIn = dir.lookupName("PersistentShardCoordinator.class", directory = false).input
1984+
val clsNode = loadClassNode(clsIn)
1985+
1986+
val expected = List("Actor", "Timers")
1987+
assertEquals(expected, clsNode.interfaces.asScala)
1988+
}
1989+
}
19661990
}
19671991

19681992
object invocationReceiversTestCode {

0 commit comments

Comments
 (0)