Skip to content

Commit be24d72

Browse files
committed
experiment
1 parent a0bf548 commit be24d72

7 files changed

Lines changed: 399 additions & 6 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ scala-doc/
1616
.fuzz_output/
1717
.ref_compare/
1818
*.jar
19+
.codium-data/

build.mill

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
//| mill-version: 1.0.6
2-
//| mill-jvm-version: temurin:24
1+
//| mill-version: 1.1.5
2+
//| mill-jvm-version: temurin:25
33
//| mill-jvm-opts: ["--enable-native-access=ALL-UNNAMED"]
44
//| mvnDeps:
55
//| - com.goyeau::mill-scalafix::0.6.0
@@ -11,16 +11,16 @@ import mill.api.Task.Simple
1111
import mill.api.TaskCtx
1212

1313
trait ForjaModule extends ScalaModule, ScalafixModule:
14-
def scalaVersion = "3.7.3"
14+
def scalaVersion = "3.8.3"
1515
def scalacOptions = Seq(
16-
"-Werror",
17-
"-Wunused:strict-no-implicit-warn",
16+
// "-Werror",
1817
"-Yexplicit-nulls",
1918
"-deprecation",
2019
"-feature",
2120
"-source:future",
2221
"-Xcheck-macros",
2322
"-explain-cyclic",
23+
"-preview",
2424
)
2525
override def forkArgs = super.forkArgs() ++ Seq(
2626
// TODO: fix when Scala 3.8?
@@ -45,6 +45,7 @@ object forja extends ForjaModule:
4545
mvn"com.lihaoyi::os-lib:0.11.5",
4646
mvn"org.typelevel::cats-core:2.13.0",
4747
mvn"io.github.java-diff-utils:java-diff-utils:4.15",
48+
mvn"dev.zio::izumi-reflect:3.0.9",
4849
)
4950
object test extends ForjaTests
5051

forja/src/ModelChecker.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ transparent trait ModelChecker:
3737

3838
while stateQueue.nonEmpty && result.isEmpty
3939
do
40-
val state = stateQueue.synchronized(stateQueue.dequeue)
40+
val state = stateQueue.synchronized(stateQueue.dequeue())
4141
var hasNextStates = false
4242
nextStates(state).foreach: nextState =>
4343
hasNextStates = true

forja/src/Prod.scala

Lines changed: 303 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
package forja
2+
3+
import scala.deriving.Mirror
4+
import scala.compiletime.ops.int.`+`
5+
import scala.util.NotGiven
6+
import scala.reflect.ClassTag
7+
import scala.reflect.TypeTest
8+
import scala.reflect.Typeable
9+
import scala.compiletime.deferred
10+
import scala.quoted.Type
11+
import scala.quoted.Quotes
12+
import scala.quoted.Expr
13+
import forja.util.TupleOf
14+
import java.util.concurrent.atomic.AtomicInteger
15+
import scala.compiletime.summonFrom
16+
import scala.compiletime.summonInline
17+
import scala.compiletime.erasedValue
18+
import scala.compiletime.asMatchable
19+
import izumi.reflect.Tag
20+
import scala.annotation.publicInBinary
21+
22+
into sealed abstract class Prod[T]:
23+
private val epoch = Prod.epoch.get()
24+
25+
override def equals(obj: Any): Boolean = ???
26+
27+
def tag: Tag[T]
28+
29+
def reify(): T
30+
31+
final def rewritePre(rw: Prod.Rewrite): Prod[T] =
32+
rw.rewrite(this).rewriteChildren([u] => uu => uu.rewritePre(rw))
33+
end rewritePre
34+
35+
final def rewritePost(rw: Prod.Rewrite): Prod[T] =
36+
rw.rewrite(rewriteChildren([u] => uu => uu.rewritePost(rw)))
37+
end rewritePost
38+
39+
final def fixpoint(rw: Prod.Rewrite): Prod[T] =
40+
def impl[T](self: Prod[T], rw: Prod.Rewrite, fromEpoch: Int): Prod[T] =
41+
if self.epoch >= fromEpoch
42+
then
43+
val nextUnstableEpoch = Prod.epoch.incrementAndGet()
44+
var currSelf = self
45+
while
46+
val prevSelf = currSelf
47+
currSelf = rw.rewrite(currSelf)
48+
prevSelf ne currSelf
49+
do ()
50+
currSelf = currSelf.rewriteChildren([u] => uu => impl(uu, rw, fromEpoch))
51+
if currSelf ne self
52+
then impl(currSelf, rw, fromEpoch = nextUnstableEpoch)
53+
else self
54+
else self
55+
end impl
56+
57+
// Start epoch at -1 for unconditional scan.
58+
// Rescans from there will only account for nodes created on or after the original epoch.
59+
// That is, we will only look at nodes that have any possibility of coming from a rewrite
60+
// we just performed.
61+
impl(this, rw, fromEpoch = -1)
62+
end fixpoint
63+
64+
protected def rewriteChildren(fn: [U] => Prod[U] => Prod[U]): Prod[T]
65+
66+
protected def unapplyLifted[U](ops: Prod.LiftableOps[U]): Option[ops.U]
67+
68+
def cast[U : Tag]: Prod[U]
69+
70+
final def upcast[U >: T : Tag]: Prod[U] = cast[U]
71+
end Prod
72+
73+
object Prod:
74+
private val epoch = AtomicInteger(0)
75+
76+
given lift: [T] => (liftable: Liftable[T]) => Conversion[T, Prod[T]]:
77+
def apply(t: T): Prod[T] = liftable.lift(t)
78+
end lift
79+
80+
def apply[T](using ops: LiftableOps.Aux[T, EmptyTuple])(): Prod[T] =
81+
Lifted(ops, EmptyTuple)
82+
end apply
83+
84+
given upcast: [T : Tag, U <: T] => Conversion[Prod[U], Prod[T]]:
85+
def apply(prod: Prod[U]): Prod[T] = prod.upcast[T]
86+
end upcast
87+
88+
type StripTuple1[U] = U match
89+
case Tuple1[u] => u
90+
case _ => U
91+
end StripTuple1
92+
93+
inline def apply[T](using ops: LiftableOps[T])(u: StripTuple1[ops.U]): Prod[T] =
94+
inline erasedValue[ops.U & Matchable] match
95+
case _: Tuple1[_] => applyImpl(Tuple1(u).asInstanceOf)
96+
case _ => applyImpl(u.asInstanceOf)
97+
end match
98+
end apply
99+
100+
private def applyImpl[T](using ops: LiftableOps[T])(u: ops.U): Prod[T] =
101+
Lifted(ops, u)
102+
end applyImpl
103+
104+
inline def unapply[T](prod: Prod[?])(using ops: LiftableOps[T]): Option[StripTuple1[ops.U]] =
105+
prod.unapplyLifted(ops).map: x =>
106+
x.asMatchable match
107+
case Tuple1(u) => u.asInstanceOf[StripTuple1[ops.U]]
108+
case u => u.asInstanceOf[StripTuple1[ops.U]]
109+
end match
110+
end unapply
111+
112+
final case class CannotReify(prod: Prod[?]) extends RuntimeException(s"cannot reify $prod")
113+
114+
trait Rewrite:
115+
def rewrite[U](prod: Prod[U]): Prod[U]
116+
end Rewrite
117+
118+
object Rewrite:
119+
def rw[T : Tag](fn: PartialFunction[Prod[T], Prod[T]]): Rewrite =
120+
Rewrite.PartialFunctionRewrite(fn)
121+
end rw
122+
123+
final class PartialFunctionRewrite[T : Tag](fn: PartialFunction[Prod[T], Prod[T]]) extends Rewrite:
124+
def rewrite[U](prod: Prod[U]): Prod[U] =
125+
if prod.tag <:< Tag[T]
126+
then fn
127+
.asInstanceOf[PartialFunction[Prod[U], Prod[U]]]
128+
.applyOrElse(prod, identity)
129+
else prod
130+
end rewrite
131+
end PartialFunctionRewrite
132+
133+
final class RewriteSeq(rewrites: Seq[Rewrite]) extends Rewrite:
134+
def rewrite[U](prod: Prod[U]): Prod[U] =
135+
rewrites.foldLeft(prod)((prod, rw) => rw.rewrite(prod))
136+
end rewrite
137+
end RewriteSeq
138+
end Rewrite
139+
140+
private final class Reject[T](val tag: Tag[T], val message: String) extends Prod[T]:
141+
def reify(): T = throw CannotReify(this)
142+
protected def rewriteChildren(fn: [U] => Prod[U] => Prod[U]): Prod[T] = this
143+
protected def unapplyLifted[U](ops: LiftableOps[U]): Option[ops.U] = None
144+
def cast[U: Tag]: Prod[U] = Reject(Tag[U], message)
145+
end Reject
146+
147+
private final class Lifted[T, U](val ops: LiftableOps.Aux[T, U], val u: U) extends Prod[T]:
148+
def tag = ops.tag
149+
def reify(): T = ops.reify(u)
150+
protected def rewriteChildren(fn: [U] => (x: Prod[U]) => Prod[U]): Prod[T] =
151+
val rw = ops.rewriteChildren(u, fn)
152+
if rw.asInstanceOf[AnyRef] ne u.asInstanceOf[AnyRef]
153+
then Lifted(ops, rw)
154+
else this
155+
end rewriteChildren
156+
protected def unapplyLifted[V](ops: LiftableOps[V]): Option[ops.U] =
157+
if this.ops.tag <:< ops.tag
158+
then Some(u.asInstanceOf[ops.U])
159+
else None
160+
end unapplyLifted
161+
def cast[U: Tag]: Prod[U] =
162+
if ops.tag =:= Tag[U]
163+
then this.asInstanceOf
164+
else if ops.tag <:< Tag[U]
165+
then Super(Tag[U], this.asInstanceOf)
166+
else Cast(Tag[U], this)
167+
end cast
168+
end Lifted
169+
170+
private final class Super[T, V <: T](val tag: Tag[T], val prod: Prod[V]) extends Prod[T]:
171+
def reify(): T = prod.reify()
172+
protected def rewriteChildren(fn: [U] => Prod[U] => Prod[U]): Prod[T] =
173+
val rw = fn[V](prod)
174+
if rw ne prod
175+
then rw.upcast[T](using tag)
176+
else this
177+
end rewriteChildren
178+
export prod.unapplyLifted
179+
def cast[U: Tag]: Prod[U] =
180+
if Tag[U] == tag
181+
then this.asInstanceOf
182+
else prod.cast[U]
183+
end cast
184+
end Super
185+
186+
private final class Cast[T, V](val tag: Tag[T], val prod: Prod[V]) extends Prod[T]:
187+
def reify(): T = throw CannotReify(this)
188+
protected def rewriteChildren(fn: [U] => Prod[U] => Prod[U]): Prod[T] =
189+
val rw = fn[V](prod)
190+
if rw ne prod
191+
then rw.cast[T](using tag)
192+
else this
193+
end rewriteChildren
194+
export prod.unapplyLifted
195+
def cast[U: Tag]: Prod[U] =
196+
if Tag[U] == tag
197+
then this.asInstanceOf
198+
else prod.cast[U]
199+
end cast
200+
end Cast
201+
202+
trait Liftable[T]:
203+
def lift(t: T): Prod[T]
204+
end Liftable
205+
206+
object Liftable:
207+
transparent inline def derived[T : Tag](using mirror: Mirror.Of[T])(using =>TupleOf[Tuple.Map[mirror.MirroredElemTypes, Liftable]]): Liftable[T] | LiftableWithOps.Aux[T, Tuple.Map[mirror.MirroredElemTypes, Prod]] =
208+
inline mirror match
209+
case mirror: Mirror.ProductOf[t & Product] =>
210+
liftableProductOf[t & Product](using mirror)
211+
.asInstanceOf[LiftableWithOps.Aux[T, Tuple.Map[mirror.MirroredElemTypes, Prod]]]
212+
case mirror: Mirror.SumOf[T] =>
213+
liftableSumOf[T](using mirror)
214+
end match
215+
end derived
216+
end Liftable
217+
218+
trait LiftableOps[T]:
219+
type U
220+
def reify(u: U): T
221+
def rewriteChildren(u: U, fn: [u] => Prod[u] => Prod[u]): U
222+
given tag: Tag[T] = deferred
223+
end LiftableOps
224+
225+
object LiftableOps:
226+
type Aux[T, U0] = LiftableOps[T] {
227+
type U = U0
228+
}
229+
end LiftableOps
230+
231+
trait LiftableWithOps[T] extends Liftable[T], LiftableOps[T]
232+
233+
object LiftableWithOps:
234+
type Aux[T, U0] = LiftableWithOps[T] {
235+
type U = U0
236+
}
237+
end LiftableWithOps
238+
239+
trait IdentityLiftable[T] extends LiftableWithOps[T]:
240+
type U = T
241+
def lift(t: T) = Lifted(this, t)
242+
def reify(u: U): T = u
243+
def rewriteChildren(u: U, fn: [u] => Prod[u] => Prod[u]): U = u
244+
end IdentityLiftable
245+
246+
given identityLiftableByte: IdentityLiftable[Byte] {}
247+
given identityLiftableInt: IdentityLiftable[Int] {}
248+
given identityLiftableLong: IdentityLiftable[Long] {}
249+
given identityLiftableShort: IdentityLiftable[Short] {}
250+
given identityLiftableDouble: IdentityLiftable[Double] {}
251+
given identityLiftableFloat: IdentityLiftable[Float] {}
252+
given identityLiftableString: IdentityLiftable[String] {}
253+
254+
given liftableProductOf: [T <: Product : Tag] => (mirror: Mirror.ProductOf[T]) => (innerLiftables: =>TupleOf[Tuple.Map[mirror.MirroredElemTypes, Liftable]]) => LiftableWithOps.Aux[T, Tuple.Map[mirror.MirroredElemTypes, Prod]] =
255+
new LiftableWithOps[T]:
256+
type U = Tuple.Map[mirror.MirroredElemTypes, Prod]
257+
def lift(t: T): Prod[T] =
258+
val elems = t
259+
.productIterator
260+
.zip(innerLiftables.value.productIterator.asInstanceOf[Iterator[Liftable[Any]]])
261+
.map: (elem, liftable) =>
262+
liftable.lift(elem)
263+
.toArray
264+
end elems
265+
Lifted(this, Tuple.fromArray(elems).asInstanceOf[U])
266+
end lift
267+
def reify(u: U): T =
268+
mirror.fromTuple(scala.runtime.Tuples.map(u, [t] => tt => tt.asInstanceOf[Prod[Any]].reify().asInstanceOf).asInstanceOf[mirror.MirroredElemTypes])
269+
end reify
270+
def rewriteChildren(u: U, fn: [u] => Prod[u] => Prod[u]): U =
271+
val elems = u
272+
.toArray
273+
.mapInPlace(x => fn[Object](x.asInstanceOf[Prod[Object]]))
274+
end elems
275+
if elems.iterator.zipWithIndex.exists((p, i) => p ne u.productElement(i).asInstanceOf[Object])
276+
then Tuple.fromArray(elems).asInstanceOf[U]
277+
else u
278+
end rewriteChildren
279+
end new
280+
end liftableProductOf
281+
282+
given liftableSumOf: [T : Tag] => (mirror: Mirror.SumOf[T]) => (innerLiftables: =>TupleOf[Tuple.Map[mirror.MirroredElemTypes, Liftable]]) => Liftable[T]:
283+
def lift(t: T): Prod[T] =
284+
innerLiftables
285+
.value
286+
.productElement(mirror.ordinal(t))
287+
.asInstanceOf[Liftable[T]]
288+
.lift(t)
289+
.cast[T]
290+
end lift
291+
end liftableSumOf
292+
293+
given liftableList: [T] => Tag[List[T]] => (liftable: Liftable[T]) => LiftableWithOps.Aux[List[T], List[Prod[T]]] =
294+
new LiftableWithOps[List[T]]:
295+
type U = List[Prod[T]]
296+
def lift(t: List[T]): Prod[List[T]] = Lifted(this, t.map(elem => liftable.lift(elem)))
297+
def reify(u: List[Prod[T]]): List[T] = u.map(_.reify())
298+
def rewriteChildren(u: List[Prod[T]], fn: [u] => Prod[u] => Prod[u]): U =
299+
u.mapConserve(_.rewriteChildren(fn))
300+
end rewriteChildren
301+
end new
302+
end liftableList
303+
end Prod

0 commit comments

Comments
 (0)