Skip to content

Commit

Permalink
Make sure synthetic apply methods are generated in deterministic order
Browse files Browse the repository at this point in the history
Fixes #17330
  • Loading branch information
raboof committed Jul 15, 2023
1 parent 9667958 commit 6d1ae5a
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 22 deletions.
36 changes: 18 additions & 18 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1750,27 +1750,27 @@ class Definitions {
@tu lazy val Tuple2SpecializedParamClasses: PerRun[Set[Symbol]] = new PerRun(Tuple2SpecializedParamTypes.map(_.symbol))

// Specialized type parameters defined for scala.Function{0,1,2}.
@tu lazy val Function1SpecializedParamTypes: collection.Set[TypeRef] =
Set(IntType, LongType, FloatType, DoubleType)
@tu lazy val Function2SpecializedParamTypes: collection.Set[TypeRef] =
Set(IntType, LongType, DoubleType)
@tu lazy val Function0SpecializedReturnTypes: collection.Set[TypeRef] =
ScalaNumericValueTypeList.toSet + UnitType + BooleanType
@tu lazy val Function1SpecializedReturnTypes: collection.Set[TypeRef] =
Set(UnitType, BooleanType, IntType, FloatType, LongType, DoubleType)
@tu lazy val Function2SpecializedReturnTypes: collection.Set[TypeRef] =
@tu lazy val Function1SpecializedParamTypes: List[TypeRef] =
List(IntType, LongType, FloatType, DoubleType)
@tu lazy val Function2SpecializedParamTypes: List[TypeRef] =
List(IntType, LongType, DoubleType)
@tu lazy val Function0SpecializedReturnTypes: List[TypeRef] =
ScalaNumericValueTypeList :+ UnitType :+ BooleanType
@tu lazy val Function1SpecializedReturnTypes: List[TypeRef] =
List(UnitType, BooleanType, IntType, FloatType, LongType, DoubleType)
@tu lazy val Function2SpecializedReturnTypes: List[TypeRef] =
Function1SpecializedReturnTypes

@tu lazy val Function1SpecializedParamClasses: PerRun[collection.Set[Symbol]] =
new PerRun(Function1SpecializedParamTypes.map(_.symbol))
new PerRun(Function1SpecializedParamTypes.toSet.map(_.symbol))
@tu lazy val Function2SpecializedParamClasses: PerRun[collection.Set[Symbol]] =
new PerRun(Function2SpecializedParamTypes.map(_.symbol))
new PerRun(Function2SpecializedParamTypes.toSet.map(_.symbol))
@tu lazy val Function0SpecializedReturnClasses: PerRun[collection.Set[Symbol]] =
new PerRun(Function0SpecializedReturnTypes.map(_.symbol))
new PerRun(Function0SpecializedReturnTypes.toSet.map(_.symbol))
@tu lazy val Function1SpecializedReturnClasses: PerRun[collection.Set[Symbol]] =
new PerRun(Function1SpecializedReturnTypes.map(_.symbol))
new PerRun(Function1SpecializedReturnTypes.toSet.map(_.symbol))
@tu lazy val Function2SpecializedReturnClasses: PerRun[collection.Set[Symbol]] =
new PerRun(Function2SpecializedReturnTypes.map(_.symbol))
new PerRun(Function2SpecializedReturnTypes.toSet.map(_.symbol))

def isSpecializableTuple(base: Symbol, args: List[Type])(using Context): Boolean =
args.length <= 2 && base.isClass && TupleSpecializedClasses.exists(base.asClass.derivesFrom) && args.match
Expand Down Expand Up @@ -1802,18 +1802,18 @@ class Definitions {
})
&& !ctx.settings.Yscala2Stdlib.value // We do not add the specilized FunctionN methods/classes when compiling the stdlib

@tu lazy val Function0SpecializedApplyNames: collection.Set[TermName] =
@tu lazy val Function0SpecializedApplyNames: List[TermName] =
for r <- Function0SpecializedReturnTypes
yield nme.apply.specializedFunction(r, Nil).asTermName

@tu lazy val Function1SpecializedApplyNames: collection.Set[TermName] =
@tu lazy val Function1SpecializedApplyNames: List[TermName] =
for
r <- Function1SpecializedReturnTypes
t1 <- Function1SpecializedParamTypes
yield
nme.apply.specializedFunction(r, List(t1)).asTermName

