Skip to content

Record like HK definitions that compile fine under 3.6.4 crash under 3.7.0 #23293

@rcano

Description

@rcano

Compiler version

3.7.0

Minimized code

I tried to minimize this by commenting parts out, but no luck (also most definitions depend on each other).

import scala.compiletime.ops.int.S import scala.compiletime.ops.string.+ import scala.deriving.Mirror import scala.annotation.{implicitNotFound} object genrec {  /** Fold a tuple just like Tuple.Fold but with typed upper bounds */ type TupleFold[T <: Tuple, UpperBound, Z <: UpperBound, F[_ <: UpperBound, _ <: UpperBound] <: UpperBound] <: UpperBound = T match case EmptyTuple => Z case h *: t => F[Z, TupleFold[t, UpperBound, h, F]] type TupleReduce[Tup <: Tuple, UpperBound, F[_ <: UpperBound, _ <: UpperBound] <: UpperBound] <: UpperBound = Tup match { case h *: t => TupleFold[t, UpperBound, h, F] }  /** Map a tuple just like Tuple.Map but with typed upper bounds */ type TupleMap[T <: Tuple, UpperBound, F[_ <: UpperBound]] <: Tuple = T match case EmptyTuple => EmptyTuple case h *: t => F[h] *: TupleMap[t, UpperBound, F] /*erased*/ trait TypeWitness[N]: type T = N /*erased*/ given [T]: TypeWitness[T] = null object opaques { @scala.annotation.showAsInfix opaque type @@[S <: String & Singleton, +T] >: T = T extension [T <: String & Singleton](t: T) def @@[V](v: V): T @@ V = v } export opaques.* type FieldName[T <: @@[String & Singleton, Any]] <: String = T match { case @@[nme, _] => nme } type FieldNames[T <: Tuple] = TupleMap[T, @@[String & Singleton, Any], FieldName] type FieldType[T <: @@[String & Singleton, Any]] = T match { case @@[_, tpe] => tpe } type FieldToString[T <: @@[String & Singleton, Any]] <: String = T match { case @@[name, tpe] => name + ": " + tpe } extension [K <: String & Singleton, T](f: K @@ T) inline def value: T = f.asInstanceOf[T] type IndexOf[Rec <: Tuple, Field] <: Int = Rec match { case h *: t => h match { //use invariant type comparison by abusing Set trick case Field => 0 case _ => S[IndexOf[t, Field]] } } type FieldToTuple[T <: @@[String & Singleton, Any]] <: Tuple = T match { case @@[nme, tpe] => (nme, tpe) } type IndexOfField[Rec <: Tuple, Field <: @@[String & Singleton, Any]] = IndexOf[Rec, Field] type WidenTuple[T <: Tuple] <: Tuple = T match { case EmptyTuple => EmptyTuple case h *: tail => h *: tail } given ProdOps: AnyRef with { extension [P <: Product](p: P)(using gen: Generic[P]) { def delField(field: String & Singleton) (using /*erased*/ w: TypeWitness[IndexOf[FieldNames[gen.Out], field.type]]) (using i: ValueOf[w.T]): Tuple.Concat[Tuple.Take[gen.Out, w.T], Tuple.Drop[gen.Out, S[w.T]]] = val arr = scala.runtime.Tuples.toIArray(gen.toGen(p)) val res = new Array[Object](arr.length - 1) System.arraycopy(arr, 0, res, 0, i.value) System.arraycopy(arr, i.value + 1, res, i.value, arr.length - i.value - 1) scala.runtime.Tuples.fromArray(res).asInstanceOf[Tuple.Concat[Tuple.Take[gen.Out, w.T], Tuple.Drop[gen.Out, S[w.T]]]] def renameField(field: String & Singleton, to: String & Singleton) (using /*erased*/ w: TypeWitness[IndexOf[FieldNames[gen.Out], field.type]]) (using i: ValueOf[w.T]): Tuple.Concat[Tuple.Take[gen.Out, w.T], (to.type @@ FieldType[Tuple.Elem[gen.Out, w.T]]) *: Tuple.Drop[gen.Out, S[w.T]]] = gen.toGen(p).asInstanceOf[Tuple.Concat[Tuple.Take[gen.Out, w.T], (to.type @@ FieldType[Tuple.Elem[gen.Out, w.T]]) *: Tuple.Drop[gen.Out, S[w.T]]]] def +[K <: String & Singleton, V](field: K @@ V): Tuple.Concat[gen.Out, (K @@ V) *: EmptyTuple] = gen.toGen(p) ++ (field *: EmptyTuple) def replaceField[NewValueType](field: String & Singleton) (using /*erased*/ w: TypeWitness[IndexOf[FieldNames[gen.Out], field.type]]) (using i: ValueOf[w.T]) (f: FieldType[Tuple.Elem[gen.Out, w.T]] => NewValueType): Tuple.Concat[Tuple.Take[gen.Out, w.T], (field.type @@ NewValueType) *: Tuple.Drop[gen.Out, S[w.T]]] = val res = scala.runtime.Tuples.toArray(gen.toGen(p)) val prev = res(i.value).asInstanceOf[FieldType[Tuple.Elem[gen.Out, w.T]]] val newv = f(prev) res(i.value) = newv.asInstanceOf[Object] scala.runtime.Tuples.fromArray(res).asInstanceOf[Tuple.Concat[Tuple.Take[gen.Out, w.T], (field.type @@ NewValueType) *: Tuple.Drop[gen.Out, S[w.T]]]] def toProd[Prd <: Product](using m: Mirror.ProductOf[Prd]): m.MirroredMonoType = m.fromProduct(gen.toGen(p)) def select(fieldName: String & Singleton)(using s: SelectByName[fieldName.type, gen.Out]): s.Out = s.apply(gen.toGen(p))[fieldName.type] def field(fieldName: String & Singleton) (using /*erased*/ w: TypeWitness[IndexOf[FieldNames[gen.Out], fieldName.type]]) (using i: ValueOf[w.T]): Tuple.Elem[gen.Out, w.T] = p.productElement(i.value).asInstanceOf[Tuple.Elem[gen.Out, w.T]] def pruneTo[Tup2 <: Tuple](using sr: SubRow[gen.Out, Tup2]): Tup2 = sr.asSubRecord(gen.toGen(p)) def transmogrify[P2 <: Product](using gen2: Generic[P2], sr: SubRow[gen.Out, gen2.Out], m: Mirror.ProductOf[P2]): P2 = m.fromProduct(sr.asSubRecord(gen.toGen(p))) } } trait Generic[Prod <: Product] { type Out <: Tuple def toGen(prod: Prod): Out extension (prod: Prod) def gen: Out = toGen(prod) } object Generic extends GenericLowPriority { type Aux[Prod <: Product, _Out <: Tuple] = Generic[Prod] { type Out = _Out} transparent inline def of[Prod <: Product] = scala.compiletime.summonInline[Generic[Prod]] type IsRecord[Rec <: Tuple] = Rec match { case EmptyTuple => true case a *: tail => a match { case @@[?, ?] => IsRecord[tail] case _ => false } } private val identityGeneric = new Generic[Tuple] { type Out = Tuple def toGen(t: Tuple) = t } given tuplesGeneric[Rec <: Tuple](using IsRecord[Rec] =:= true): Generic.Aux[Rec, Rec] = identityGeneric.asInstanceOf[Generic.Aux[Rec, Rec]] } trait GenericLowPriority { self: Generic.type => given productsGeneric[Prod <: Product](using m: Mirror.ProductOf[Prod]): Generic.Aux[ Prod, WidenTuple[Tuple.Map[Tuple.Zip[m.MirroredElemLabels, m.MirroredElemTypes], [Entry] =>> Entry match { case (nme, tpe) => nme @@ tpe }]] ] = new Generic[Prod] { type Out = WidenTuple[Tuple.Map[Tuple.Zip[m.MirroredElemLabels, m.MirroredElemTypes], [Entry] =>> Entry match { case (nme, tpe) => nme @@ tpe }]] def toGen(prod: Prod): Out = val r = prod.productIterator.map(v => ("s" @@ v).asInstanceOf[Object]).toArray scala.runtime.Tuples.fromArray(r).asInstanceOf[Out] } } type FieldSet[Rec <: Tuple] = Tuple.Union[Tuple.Map[Rec, FieldToTuple]] type MissingFields[Rec <: Tuple, AvailableFields] <: Tuple = Rec match { case EmptyTuple => EmptyTuple case h *: t => FieldToTuple[h] match { case AvailableFields => MissingFields[t, AvailableFields] case _ => h *: MissingFields[t, AvailableFields] } }  /** Read field [[Field]] from [[Rec]] */ @implicitNotFound("Field with name ${Field} is not present in ${Rec}") trait SelectByName[Field <: String & Singleton, Rec <: Tuple] { type Out extension (r: Rec) def apply[F <: Field]: Out } object SelectByName { type Aux[Field <: String & Singleton, Rec <: Tuple, O] = SelectByName[Field, Rec] { type Out = O } given [T, Field <: String & Singleton, Rec <: Tuple] // (using /*erased*/ w: TypeWitness[IndexOf[TupleMap[Rec, @@[String & Singleton, Any], FieldName], Field]]) (using /*erased*/ w: TypeWitness[IndexOf[Tuple.Map[Rec, FieldName], Field]]) (using v: ValueOf[w.T]): Aux[Field, Rec, FieldType[Tuple.Elem[Rec, w.T]]] = new SelectByName[Field, Rec] { type Out = FieldType[Tuple.Elem[Rec, w.T]] extension (r: Rec) def apply[F <: Field] = r.productElement(v.value).asInstanceOf[@@[Field, Out]].value } }  /** Typeclass denoting that [[T]] is a subrow of [[Rec]], this allows to select every field from [[Rec]] in [[T]] */ @implicitNotFound("can't prove that ${T} is a subrow of ${Rec}") trait SubRow[T, Rec <: Tuple] {  /** Read [[FieldName] value  * Note: the SelectByName is /*erased*/, it's only there to ensure via the compiler that you're trying to read one of the  * fields defined in the SubRow  */ extension (r: T) def apply[FieldName <: String & Singleton](using ValueOf[FieldName])(using /*erased*/ s: SelectByName[FieldName, Rec]): s.Out def asSubRecord: Rec } object SubRow { import scala.compiletime.* type Of[Rec <: Tuple] = [T] =>> SubRow[T, Rec]  /** Developer API, don't use directly */ class SubRowImpl[T <: Product, Rec <: Tuple](val fieldIndices: collection.immutable.SeqMap[String, Int], val recFields: IArray[String]) extends SubRow[T, Rec] { extension (r: T) def apply[FieldName <: String & Singleton](using ValueOf[FieldName])(using /*erased*/ s: SelectByName[FieldName, Rec]): s.Out = val i = fieldIndices(valueOf[FieldName]) r.productElement(i).asInstanceOf[s.Out] def asSubRecord: Rec = val resContent = new Array[Object](recFields.size) recFields.zipWithIndex `foreach` ((f, i) => resContent(i) = r.productElement(fieldIndices(f)).asInstanceOf[Object]) scala.runtime.Tuples.fromArray(resContent).asInstanceOf[Rec] } transparent inline given isSubRow[T <: Product, Rec <: Tuple] (using gen: Generic[T]): SubRow[T, Rec] = { inline erasedValue[MissingFields[Rec, FieldSet[gen.Out]]] match { case t: NonEmptyTuple => inline val mf = valueOf[TupleReduce[FieldNames[t.type], String, [a <: String, b <: String] =>> a + "\n " + b]] error("You have missing fields:\n " + mf + "\nEnsure that these fields are present and that their types match") // error("You have missing fields:\nEnsure that these fields are present and that their types match") case _ => } val labels = summonAll[TupleMap[gen.Out, @@[String & Singleton, Any], [x <: @@[String & Singleton, Any]] =>> ValueOf[FieldName[x]]]].toArray val recFields = summonAll[TupleMap[Rec, @@[String & Singleton, Any], [x <: @@[String & Singleton, Any]] =>> ValueOf[FieldName[x]]]].toIArray.map(_.asInstanceOf[ValueOf[String]].value) (SubRowImpl[T, Rec](labels.map(_.asInstanceOf[ValueOf[String]].value).zipWithIndex.to(collection.immutable.TreeSeqMap), recFields): SubRow[T, Rec]) } } } 

Output (click arrow to expand)

unhandled exception while running MegaPhase{crossVersionChecks, firstTransform, checkReentrant, elimPackagePrefixes, cookComments, checkLoopingImplicits, betaReduce, inlineVals, expandSAMs, elimRepeated, refchecks, dropForMap} on /tmp/genrec/genrec.scala

An unhandled exception was thrown in the compiler.
Please file a crash report here:
https://github.com/scala/scala3/issues/new/choose
For non-enriched exceptions, compile with -Xno-enrich-error-messages.

 while compiling: /tmp/genrec/genrec.scala during phase: MegaPhase{crossVersionChecks, firstTransform, checkReentrant, elimPackagePrefixes, cookComments, checkLoopingImplicits, betaReduce, inlineVals, expandSAMs, elimRepeated, refchecks, dropForMap} mode: Mode(ImplicitsEnabled) library version: version 2.13.16 compiler version: version 3.7.0 settings: -classpath /home/user/.cache/coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-library_3/3.7.0/scala3-library_3-3.7.0.jar:/home/user/.cache/coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-library/2.13.16/scala-library-2.13.16.jar -d /tmp/genrec/.scala-build/genrec_71ae519d80/classes/main -sourceroot /tmp/genrec 

Exception in thread "main" java.lang.AssertionError: assertion failed: TypeBounds(TypeRef(ThisType(TypeRef(NoPrefix,module class scala)),class Nothing),TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class )),object scala),trait Tuple))
at scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:8)
at dotty.tools.dotc.core.Types$TypeBounds.(Types.scala:5554)
at dotty.tools.dotc.core.Types$RealTypeBounds.(Types.scala:5631)
at dotty.tools.dotc.core.Types$TypeBounds$.apply(Types.scala:5672)
at dotty.tools.dotc.core.Types$TypeBounds.derivedTypeBounds(Types.scala:5562)
at dotty.tools.dotc.core.Types$ApproximatingTypeMap.derivedTypeBounds(Types.scala:6503)
at dotty.tools.dotc.core.Types$TypeMap.mapOver(Types.scala:6215)
at dotty.tools.dotc.core.TypeOps$AsSeenFromMap.apply(TypeOps.scala:111)
at dotty.tools.dotc.core.TypeOps$AsSeenFromMap.apply(TypeOps.scala:69)
at scala.collection.immutable.List.mapConserve(List.scala:473)
at dotty.tools.dotc.core.Types$TypeMap.mapOverLambda(Types.scala:6158)
at dotty.tools.dotc.core.TypeOps$AsSeenFromMap.apply(TypeOps.scala:105)
at dotty.tools.dotc.core.TypeOps$.asSeenFrom(TypeOps.scala:55)
at dotty.tools.dotc.core.Types$Type.asSeenFrom(Types.scala:1113)
at dotty.tools.dotc.core.Denotations$SingleDenotation.derived$1(Denotations.scala:1107)
at dotty.tools.dotc.core.Denotations$SingleDenotation.computeAsSeenFrom(Denotations.scala:1134)
at dotty.tools.dotc.core.Denotations$SingleDenotation.computeAsSeenFrom(Denotations.scala:1087)
at dotty.tools.dotc.core.Denotations$PreDenotation.asSeenFrom(Denotations.scala:137)
at dotty.tools.dotc.core.SymDenotations$ClassDenotation.findMember(SymDenotations.scala:2194)
at dotty.tools.dotc.core.Types$Type.go$1(Types.scala:778)
at dotty.tools.dotc.core.Types$Type.findMember(Types.scala:959)
at dotty.tools.dotc.core.Types$Type.memberBasedOnFlags(Types.scala:751)
at dotty.tools.dotc.core.Types$Type.nonPrivateMember(Types.scala:741)
at dotty.tools.dotc.typer.RefChecks$.hidden$1(RefChecks.scala:1190)
at dotty.tools.dotc.typer.RefChecks$.checkExtensionMethods(RefChecks.scala:1202)
at dotty.tools.dotc.typer.RefChecks.transformDefDef(RefChecks.scala:1356)
at dotty.tools.dotc.typer.RefChecks.transformDefDef(RefChecks.scala:1351)
at dotty.tools.dotc.transform.MegaPhase.goDefDef(MegaPhase.scala:1041)
at dotty.tools.dotc.transform.MegaPhase.goDefDef(MegaPhase.scala:1042)
at dotty.tools.dotc.transform.MegaPhase.goDefDef(MegaPhase.scala:1042)
at dotty.tools.dotc.transform.MegaPhase.goDefDef(MegaPhase.scala:1042)
at dotty.tools.dotc.transform.MegaPhase.goDefDef(MegaPhase.scala:1042)
at dotty.tools.dotc.transform.MegaPhase.transformNamed$1(MegaPhase.scala:268)
at dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:452)
at dotty.tools.dotc.transform.MegaPhase.loop$1(MegaPhase.scala:465)
at dotty.tools.dotc.transform.MegaPhase.transformStats(MegaPhase.scala:465)
at dotty.tools.dotc.transform.MegaPhase.transformUnnamed$1(MegaPhase.scala:376)
at dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:454)
at dotty.tools.dotc.transform.MegaPhase.transformNamed$1(MegaPhase.scala:272)
at dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:452)
at dotty.tools.dotc.transform.MegaPhase.loop$1(MegaPhase.scala:465)
at dotty.tools.dotc.transform.MegaPhase.transformStats(MegaPhase.scala:465)
at dotty.tools.dotc.transform.MegaPhase.transformUnnamed$1(MegaPhase.scala:376)
at dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:454)
at dotty.tools.dotc.transform.MegaPhase.transformNamed$1(MegaPhase.scala:272)
at dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:452)
at dotty.tools.dotc.transform.MegaPhase.loop$1(MegaPhase.scala:465)
at dotty.tools.dotc.transform.MegaPhase.transformStats(MegaPhase.scala:465)
at dotty.tools.dotc.transform.MegaPhase.mapPackage$1(MegaPhase.scala:396)
at dotty.tools.dotc.transform.MegaPhase.transformUnnamed$1(MegaPhase.scala:399)
at dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:454)
at dotty.tools.dotc.transform.MegaPhase.transformUnit(MegaPhase.scala:481)
at dotty.tools.dotc.transform.MegaPhase.run(MegaPhase.scala:493)
at dotty.tools.dotc.core.Phases$Phase.runOn$$anonfun$1(Phases.scala:383)
at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
at scala.collection.immutable.List.foreach(List.scala:334)
at dotty.tools.dotc.core.Phases$Phase.runOn(Phases.scala:376)
at dotty.tools.dotc.Run.runPhases$1$$anonfun$1(Run.scala:367)
at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
at scala.collection.ArrayOps$.foreach$extension(ArrayOps.scala:1324)
at dotty.tools.dotc.Run.runPhases$1(Run.scala:360)
at dotty.tools.dotc.Run.compileUnits$$anonfun$1$$anonfun$2(Run.scala:407)
at dotty.tools.dotc.Run.compileUnits$$anonfun$1$$anonfun$adapted$1(Run.scala:407)
at scala.Function0.apply$mcV$sp(Function0.scala:42)
at dotty.tools.dotc.Run.showProgress(Run.scala:469)
at dotty.tools.dotc.Run.compileUnits$$anonfun$1(Run.scala:407)
at dotty.tools.dotc.Run.compileUnits$$anonfun$adapted$1(Run.scala:419)
at dotty.tools.dotc.util.Stats$.maybeMonitored(Stats.scala:69)
at dotty.tools.dotc.Run.compileUnits(Run.scala:419)
at dotty.tools.dotc.Run.compileSources(Run.scala:306)
at dotty.tools.dotc.Run.compile(Run.scala:291)
at dotty.tools.dotc.Driver.doCompile(Driver.scala:37)
at dotty.tools.dotc.Driver.process(Driver.scala:201)
at dotty.tools.dotc.Driver.process(Driver.scala:169)
at dotty.tools.dotc.Driver.process(Driver.scala:181)
at dotty.tools.dotc.Driver.main(Driver.scala:211)
at dotty.tools.dotc.Main.main(Main.scala)
Compilation failed

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions