Skip to content

Commit 599ff14

Browse files
authored
Repair Record.stage method after 3.7 migration (#1187)
Fix `pendingUntilFixed` test of `Record.stage` after migrating to 3.7.0 (in #1175) Firstly, I have fixed problems with `unlimited recursion` which was similar to (scala/scala3#23137). I have just made scopes of opaque types (`AsFields, AsField`) inside `Record` narrower. Secondly, I have faced with interesting behavior: moving bound `AsFields` from `unsafeFrom` to `StageOps.apply` changes compiler result. I've spent a lot of time to minimize it to scala compiler bug, but no succeeded.
1 parent 934098b commit 599ff14

File tree

2 files changed

+23
-18
lines changed

2 files changed

+23
-18
lines changed

kyo-data/shared/src/main/scala/kyo/Record.scala

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -121,20 +121,21 @@ object Record:
121121
*/
122122
val empty: Record[Any] = Record[Any](Map())
123123

124-
private def unsafeFrom[Fields: AsFields](map: Map[Field[?, ?], Any]): Record[Fields] = Record(map)
124+
private def unsafeFrom[Fields](map: Map[Field[?, ?], Any]): Record[Fields] = Record(map)
125125

126126
inline def stage[Fields]: StageOps[Fields] = new StageOps[Fields](())
127127

128128
class StageOps[Fields](dummy: Unit) extends AnyVal:
129129
/** Applies `StageAs` logic to each field. Called on a record type `n1 ~ v1 & ... & nk ~ vk`, returns a new record of type
130130
* `n1 ~ F[n1, v1] & ... & nk ~ F[nk, vk]`.
131131
*/
132-
inline def apply[F[_, _]](as: StageAs[F])(using
133-
asFields: AsFields[Fields],
134-
ev: TypeIntersection[Fields]
132+
inline def apply[F[_, _]](as: Record.StageAs[F])(using
133+
initFields: AsFields[Fields],
134+
ev: TypeIntersection[Fields],
135+
targetFields: AsFields[ev.Map[~.Map[F]]]
135136
): Record[ev.Map[~.Map[F]]] =
136-
unsafeFrom(TypeIntersection.inlineAll[Fields](as).view.map {
137-
case (f, g) => (f.unwrap: Field[?, ?], g.unwrap)
137+
Record.unsafeFrom(TypeIntersection.inlineAll[Fields](as).view.map {
138+
case (f, g) => (AsFieldAny.toField(f), g.unwrap)
138139
}.toMap)
139140
end StageOps
140141

@@ -255,27 +256,33 @@ object Record:
255256
* @tparam A
256257
* The field type, typically in the form of `"fieldName" ~ ValueType`
257258
*/
258-
opaque type AsField[Name <: String, Value] = Field[Name, Value]
259-
260-
private[kyo] type AsFieldAny[n, v] = AsField[n & String, v]
261-
259+
type AsField[Name <: String, Value] = AsField.Type[Name, Value]
262260
object AsField:
261+
opaque type Type[Name <: String, Value] = Field[Name, Value]
262+
263263
inline given [N <: String, V](using tag: Tag[V]): AsField[N, V] =
264264
Field(constValue[N], tag)
265265

266-
private[kyo] def toField[Name <: String, Value](as: AsField[Name, Value]): Field[?, ?] = as
266+
private[kyo] def fromField[Name <: String, Value](field: Field[Name, Value]): AsField[Name, Value] = field
267+
private[kyo] def toField[Name <: String, Value](field: AsField[Name, Value]): Field[Name, Value] = field
267268
end AsField
268269

270+
private[kyo] type AsFieldAny[n, v] = AsField[n & String, v]
271+
private[kyo] object AsFieldAny:
272+
def toField(as: ForSome2[AsFieldAny]): Field[?, ?] = AsField.toField(as.unwrap)
273+
269274
/** Type class for working with sets of Record fields.
270275
*
271276
* AsFields provides type-safe field set operations and is used primarily for the `compact` operation on Records.
272277
*
273278
* @tparam A
274279
* The combined type of all fields in the set
275280
*/
276-
opaque type AsFields[+A] <: Set[Field[?, ?]] = Set[Field[?, ?]]
281+
type AsFields[+A] = AsFields.Type[A]
277282

278283
object AsFields:
284+
opaque type Type[+A] <: Set[Field[?, ?]] = Set[Field[?, ?]]
285+
279286
def apply[A](using af: AsFields[A]): Set[Field[?, ?]] = af
280287

281288
inline given [Fields](using ev: TypeIntersection[Fields]): AsFields[Fields] =
@@ -317,10 +324,9 @@ object Record:
317324
val nextTag = summonInline[Tag[F[n, v]]]
318325

319326
(
320-
ForSome2.of[AsFieldAny](Field(name, nextTag)),
327+
ForSome2.of[AsFieldAny](AsField.fromField(Field(name, nextTag))),
321328
ForSome2(stage[n, v](Field(name, prevTag)))
322329
)
323-
end apply
324330
end StageAs
325331
end Record
326332

@@ -334,7 +340,6 @@ object AsFieldsInternal:
334340
end AsFieldInliner
335341

336342
inline def summonAsField[Fields](using ev: TypeIntersection[Fields]): Set[Field[?, ?]] =
337-
TypeIntersection.inlineAll[Fields](AsFieldInliner).map { x =>
338-
Record.AsField.toField(x.unwrap)
339-
}.toSet
343+
TypeIntersection.inlineAll[Fields](AsFieldInliner).map(Record.AsFieldAny.toField).toSet
344+
end summonAsField
340345
end AsFieldsInternal

kyo-data/shared/src/test/scala/kyo/RecordTest.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ class RecordTest extends Test:
297297
inline def stage[Name <: String, Value](field: Field[Name, Value]): Column[Value] =
298298
Column[Value](field.name)(using summonInline[AsColumn[Value]])
299299

300-
"build record if all inlined" in pendingUntilFixed {
300+
"build record if all inlined" in {
301301
assertCompiles("""
302302
type Person = "name" ~ String & "age" ~ Int
303303

0 commit comments

Comments
 (0)