ExpressibleByStringLiteral introduces unexpected init(_:)

I have a type conforming to both RawRepresentable and CustomStringConvertible, but something strange happens when I introduce the latter conformance โ€” apart from the expected init(rawValue:) and init(stringLiteral:) it seems to gain a non-failable init(_:String) which calls init(stringLiteral:).
This even overrides any init with anonymous argument defined explicitly for that type; but not if that init is called explicitly with S.init("bla") as opposed to S("bla").

struct S: RawRepresentable {
    var rawValue: String

    init?(rawValue: String) {
        guard rawValue.count > 3 else { return nil }
        self.rawValue = rawValue
    }
}

extension S {
    init(_ value: String) {
        self.rawValue = "Called from extra non-failable init with anonymous argument."
    }
}

extension S: ExpressibleByStringLiteral {
    init(stringLiteral value: RawValue.StringLiteralType) {
        self.rawValue = .init(stringLiteral: value)
    }
}

extension S: CustomStringConvertible {
    var description: String { rawValue }
}

let tooShort = S(rawValue: "abc") // nil
let longEnough = S(rawValue: "abcdef") // "abcdef"
let fromStringLiteral: S = "abc123" // "abc123"
let what = S("What?") // "What?"
let extra = S.init("Hmm") // "Called from extra non-failable init with anonymous argument."

Where does this behaviour come from โ€” and why?

I think you're running into swift-evolution/proposals/0213-literal-init-via-coercion.md at main ยท swiftlang/swift-evolution ยท GitHub

4 Likes

Is this documented somewhere? I wonder if this proposal wasn't a bit too far-reaching by also applying to user-written ExpressibleByStringLiteral conformances and not just to the standard library's numeric types.