Skip to content

Partial block result builder fails to pick correct overload and generates compiler error #62978

@JaapWijnen

Description

@JaapWijnen

Description
Partial block result builder fails to pick correct overload and generates a compiler error Ambiguous use of 'parser(of:)'

Steps to reproduce
Paste the following code into a new project
(This issue is a reduction of trying to compile an example (final 4 lines of the code below) of the swift-parsing package)

//MARK: Parsers
@rethrows public protocol Parser<Input> {
    associatedtype Input
}

extension String: Parser {
    public typealias Input = Substring
}

extension Int {
    static func parser(
        of inputType: Substring.Type = Substring.self
    ) -> FromSubstringToUTF8<IntParser<Substring.UTF8View>> {
        FromSubstringToUTF8 { IntParser<Substring.UTF8View>() }
    }

    static func parser(
        of inputType: Substring.UTF8View.Type = Substring.UTF8View.self
    ) -> IntParser<Substring.UTF8View> {
        .init()
    }
}

struct FromSubstringToUTF8<P: Parser>: Parser where P.Input == Substring.UTF8View {
    typealias Input = Substring
    
    let parser: P
    
    init(@ParserBuilder _ build: () -> P) { self.parser = build() }
}

struct IntParser<Input: Collection>: Parser where Input.SubSequence == Input, Input.Element == UTF8.CodeUnit {
    public init() { }
}

//MARK: ParserBuilder
struct Parse<Parsers: Parser>: Parser {
    typealias Input = Parsers.Input

    let parsers: Parsers
        
    init(@ParserBuilder with build: () -> Parsers) { self.parsers = build() }
}

@resultBuilder
struct ParserBuilder {
    public static func buildPartialBlock<P: Parser>(first: P) -> P { first }

    public static func buildPartialBlock<P0, P1>(accumulated: P0, next: P1) -> SkipFirst<P0, P1> {
        .init(p0: accumulated, p1: next)
    }
}

struct SkipFirst<P0: Parser, P1: Parser>: Parser where P0.Input == P1.Input {
    typealias Input = P0.Input

    let p0: P0
    let p1: P1
}

//MARK: main
let first = ParserBuilder.buildPartialBlock(first: ",")
let second = ParserBuilder.buildPartialBlock(accumulated: first, next: Int.parser())

let parser = Parse {
    ","
    Int.parser() // Ambiguous use of 'parser(of:)'
}

Expected behavior
This compiles for toolchain 2023-01-02 but breaks from 2023-01-07.
Expected to compile and choose the correct overload (in this case static func parser(of inputType: Substring.Type = Substring.self))

Since the first parser passed to the resultBuilder closure is of type String therefore the Parser associated type Input is of type Substring. Therefore the compiler should be able to infer that the parser returned by Int.parser() should have an Input of Substring as well.

Calling the individual partialBuildBlock functions does compile
works:

let first = ParserBuilder.buildPartialBlock(first: ",")
let second = ParserBuilder.buildPartialBlock(accumulated: first, next: Int.parser())

fails:

let parser = Parse {
    ","
    Int.parser() // Ambiguous use of 'parser(of:)'
}

Environment

  • Swift compiler version info Swift Development Snapshot 2023-01-09
  • Xcode version info Xcode 13.4.1 Build version 13F100
  • Deployment target: M1 running macOS 12.5

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugA deviation from expected or documented behavior. Also: expected but undesirable behavior.compilerThe Swift compiler itselfnot a bugResolution → not a bug: Reported as a bug but turned out to be expected behavior or programmer errorregressionresult buildersFeature: Result builderstype checkerArea → compiler: Semantic analysis

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions