@@ -19,6 +19,9 @@ import config.Feature
1919import util .{SrcPos , Stats }
2020import reporting .*
2121import NameKinds .WildcardParamName
22+ import typer .Applications .{spread , HasSpreads }
23+ import typer .Implicits .SearchFailureType
24+ import Constants .Constant
2225import cc .*
2326import dotty .tools .dotc .transform .MacroAnnotations .hasMacroAnnotation
2427import dotty .tools .dotc .core .NameKinds .DefaultGetterName
@@ -376,6 +379,73 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase =>
376379 case _ =>
377380 tpt
378381
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+
379449 override def transform (tree : Tree )(using Context ): Tree =
380450 try tree match {
381451 // TODO move CaseDef case lower: keep most probable trees first for performance
@@ -592,6 +662,8 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase =>
592662 case tree : RefinedTypeTree =>
593663 Checking .checkPolyFunctionType(tree)
594664 super .transform(tree)
665+ case tree : SeqLiteral if tree.hasAttachment(HasSpreads ) =>
666+ flattenSpreads(tree)
595667 case _ : Quote | _ : QuotePattern =>
596668 ctx.compilationUnit.needsStaging = true
597669 super .transform(tree)
0 commit comments