@@ -504,53 +504,103 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
504
504
/** The class
505
505
*
506
506
* ```
507
- * case class C[T <: U](x: T, y: String*)
507
+ * trait U:
508
+ * type Elem
509
+ *
510
+ * case class C[T <: U](a: T, b: a.Elem, c: String*)
508
511
* ```
509
512
*
510
513
* gets the `fromProduct` method:
511
514
*
512
515
* ```
513
516
* def fromProduct(x$0: Product): MirroredMonoType =
514
- * new C[U](
515
- * x$0.productElement(0).asInstanceOf[U],
516
- * x$0.productElement(1).asInstanceOf[Seq[String]]: _*)
517
+ * val a$1 = x$0.productElement(0).asInstanceOf[U]
518
+ * val b$1 = x$0.productElement(1).asInstanceOf[a$1.Elem]
519
+ * val c$1 = x$0.productElement(2).asInstanceOf[Seq[String]]
520
+ * new C[U](a$1, b$1, c$1*)
517
521
* ```
518
522
* where
519
523
* ```
520
524
* type MirroredMonoType = C[?]
521
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[Seq[String]]
539
+ * new C[U](a$1, b$1, c$1*)
522
540
*/
523
- def fromProductBody (caseClass : Symbol , param : Tree , optInfo : Option [MirrorImpl .OfProduct ])(using Context ): Tree =
524
- def extractParams (tpe : Type ): List [Type ] =
525
- tpe.asInstanceOf [MethodType ].paramInfos
526
-
527
- def computeFromCaseClass : (Type , List [Type ]) =
528
- val (baseRef, baseInfo) =
529
- val rawRef = caseClass.typeRef
530
- val rawInfo = caseClass.primaryConstructor.info
531
- optInfo match
532
- case Some (info) =>
533
- (rawRef.asSeenFrom(info.pre, caseClass.owner), rawInfo.asSeenFrom(info.pre, caseClass.owner))
534
- case _ =>
535
- (rawRef, rawInfo)
536
- baseInfo match
541
+ def fromProductBody (caseClass : Symbol , productParam : Tree , optInfo : Option [MirrorImpl .OfProduct ])(using Context ): Tree =
542
+ val classRef = optInfo match
543
+ case Some (info) => TypeRef (info.pre, caseClass)
544
+ case _ => caseClass.typeRef
545
+ val (newPrefix, constrMeth, constrSyms) =
546
+ val constr = TermRef (classRef, caseClass.primaryConstructor)
547
+ val symss = caseClass.primaryConstructor.paramSymss
548
+ (constr.info: @ unchecked) match
537
549
case tl : PolyType =>
538
550
val tvars = constrained(tl)
539
551
val targs = for tvar <- tvars yield
540
552
tvar.instantiate(fromBelow = false )
541
- (baseRef.appliedTo(targs), extractParams(tl.instantiate(targs)))
542
- case methTpe =>
543
- (baseRef, extractParams(methTpe))
544
- end computeFromCaseClass
545
-
546
- val (classRefApplied, paramInfos) = computeFromCaseClass
547
- val elems =
548
- for ((formal, idx) <- paramInfos.zipWithIndex) yield
549
- val elem =
550
- param.select(defn.Product_productElement ).appliedTo(Literal (Constant (idx)))
551
- .ensureConforms(formal.translateFromRepeated(toArray = false ))
552
- if (formal.isRepeatedParam) ctx.typer.seqToRepeated(elem) else elem
553
- New (classRefApplied, elems)
553
+ (AppliedType (classRef, targs), tl.instantiate(targs).asInstanceOf [MethodType ], symss(1 ))
554
+ case mt : MethodType =>
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 ).withSpan(ctx.owner.span.focus)))
565
+ else None
566
+ val arityRefTree = arityDef.map(vd => ref(vd.symbol))
567
+
568
+ // Create symbols for the vals corresponding to each parameter
569
+ // If there are dependent parameters, the infos won't be correct yet.
570
+ val bindingSyms = constrMeth.paramRefs.map: pref =>
571
+ newSymbol(ctx.owner, pref.paramName.freshened, Synthetic ,
572
+ pref.underlying.translateFromRepeated(toArray = false ), coord = ctx.owner.span.focus)
573
+ val bindingRefs = bindingSyms.map(TermRef (NoPrefix , _))
574
+ // Fix the infos for dependent parameters
575
+ if constrMeth.isParamDependent then
576
+ bindingSyms.foreach: bindingSym =>
577
+ bindingSym.info = bindingSym.info.substParams(constrMeth, bindingRefs)
578
+
579
+ def defaultGetterAtIndex (idx : Int ): Tree =
580
+ val defaultGetterPrefix = caseClass.primaryConstructor.name.toTermName
581
+ ref(caseClass.companionModule).select(NameKinds .DefaultGetterName (defaultGetterPrefix, idx))
582
+
583
+ val bindingDefs = bindingSyms.zipWithIndex.map: (bindingSym, idx) =>
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)
596
+
597
+ val newArgs = bindingRefs.lazyZip(constrMeth.paramInfos).map: (bindingRef, paramInfo) =>
598
+ val refTree = ref(bindingRef)
599
+ if paramInfo.isRepeatedParam then ctx.typer.seqToRepeated(refTree) else refTree
600
+ Block (
601
+ arityDef.toList ::: bindingDefs,
602
+ New (newPrefix, newArgs)
603
+ )
554
604
end fromProductBody
555
605
556
606
/** For an enum T:
0 commit comments