Optimise array literal with one element to CollectionOfOne in a generic context

Given a generic function that takes a Sequence as an argument, like this one:

func printTypeOfSequence<S: Sequence>(_ s: S) {
    print(type(of: s))
}

and a call with an array literal with just one element, like this:

printTypeOfSequence([1])

I would expect that it prints CollectionOfOne<Int> but it prints Array<Int>.
Would the compiler be allowed to do this kind of optimisation?

1 Like

Hopefully not in this particular example, because it's an observable change in semantics as you demonstrated by printing the type. Perhaps it could be done in situations where the difference isn't observable, though.

2 Likes

No, the type of [1] is guaranteed to be Array in that context.

1 Like

CollectionOfOne does not conform to ExpressibleByArrayLiteral anyway, so the compiler won't use CollectionOfOne<Int> for [1], at least not visibly.

3 Likes

To be fair, Swift could (does) have optimizations that essentially do the equivalent of what is being suggested, by way of stack-allocating the array buffer and eliminating some of Array's abstractions. But as @jawbroken points out, it can only do this in (mostly) non-user-observable ways so it would never change what's happening at the level of the type system. It also needs full visibility of the code inside printTypeOfSequence to make sure e.g. the array doesn't escape.

4 Likes

It seems impossible to emulate splatting, too:

extension CollectionOfOne: ExpressibleByIntegerLiteral where Element: _ExpressibleByBuiltinIntegerLiteral {
  public init(integerLiteral element: Element) {
    self.init(element)
  }
}
// Compiles.
printTypeOfSequence(0 as CollectionOfOne<Int>)

// Does not compile.
printTypeOfSequence(0)
printTypeOfSequence(.init(integerLiteral: 0))

But, when splatting becomes possible (i.e. ... becomes obsolete), using a literal of a single element has no possibility to break code, right?

1 Like