@@ -19,6 +19,9 @@ import config.Feature
19
19
import util .{SrcPos , Stats }
20
20
import reporting .*
21
21
import NameKinds .WildcardParamName
22
+ import typer .Applications .{spread , HasSpreads }
23
+ import typer .Implicits .SearchFailureType
24
+ import Constants .Constant
22
25
import cc .*
23
26
import dotty .tools .dotc .transform .MacroAnnotations .hasMacroAnnotation
24
27
import dotty .tools .dotc .core .NameKinds .DefaultGetterName
@@ -376,6 +379,73 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase =>
376
379
case _ =>
377
380
tpt
378
381
382
+ /** Translate sequence literal containing spread operators. Example:
383
+ *
384
+ * val xs, ys: List[Int]
385
+ * [1, xs*, 2, ys*]
386
+ *
387
+ * Here the sequence literal is translated at typer to
388
+ *
389
+ * [1, spread(xs), 2, spread(ys)]
390
+ *
391
+ * This then translates to
392
+ *
393
+ * scala.runtime.ArraySeqBuilcder.ofInt(2 + xs.length + ys.length)
394
+ * .add(1)
395
+ * .addSeq(xs)
396
+ * .add(2)
397
+ * .addSeq(ys)
398
+ *
399
+ * The reason for doing a two-step typer/postTyper translation is that
400
+ * at typer, we don't have all type variables instantiated yet.
401
+ */
402
+ private def flattenSpreads [T ](tree : SeqLiteral )(using Context ): Tree =
403
+ val SeqLiteral (elems, elemtpt) = tree
404
+ val elemType = elemtpt.tpe
405
+ val elemCls = elemType.classSymbol
406
+
407
+ val lengthCalls = elems.collect:
408
+ case spread(elem) => elem.select(nme.length)
409
+ val singleElemCount : Tree = Literal (Constant (elems.length - lengthCalls.length))
410
+ val totalLength =
411
+ lengthCalls.foldLeft(singleElemCount): (acc, len) =>
412
+ acc.select(defn.Int_+ ).appliedTo(len)
413
+
414
+ def makeBuilder (name : String ) =
415
+ ref(defn.ArraySeqBuilderModule ).select(name.toTermName)
416
+ def genericBuilder = makeBuilder(" generic" )
417
+ .appliedToType(elemType)
418
+ .appliedTo(totalLength)
419
+
420
+ val builder =
421
+ if defn.ScalaValueClasses ().contains(elemCls) then
422
+ makeBuilder(s " of ${elemCls.name}" ).appliedTo(totalLength)
423
+ else if elemCls.derivesFrom(defn.ObjectClass ) then
424
+ val classTagType = defn.ClassTagClass .typeRef.appliedTo(elemType)
425
+ val classTag = atPhase(Phases .typerPhase):
426
+ ctx.typer.inferImplicitArg(classTagType, tree.span.startPos)
427
+ classTag.tpe match
428
+ case _ : SearchFailureType =>
429
+ genericBuilder
430
+ case _ =>
431
+ makeBuilder(" ofRef" )
432
+ .appliedToType(elemType)
433
+ .appliedTo(totalLength)
434
+ .appliedTo(classTag)
435
+ else
436
+ genericBuilder
437
+
438
+ elems.foldLeft(builder): (bldr, elem) =>
439
+ elem match
440
+ case spread(arg) =>
441
+ val selector =
442
+ if arg.tpe.derivesFrom(defn.SeqClass ) then " addSeq"
443
+ else " addArray"
444
+ bldr.select(selector.toTermName).appliedTo(arg)
445
+ case _ => bldr.select(" add" .toTermName).appliedTo(elem)
446
+ .select(" result" .toTermName)
447
+ end flattenSpreads
448
+
379
449
override def transform (tree : Tree )(using Context ): Tree =
380
450
try tree match {
381
451
// TODO move CaseDef case lower: keep most probable trees first for performance
@@ -592,6 +662,8 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase =>
592
662
case tree : RefinedTypeTree =>
593
663
Checking .checkPolyFunctionType(tree)
594
664
super .transform(tree)
665
+ case tree : SeqLiteral if tree.hasAttachment(HasSpreads ) =>
666
+ flattenSpreads(tree)
595
667
case _ : Quote | _ : QuotePattern =>
596
668
ctx.compilationUnit.needsStaging = true
597
669
super .transform(tree)
0 commit comments