Skip to content

Commit b93d83c

Browse files
committed
Fix unstable compiler output due to using Set
`membersBasedOnFlags` internally uses `memberNames` which returns a Set, so the order of its elements is unstable. We were using it when generating static forwarder methods which lead to some idempotency tests failing (no idea why this didn't manifest itself before). Fixed by adding a new `sortedMembersBasedOnFlags` which sorts members by names and signatures (this required adding a somewhat arbitrary lexicographic ordering to Signature).
1 parent 960498b commit b93d83c

File tree

4 files changed

+46
-4
lines changed

4 files changed

+46
-4
lines changed

compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
387387
}
388388
debuglog(s"Potentially conflicting names for forwarders: $conflictingNames")
389389

390-
for (m0 <- moduleClass.info.membersBasedOnFlags(ExcludedForwarderFlags, Flag_METHOD)) {
390+
for (m0 <- moduleClass.info.sortedMembersBasedOnFlags(required = Flag_METHOD, excluded = ExcludedForwarderFlags)) {
391391
val m = if (m0.isBridge) m0.nextOverriddenSymbol else m0
392392
if (m == NoSymbol)
393393
log(s"$m0 is a bridge method that overrides nothing, something went wrong in a previous phase.")

compiler/src/dotty/tools/backend/jvm/BackendInterface.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,11 @@ abstract class BackendInterface extends BackendInterfaceDefinitions {
602602
def params: List[Symbol]
603603
def resultType: Type
604604
def memberInfo(s: Symbol): Type
605-
def membersBasedOnFlags(excludedFlags: Flags, requiredFlags: Flags): List[Symbol]
605+
606+
/** The members of this type that have all of `required` flags but none of `excluded` flags set.
607+
* The members are sorted by name and signature to guarantee a stable ordering.
608+
*/
609+
def sortedMembersBasedOnFlags(required: Flags, excluded: Flags): List[Symbol]
606610
def members: List[Symbol]
607611
def decls: List[Symbol]
608612
def underlying: Type

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -878,8 +878,18 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
878878

879879
def =:=(other: Type): Boolean = tp =:= other
880880

881-
def membersBasedOnFlags(excludedFlags: Flags, requiredFlags: Flags): List[Symbol] =
882-
tp.membersBasedOnFlags(termFlagSet(requiredFlags), termFlagSet(excludedFlags)).map(_.symbol).toList
881+
def sortedMembersBasedOnFlags(required: Flags, excluded: Flags): List[Symbol] = {
882+
val requiredFlagSet = termFlagSet(required)
883+
val excludedFlagSet = termFlagSet(excluded)
884+
// The output of `memberNames` is a Set, sort it to guarantee a stable ordering.
885+
val names = tp.memberNames(takeAllFilter).toSeq.sorted
886+
val buffer = mutable.ListBuffer[Symbol]()
887+
names.foreach { name =>
888+
buffer ++= tp.memberBasedOnFlags(name, requiredFlagSet, excludedFlagSet)
889+
.alternatives.sortBy(_.signature)(Signature.lexicographicOrdering).map(_.symbol)
890+
}
891+
buffer.toList
892+
}
883893

884894
def resultType: Type = tp.resultType
885895

compiler/src/dotty/tools/dotc/core/Signature.scala

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,4 +158,32 @@ object Signature {
158158
assert(!resultType.isInstanceOf[ExprType])
159159
apply(Nil, sigName(resultType, isJava))
160160
}
161+
162+
val lexicographicOrdering: Ordering[Signature] = new Ordering[Signature] {
163+
val paramSigOrdering: Ordering[Signature.ParamSig] = new Ordering[Signature.ParamSig] {
164+
def compare(x: ParamSig, y: ParamSig): Int = x match { // `(x, y) match` leads to extra allocations
165+
case x: TypeName =>
166+
y match {
167+
case y: TypeName =>
168+
// `Ordering[TypeName]` doesn't work due to `Ordering` still being invariant
169+
the[Ordering[Name]].compare(x, y)
170+
case y: Int =>
171+
1
172+
}
173+
case x: Int =>
174+
y match {
175+
case y: Name =>
176+
-1
177+
case y: Int =>
178+
x - y
179+
}
180+
}
181+
}
182+
def compare(x: Signature, y: Signature): Int = {
183+
import scala.math.Ordering.Implicits.seqOrdering
184+
val paramsOrdering = seqOrdering(paramSigOrdering).compare(x.paramsSig, y.paramsSig)
185+
if (paramsOrdering != 0) paramsOrdering
186+
else the[Ordering[Name]].compare(x.resSig, y.resSig)
187+
}
188+
}
161189
}

0 commit comments

Comments
 (0)