@tu lazy val Function2SpecializedApplyNames: collection.Set[TermName] =
@tu lazy val Function2SpecializedApplyNames: List[TermName] =
for
r <- Function2SpecializedReturnTypes
t1 <- Function2SpecializedParamTypes
Expand All @@ -1822,7 +1822,7 @@ class Definitions {
nme.apply.specializedFunction(r, List(t1, t2)).asTermName

@tu lazy val FunctionSpecializedApplyNames: collection.Set[Name] =
Function0SpecializedApplyNames ++ Function1SpecializedApplyNames ++ Function2SpecializedApplyNames
(Function0SpecializedApplyNames ++ Function1SpecializedApplyNames ++ Function2SpecializedApplyNames).toSet

def functionArity(tp: Type)(using Context): Int = tp.functionArgInfos.length - 1

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ class SpecializeApplyMethods extends MiniPhase with InfoTransformer {
override def transformTemplate(tree: Template)(using Context) = {
val cls = tree.symbol.owner.asClass

def synthesizeApply(names: collection.Set[TermName]): Tree = {
def synthesizeApply(names: List[TermName]): Tree = {
val applyBuf = new mutable.ListBuffer[DefDef]
names.foreach { name =>
val applySym = cls.info.decls.lookup(name)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ class SpecializeFunctionsTests extends DottyBytecodeTest {
)
assert(applys.contains("apply"), "Foo did not contain `apply` forwarder method")
assert(applys.contains("apply$mcII$sp"), "Foo did not contain specialized apply")

// It's not essential they are in this particular order,
// but they should be in deterministic order
assert(applys == List("apply", "apply$mcII$sp", "apply"))
}
}

Expand All @@ -48,20 +52,84 @@ class SpecializeFunctionsTests extends DottyBytecodeTest {
checkBCode(source) { dir =>
val apps =
findClass("Func2", dir).methods.asScala.collect {
case m if m.name == "apply" => m
case m if m.name == "apply$mcIII$sp" =>
assert(!hasInvokeStatic(m)) // should not call super specialized method
assert(!hasInvokeStatic(m), s"${m.name} should not call super specialized method")
m
case m if m.name == "apply" => m
case m if m.name.startsWith("apply") => m
}
.map(_.name)
.toList

assert(
apps.length == 3,
apps.length == 56,
s"Wrong number of specialized applys, actual length: ${apps.length} - $apps"
)
assert(apps.contains("apply"), "Func2 did not contain `apply` forwarder method")
assert(apps.contains("apply$mcIII$sp"), "Func2 did not contain specialized apply")

// It's not essential they are in this particular order,
// but they should be in some deterministic order:
assert(
apps == List(
"apply$mcVII$sp",
"apply$mcVIJ$sp",
"apply$mcVID$sp",
"apply$mcVJI$sp",
"apply$mcVJJ$sp",
"apply$mcVJD$sp",
"apply$mcVDI$sp",
"apply$mcVDJ$sp",
"apply$mcVDD$sp",
"apply$mcZII$sp",
"apply$mcZIJ$sp",
"apply$mcZID$sp",
"apply$mcZJI$sp",
"apply$mcZJJ$sp",
"apply$mcZJD$sp",
"apply$mcZDI$sp",
"apply$mcZDJ$sp",
"apply$mcZDD$sp",
"apply$mcIIJ$sp",
"apply$mcIID$sp",
"apply$mcIJI$sp",
"apply$mcIJJ$sp",
"apply$mcIJD$sp",
"apply$mcIDI$sp",
"apply$mcIDJ$sp",
"apply$mcIDD$sp",
"apply$mcFII$sp",
"apply$mcFIJ$sp",
"apply$mcFID$sp",
"apply$mcFJI$sp",
"apply$mcFJJ$sp",
"apply$mcFJD$sp",
"apply$mcFDI$sp",
"apply$mcFDJ$sp",
"apply$mcFDD$sp",
"apply$mcJII$sp",
"apply$mcJIJ$sp",
"apply$mcJID$sp",
"apply$mcJJI$sp",
"apply$mcJJJ$sp",
"apply$mcJJD$sp",
"apply$mcJDI$sp",
"apply$mcJDJ$sp",
"apply$mcJDD$sp",
"apply$mcDII$sp",
"apply$mcDIJ$sp",
"apply$mcDID$sp",
"apply$mcDJI$sp",
"apply$mcDJJ$sp",
"apply$mcDJD$sp",
"apply$mcDDI$sp",
"apply$mcDDJ$sp",
"apply$mcDDD$sp",
"apply",
"apply$mcIII$sp",
"apply"),
s"Apply methods were not in the expected order: $apps"
)
}
}

Expand Down

0 comments on commit 6d1ae5a

Please sign in to comment.