@@ -523,21 +523,47 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
523
523
* ```
524
524
* type MirroredMonoType = C[?]
525
525
* ```
526
+ *
527
+ * However, if the last parameter is annotated `@unroll` then we generate:
528
+ *
529
+ * def fromProduct(x$0: Product): MirroredMonoType =
530
+ * val arity = x$0.productArity
531
+ * val a$1 = x$0.productElement(0).asInstanceOf[U]
532
+ * val b$1 = x$0.productElement(1).asInstanceOf[a$1.Elem]
533
+ * val c$1 = (
534
+ * if arity > 2 then
535
+ * x$0.productElement(2)
536
+ * else
537
+ * <default getter for the third parameter of C>
538
+ * ).asInstanceOf[a$1.Elem]
539
+ * new C[U](a$1, b$1, c$1*)
526
540
*/
527
541
def fromProductBody (caseClass : Symbol , productParam : Tree , optInfo : Option [MirrorImpl .OfProduct ])(using Context ): Tree =
528
542
val classRef = optInfo match
529
543
case Some (info) => TypeRef (info.pre, caseClass)
530
544
case _ => caseClass.typeRef
531
- val (newPrefix, constrMeth) =
545
+ val (newPrefix, constrMeth, constrSyms ) =
532
546
val constr = TermRef (classRef, caseClass.primaryConstructor)
547
+ val symss = caseClass.primaryConstructor.paramSymss
533
548
(constr.info: @ unchecked) match
534
549
case tl : PolyType =>
535
550
val tvars = constrained(tl)
536
551
val targs = for tvar <- tvars yield
537
552
tvar.instantiate(fromBelow = false )
538
- (AppliedType (classRef, targs), tl.instantiate(targs).asInstanceOf [MethodType ])
553
+ (AppliedType (classRef, targs), tl.instantiate(targs).asInstanceOf [MethodType ], symss( 1 ) )
539
554
case mt : MethodType =>
540
- (classRef, mt)
555
+ (classRef, mt, symss.head)
556
+
557
+ // Index of the first parameter marked `@unroll` or -1
558
+ val unrolledFrom =
559
+ constrSyms.indexWhere(_.hasAnnotation(defn.UnrollAnnot ))
560
+
561
+ // `val arity = x$0.productArity`
562
+ val arityDef : Option [ValDef ] =
563
+ if unrolledFrom != - 1 then
564
+ Some (SyntheticValDef (nme.arity, productParam.select(defn.Product_productArity )))
565
+ else None
566
+ val arityRefTree = arityDef.map(vd => ref(vd.symbol))
541
567
542
568
// Create symbols for the vals corresponding to each parameter
543
569
// If there are dependent parameters, the infos won't be correct yet.
@@ -550,16 +576,29 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
550
576
bindingSyms.foreach: bindingSym =>
551
577
bindingSym.info = bindingSym.info.substParams(constrMeth, bindingRefs)
552
578
579
+ def defaultGetterAtIndex (idx : Int ): Tree =
580
+ val defaultGetterPrefix = caseClass.primaryConstructor.name.toTermName
581
+ ref(caseClass.companionModule).select(NameKinds .DefaultGetterName (defaultGetterPrefix, idx))
582
+
553
583
val bindingDefs = bindingSyms.zipWithIndex.map: (bindingSym, idx) =>
554
- ValDef (bindingSym,
555
- productParam.select(defn.Product_productElement ).appliedTo(Literal (Constant (idx)))
556
- .ensureConforms(bindingSym.info))
584
+ val selection = productParam.select(defn.Product_productElement ).appliedTo(Literal (Constant (idx)))
585
+ val rhs = (
586
+ if unrolledFrom != - 1 && idx >= unrolledFrom then
587
+ If (arityRefTree.get.select(defn.Int_> ).appliedTo(Literal (Constant (idx))),
588
+ thenp =
589
+ selection,
590
+ elsep =
591
+ defaultGetterAtIndex(idx))
592
+ else
593
+ selection
594
+ ).ensureConforms(bindingSym.info)
595
+ ValDef (bindingSym, rhs)
557
596
558
597
val newArgs = bindingRefs.lazyZip(constrMeth.paramInfos).map: (bindingRef, paramInfo) =>
559
598
val refTree = ref(bindingRef)
560
599
if paramInfo.isRepeatedParam then ctx.typer.seqToRepeated(refTree) else refTree
561
600
Block (
562
- bindingDefs,
601
+ arityDef.map(_ :: bindingDefs).getOrElse(bindingDefs) ,
563
602
New (newPrefix, newArgs)
564
603
)
565
604
end fromProductBody
0 commit comments