Skip to content

Commit 909f39d

Browse files
committed
Refactor code generation of specialized variants
1 parent c4fc783 commit 909f39d

File tree

1 file changed

+96
-82
lines changed

1 file changed

+96
-82
lines changed

project/CodeGen.scala

Lines changed: 96 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -141,115 +141,129 @@ object CodeGen {
141141

142142
private val initName = "$init$"
143143
private val function1ImplClass = "scala.Function1$class"
144-
private val function2ImplClass = "scala.Function2$class"
145144
private val copyright =
146145
"""
147146
|/*
148147
| * Copyright (C) 2012-2014 Typesafe Inc. <http://www.typesafe.com>
149148
| */""".stripMargin.trim
150149

151-
private def apply0MethodSpec(r: Type): String = {
152-
val name = "apply$mc" + s"${r.code}" + "$sp"
153-
val applyCall = s"apply();"
154-
def body = if (r == Type.Void) applyCall else s"return (${r.ref}) $applyCall"
155-
s"""
156-
|default ${r.prim} $name() {
157-
| $body
158-
|}
159-
|""".stripMargin.trim
150+
private def function0SpecMethods = {
151+
val apply = specialized("apply", function0Spec) {
152+
case (name, List(r)) =>
153+
val applyCall = s"apply();"
154+
def body = if (r == Type.Void) applyCall else s"return (${r.ref}) $applyCall"
155+
s"""
156+
|default ${r.prim} $name() {
157+
| $body
158+
|}
159+
|""".stripMargin.trim
160+
}
161+
indent(apply)
160162
}
161163

162-
private def apply0SpecMethods = {
164+
private val function0Spec = {
163165
val rs = List(Type.Void, Type.Byte, Type.Short, Type.Int, Type.Long, Type.Char, Type.Float, Type.Double, Type.Boolean)
164-
val methods = for (r <- rs) yield apply0MethodSpec(r)
165-
methods.map(indent).mkString("\n\n")
166+
List("R" -> rs)
166167
}
167-
168-
val function1SpecTs = List(Type.Int, Type.Long, Type.Float, Type.Double)
169-
val function1SpecRs = List(Type.Void, Type.Boolean, Type.Int, Type.Float, Type.Long, Type.Double)
170-
171-
private def apply1MethodSpec(t1: Type, r: Type): String = {
172-
val name = "apply$mc" + s"${r.code}${t1.code}" + "$sp"
173-
val applyCall = s"apply((T1) ((${t1.ref}) v1));"
174-
def body = if (r == Type.Void) applyCall else s"return (${r.ref}) $applyCall"
175-
176-
s"""
177-
|default ${r.prim} $name(${t1.prim} v1) {
178-
| $body
179-
|}
180-
|""".stripMargin.trim
168+
private val function1Spec = {
169+
val ts = List(Type.Int, Type.Long, Type.Float, Type.Double)
170+
val rs = List(Type.Void, Type.Boolean, Type.Int, Type.Float, Type.Long, Type.Double)
171+
List("T1" -> ts, "R" -> rs)
181172
}
182-
183-
private def apply1SpecMethods = {
184-
val methods = for (t1 <- function1SpecTs; r <- function1SpecRs) yield apply1MethodSpec(t1, r)
185-
methods.map(indent).mkString("\n\n")
173+
private val function2Spec = {
174+
val ts = List(Type.Int, Type.Long, Type.Double)
175+
val rs = List(Type.Void, Type.Boolean, Type.Int, Type.Float, Type.Long, Type.Double)
176+
List("T1" -> ts, "T2" -> ts, "R" -> rs)
186177
}
187178

188-
private def andThenComposeMethodSpec(t1: Type, r: Type): String = {
189-
val suffix = "$mc" + s"${r.code}${t1.code}" + "$sp"
190-
s"""
191-
|default scala.Function1 compose$suffix(scala.Function1 g) {
192-
| return compose(g);
193-
|}
194-
|default scala.Function1 andThen$suffix(scala.Function1 g) {
195-
| return andThen(g);
196-
|}
197-
|""".stripMargin.trim
179+
private def function1SpecMethods = {
180+
val apply = specialized("apply", function1Spec) {
181+
case (name, List(t1, r)) =>
182+
val applyCall = s"apply((T1) ((${t1.ref}) v1));"
183+
def body = if (r == Type.Void) applyCall else s"return (${r.ref}) $applyCall"
184+
s"""
185+
|default ${r.prim} $name(${t1.prim} v1) {
186+
| $body
187+
|}
188+
|""".stripMargin.trim
189+
}
190+
// andThen / compose variants are no longer needed under 2.11 (@unspecialized has been fixed),
191+
// but harmless. With them, we can use the same artifact for 2.10 and 2.11
192+
val compose = specialized("compose", function1Spec) {
193+
case (name, List(t1, r1)) =>
194+
s"""
195+
|default scala.Function1 $name(scala.Function1 g) {
196+
| return compose(g);
197+
|}""".stripMargin.trim
198+
}
199+
val andThen = specialized("andThen", function1Spec) {
200+
case (name, List(t1, r1)) =>
201+
s"""
202+
|default scala.Function1 $name(scala.Function1 g) {
203+
| return andThen(g);
204+
|}""".stripMargin.trim
205+
}
206+
indent(List(apply, compose, andThen).mkString("\n\n"))
198207
}
199208

200209
// No longer needed under 2.11 (@unspecialized has been fixed), but harmless to keep around to avoid cross-publishing this artifact.
201-
private def andThenComposeSpecMethods = {
202-
val methods = for (t1 <- function1SpecTs; r <- function1SpecRs) yield andThenComposeMethodSpec(t1, r)
203-
methods.map(indent).mkString("\n\n")
204-
}
205-
206-
val function2SpecTs = List(Type.Int, Type.Long, Type.Double)
207-
val function2SpecRs = function1SpecRs
208-
209-
private def apply2MethodSpec(t1: Type, t2: Type, r: Type): String = {
210-
val name = "apply$mc" + s"${r.code}${t1.code}${t2.code}" + "$sp"
211-
val applyCall = s"apply((T1) ((${t1.ref}) v1), (T2) ((${t2.ref}) v2));"
212-
def body = if (r == Type.Void) applyCall else s"return (${r.ref}) $applyCall"
213-
214-
s"""
215-
|default ${r.prim} $name(${t1.prim} v1, ${t2.prim} v2) {
216-
| $body
217-
|}
218-
|""".stripMargin.trim
219-
}
220-
221-
private def apply2SpecMethods = {
222-
val methods = for (t1 <- function2SpecTs; t2 <- function2SpecTs; r <- function2SpecRs) yield apply2MethodSpec(t1, t2, r)
223-
methods.map(indent).mkString("\n\n")
210+
private def function2SpecMethods = {
211+
val apply = specialized("apply", function2Spec) {
212+
case (name, List(t1, t2, r)) =>
213+
val applyCall = s"apply((T1) ((${t1.ref}) v1), (T2) ((${t2.ref}) v2));"
214+
def body = if (r == Type.Void) applyCall else s"return (${r.ref}) $applyCall"
215+
216+
s"""
217+
|default ${r.prim} $name(${t1.prim} v1, ${t2.prim} v2) {
218+
| $body
219+
|}
220+
|""".stripMargin.trim
221+
}
222+
val curried = specialized("curried", function2Spec) {
223+
case (name, List(t1, t2, r)) =>
224+
s"""
225+
|default scala.Function1 $name() {
226+
| return curried();
227+
|}""".stripMargin.trim
228+
}
229+
val tupled = specialized("tupled", function2Spec) {
230+
case (name, List(t1, t2, r)) =>
231+
s"""
232+
|default scala.Function1 $name() {
233+
| return tupled();
234+
|}""".stripMargin.trim
235+
}
236+
indent(List(apply, curried, tupled).mkString("\n\n"))
224237
}
225238

226-
private def curriedTupled2MethodSpec(t1: Type, t2: Type, r: Type): String = {
227-
val suffix = "$mc" + s"${r.code}${t1.code}${t2.code}" + "$sp"
228-
s"""
229-
|default scala.Function1 curried$suffix() {
230-
| return curried();
231-
|}
232-
|default scala.Function1 tupled$suffix() {
233-
| return tupled();
234-
|}
235-
|""".stripMargin.trim
239+
private def specialized(name: String, tps: List[(String, List[Type])])(f: (String, List[Type]) => String): String = {
240+
val tparamNames = tps.map(_._1)
241+
def code(tps: List[Type]) = {
242+
val sorted = (tps zip tparamNames).sortBy(_._2).map(_._1) // as per scalac, sort by tparam name before assembling the code
243+
sorted.map(_.code).mkString
244+
}
245+
val ms = for {
246+
variantTypes <- crossProduct(tps.map(_._2))
247+
specName = name + "$mc" + code(variantTypes) + "$sp"
248+
} yield f(specName, variantTypes)
249+
ms.mkString("\n")
236250
}
237251

238-
// No longer needed under 2.11 (@unspecialized has been fixed), but harmless to keep around to avoid cross-publishing this artifact.
239-
private def curriedTupled2SpecMethods = {
240-
val methods = for (t1 <- function2SpecTs; t2 <- function2SpecTs; r <- function2SpecRs) yield curriedTupled2MethodSpec(t1, t2, r)
241-
methods.map(indent).mkString("\n\n")
252+
def crossProduct[A](input: List[List[A]]): List[List[A]] = input match {
253+
case Nil => Nil
254+
case head :: Nil => head.map(_ :: Nil)
255+
case head :: tail => for (elem <- head; sub <- crossProduct(tail)) yield elem :: sub
242256
}
243257

244258
def fN(n: Int) = {
245259
val header = arity(n).fHeader
246260
val specializedVariants = n match {
247-
case 0 => apply0SpecMethods
248-
case 1 => apply1SpecMethods + "\n\n" + andThenComposeSpecMethods
249-
case 2 => apply2SpecMethods + "\n\n" + curriedTupled2SpecMethods
261+
case 0 => function0SpecMethods
262+
case 1 => function1SpecMethods
263+
case 2 => function2SpecMethods
250264
case x => ""
251265
}
252-
val trailer = "}\n"
266+
val trailer = "\n}\n"
253267
List(header, specializedVariants, trailer).mkString
254268
}
255269

0 commit comments

Comments
 (0)