[swift-evolution] Proposal for Passing Arrays to Variadic Functions

Kevin Ballard kevin at sb.org
Sun Dec 13 17:53:33 CST 2015

On Sun, Dec 13, 2015, at 03:00 PM, Chris Lattner wrote:
> > On Dec 11, 2015, at 10:54 PM, Kevin Ballard via swift-evolution <swift-evolution at swift.org> wrote:
> > 
> > You can't use * as suggested, because that conflicts with * as a prefix operator.
> >  
> > You could probably get away with using a trailing ... in the call, as in
> >  
> > func foo(xs: [Int]...) {
> >     bar(xs...)
> > }
> >  
> > I also wonder whether there's a reason Swift doesn't already support this. Maybe there's some desired future change to variadic arguments to improve performance (i.e. to avoid having to allocate an array) that wouldn't be compatible? Although I'm not sure what that would be. Maybe there's actually no barrier at all and it was just something that wasn't prioritized. It would be good to know either way.
> This hasn’t been done just because it has never risen to the highest priority, not because we’re against it.  In addition to Joe Groff’s downstream comment, one change we need to discuss: requiring the formation of a formal Array is somewhat unfortunate from a performance perspective.  It would make more sense to pass varargs by allocating the array as a fixed size buffer in the callers stack, and passing a pointer+length down to the callee.  The callee could then wrap that up and vend it with a formal type of Array or perhaps just SequenceType.  In the case where the sequence doesn’t escape, this would allow the allocation/deallocation + copy to the heap to be eliminated.

It would actually be pretty neat if we could do this not as a special case of variadic arguments, but as a general case of passing an array to any function. If the array never escapes the function call and it only reads from it, then having the array point to the stack frame of the caller would be great. Even mutation that doesn't increase the array size could be done in-place on the stack. Any action that might cause the array to escape the function (or any mutation that increases its size) could then move it to the heap. Mutation doesn't even need special handling; since the capacity is equal to the size, it'll move to the heap on any size increase automatically (and size decreases is fine, as the array will manage destruction of the elements). All the compiler really has to do is insert a call that forces a move-to-heap prior to any action that may cause the array to escape the function (e.g. assigning to a property, or capturing in an escaping closure).

If that function call is sufficiently cheap (and it should basically just be testing a boolean flag), then hopefully it's cheap enough to use everywhere for arrays passed as parameters to functions. If it turns out to actually be an issue then maybe we could use an attribute to annotate array parameters that allow for stack allocation, but that would mean it's used pretty rarely as people will usually not bother (except when it's automatically added for varargs).

-Kevin Ballard

More information about the swift-evolution mailing list