@@ -322,6 +322,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
322322 case ClassSymbol (pre : Type , cls : Symbol )
323323 case Singleton (src : Symbol , tref : TermRef )
324324 case GenericTuple (tps : List [Type ])
325+ case NamedTuple (nameTypePairs : List [(TermName , Type )])
325326
326327 /** Tests that both sides are tuples of the same arity */
327328 infix def sameTuple (that : MirrorSource )(using Context ): Boolean =
@@ -351,6 +352,11 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
351352 val arity = tps.size
352353 if arity <= Definitions .MaxTupleArity then s " class Tuple $arity"
353354 else s " trait Tuple { def size: $arity } "
355+ case NamedTuple (nameTypePairs) =>
356+ val (names, types) = nameTypePairs.unzip
357+ val namesStr = names.map(_.show).mkString(" (\" " , " \" , \" " , " \" )" )
358+ val typesStr = types.map(_.show).mkString(" (" , " , " , " )" )
359+ s " NamedTuple.NamedTuple[ ${namesStr}, ${typesStr}] "
354360
355361 private [Synthesizer ] object MirrorSource :
356362
@@ -398,6 +404,8 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
398404 // avoid type aliases for tuples
399405 Right (MirrorSource .GenericTuple (types))
400406 case _ => reduce(tp.underlying)
407+ case defn.NamedTupleDirect (_, _) =>
408+ Right (MirrorSource .NamedTuple (tp.namedTupleElementTypes(derived = false )))
401409 case tp : MatchType =>
402410 val n = tp.tryNormalize
403411 if n.exists then reduce(n) else Left (i " its subpart ` $tp` is an unreducible match type. " )
@@ -428,10 +436,25 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
428436 def newTupleMirror (arity : Int ): Tree =
429437 New (defn.RuntimeTupleMirrorTypeRef , Literal (Constant (arity)) :: Nil )
430438
431- def makeProductMirror (pre : Type , cls : Symbol , tps : Option [List [Type ]]): TreeWithErrors =
439+ def makeNamedTupleProductMirror (nameTypePairs : List [(TermName , Type )]): TreeWithErrors =
440+ val (labels, typeElems) = nameTypePairs.unzip
441+ val elemLabels = labels.map(label => ConstantType (Constant (label.toString)))
442+ val mirrorRef : Type => Tree = _ => newTupleMirror(typeElems.size)
443+ makeProductMirror(typeElems, elemLabels, tpnme.NamedTuple , mirrorRef)
444+ end makeNamedTupleProductMirror
445+
446+ def makeClassProductMirror (pre : Type , cls : Symbol , tps : Option [List [Type ]]) =
432447 val accessors = cls.caseAccessors
433448 val elemLabels = accessors.map(acc => ConstantType (Constant (acc.name.toString)))
434449 val typeElems = tps.getOrElse(accessors.map(mirroredType.resultType.memberInfo(_).widenExpr))
450+ val mirrorRef = (monoType : Type ) =>
451+ if cls.useCompanionAsProductMirror then companionPath(pre, cls, span)
452+ else if defn.isTupleClass(cls) then newTupleMirror(typeElems.size) // TODO: cls == defn.PairClass when > 22
453+ else anonymousMirror(monoType, MirrorImpl .OfProduct (pre), span)
454+ makeProductMirror(typeElems, elemLabels, cls.name, mirrorRef)
455+ end makeClassProductMirror
456+
457+ def makeProductMirror (typeElems : List [Type ], elemLabels : List [Type ], label : Name , mirrorRef : Type => Tree ): TreeWithErrors =
435458 val nestedPairs = TypeOps .nestedPairs(typeElems)
436459 val (monoType, elemsType) = mirroredType match
437460 case mirroredType : HKTypeLambda =>
@@ -442,15 +465,11 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
442465 checkRefinement(formal, tpnme.MirroredElemTypes , elemsType, span)
443466 checkRefinement(formal, tpnme.MirroredElemLabels , elemsLabels, span)
444467 val mirrorType = formal.constrained_& {
445- mirrorCore(defn.Mirror_ProductClass , monoType, mirroredType, cls.name )
468+ mirrorCore(defn.Mirror_ProductClass , monoType, mirroredType, label )
446469 .refinedWith(tpnme.MirroredElemTypes , TypeAlias (elemsType))
447470 .refinedWith(tpnme.MirroredElemLabels , TypeAlias (elemsLabels))
448471 }
449- val mirrorRef =
450- if cls.useCompanionAsProductMirror then companionPath(pre, cls, span)
451- else if defn.isTupleClass(cls) then newTupleMirror(typeElems.size) // TODO: cls == defn.PairClass when > 22
452- else anonymousMirror(monoType, MirrorImpl .OfProduct (pre), span)
453- withNoErrors(mirrorRef.cast(mirrorType).withSpan(span))
472+ withNoErrors(mirrorRef(monoType).cast(mirrorType).withSpan(span))
454473 end makeProductMirror
455474
456475 MirrorSource .reduce(mirroredType) match
@@ -474,10 +493,12 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
474493 val arity = tps.size
475494 if tps.size <= maxArity then
476495 val tupleCls = defn.TupleType (arity).nn.classSymbol
477- makeProductMirror (tupleCls.owner.reachableThisType, tupleCls, Some (tps))
496+ makeClassProductMirror (tupleCls.owner.reachableThisType, tupleCls, Some (tps))
478497 else
479498 val reason = s " it reduces to a tuple with arity $arity, expected arity <= $maxArity"
480499 withErrors(i " ${defn.PairClass } is not a generic product because $reason" )
500+ case MirrorSource .NamedTuple (nameTypePairs) =>
501+ makeNamedTupleProductMirror(nameTypePairs)
481502 case MirrorSource .ClassSymbol (pre, cls) =>
482503 if cls.isGenericProduct then
483504 if ctx.runZincPhases then
@@ -486,7 +507,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
486507 val rec = ctx.compilationUnit.depRecorder
487508 rec.addClassDependency(cls, DependencyByMemberRef )
488509 rec.addUsedName(cls.primaryConstructor)
489- makeProductMirror (pre, cls, None )
510+ makeClassProductMirror (pre, cls, None )
490511 else withErrors(i " $cls is not a generic product because ${cls.whyNotGenericProduct}" )
491512 case Left (msg) =>
492513 withErrors(i " type ` $mirroredType` is not a generic product because $msg" )
@@ -501,6 +522,8 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
501522 val arity = tps.size
502523 val cls = if arity <= Definitions .MaxTupleArity then defn.TupleType (arity).nn.classSymbol else defn.PairClass
503524 (" " , NoType , cls)
525+ case Right (MirrorSource .NamedTuple (_)) =>
526+ (" named tuples are not sealed classes" , NoType , NoSymbol )
504527 case Left (msg) => (msg, NoType , NoSymbol )
505528
506529 val clsIsGenericSum = cls.isGenericSum(pre)
0 commit comments