Skip to content

Conversation

odersky
Copy link
Contributor

@odersky odersky commented Sep 1, 2025

Implements SIP 70. Currently, only Seqs and Arrays can be unpacked whereas the SIP also specifies Option unpacking.

We need to access them twice because we first need to take their length, then
append them to the buffer. If a spread might have side effects, lift all side-effecting
arguments out in the order of occurrence.
@odersky odersky marked this pull request as ready for review September 18, 2025 21:10
@odersky odersky requested a review from a team as a code owner September 18, 2025 21:10
Copy link
Member

@sjrd sjrd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some comments on the implementation strategy. Overall it looks like the right design, but some details can be improved IMO.

then addArg(typedArg(arg, formal), formal)
else addArg(typedArg(arg, elemFormal), elemFormal)
else
val typedArgs = harmonic(harmonizeArgs, elemFormal):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICT, there is no equivalent code in the code path with multiSpreads. I understand it may be tricky, but it's at least worth calling out at the spec/SIP level. IIUC It means we get:

def foo[A](xs: A*): List[A] = xs.toList

foo(5, 5.6) // List[Double]
val doubles = List(5.6)
foo(5, doubles*) // List[AnyVal]

Basically having any spread in a vararg argument disables harmonization entirely.

@odersky
Copy link
Contributor Author

odersky commented Sep 19, 2025

@sjrd I got harmonization working for spreads and also implemented the other review suggestions

Copy link
Member

@sjrd sjrd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

I'm not sure what happens if we provide an Array[S] where S <: T. For example in Scala 3.7.3 we can do:

scala> def foo(x: CharSequence*): String = x.mkString
def foo(x: CharSequence*): String
                                                                                                              
scala> val strings: Array[String] = Array("foo", "bar")
val strings: Array[String] = Array(foo, bar)
                                                                                                              
scala> foo(strings*)
val res0: String = foobar

What is then the desugaring of

scala> foo(("oof": CharSequence), strings*)

It seems we can't call addArray(strings), because it requires an Array[CharSequence] and strings is an Array[String].

@odersky
Copy link
Contributor Author

odersky commented Sep 19, 2025

I'm not sure what happens if we provide an Array[S] where S <: T.

Good question! In fact it runs fine, but fails -Ycheck. To fix this, I changed the generated code to add a cast if needed. The example

foo(("oof": CharSequence), strings*)

now compiles to

        foo(
          scala.runtime.VarArgsBuilder.ofRef[CharSequence](1 + strings.length)
            .add("oof":CharSequence)
            .addArray(strings.$asInstanceOf[Array[CharSequence]]).result()*
        )

@sjrd
Copy link
Member

sjrd commented Sep 19, 2025

Ah yes, that works.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants