The sharpest lego: infinite recursion in ExpressibleByDictionaryLiteral

ExpressibleByDictionaryLiteral and its sibling ExpressibleByArrayLiteral have variadic requirement signatures:

init(dictionaryLiteral:(Key, Value)...)
init(arrayLiteral:Element...)

one unfortunate consequence i have come to appreciate is that it is really easy to accidentally cause infinite recursion when you want to enable empty dictionary literal syntax ([:]) and have it delegate to a parameterless init, such as:

public
protocol DocumentProtocol
{
    ...
}
extension DocumentProtocol
{
    @inlinable public
    init()
    {
        self.init(Storage.init())
    }
}
extension DocumentProtocol
    where Self:ExpressibleByDictionaryLiteral, Key == Never, Value == Never
{
    @inlinable public
    init(dictionaryLiteral:(Never, Never)...)
    {
        self.init()
    }
}

oops! this doesn’t call the Self.init protocol extension, it calls itself with zero variadic arguments! and it doesn’t produce the usual “infinite recursion” diagnostic because it is a protocol extension.

i’m not really sure what the best way to prevent this from happening is. but for me at least, when i encounter a segfault, empty dictionary literals are not very high on my list of suspects…

2 Likes

Is it possible to write something convoluted like (self.init as () -> _)() or self[keyPath: \.init]()? I would be unsurprised if those are both compile errors.

The alternative, of course, is to do this:

extension DocumentProtocol {
  public init() { … }

  @inline(__always)
  internal init(_default: Void) { self.init() }
}

extension DocumentProtocol
where Self: ExpressibleByDictionaryLiteral, Key == Never, Value == Never {
  public init(dictionaryLiteral: (Never, Never)...) {
    self.init(_default: ())
  }
}