swiftlang / swift

The Swift Programming Language
https://swift.org
Apache License 2.0
67.57k stars 10.36k forks source link

[SR-15427] Result builders plus implicit member syntax mix poorly #57733

Open swift-ci opened 3 years ago

swift-ci commented 3 years ago
Previous ID SR-15427
Radar None
Original Reporter kyleve (JIRA User)
Type Bug

Attachment: Download

Environment Xcode Version 13.0 (13A233)
Additional Detail from JIRA | | | |------------------|-----------------| |Votes | 0 | |Component/s | Compiler | |Labels | Bug | |Assignee | None | |Priority | Medium | md5: bd111aa43dbf15fbdc67605aa78729d0

Issue Description:

When a result builder, which takes in a concrete type, is used with implicit member syntax, compiler errors occur:

@resultBuilder
public struct Builder<Child> {
    public typealias Children = [Child]

    public static func buildBlock(_ children: Children...) -> Children {
        children.flatMap { $0 }
    }

    public static func buildOptional(_ children: Children?) -> Children {
        children ?? []
    }

    public static func buildEither(first: Children) -> Children {
        first
    }

    public static func buildEither(second: Children) -> Children {
        second
    }

    public static func buildExpression(_ child: Child) -> Children {
        [child]
    }

    /// Allow for an array of `Child` to be flattened into the overall result.
    public static func buildExpression(_ children: [Child]) -> Children {
        children
    }
}

struct Thingy {

    var name : String

    static func thingy(with name : String) -> Self {
        .init(name: name)
    }

    static func thingies(
    @Builder<Thingy> with things: () -> [Thingy]
    ) -> [Thingy]
    {
        things()
    }
}

/// One item works
func this_works_1() {

    _ = Thingy.thingies {
        .thingy(with: "Thing one")
    }
}

/// Fully qualified type works
func this_works_2() {

    _ = Thingy.thingies {
        Thingy.thingy(with: "Thing one")
    }
}

/// Fully qualified type works with more than one item
func this_works_3() {

    _ = Thingy.thingies {
        Thingy.thingy(with: "Thing one")
        Thingy.thingy(with: "Thing two")
    }
}

/// But two inferred types fails – seems to think the second `thingy` is a chained method call (thanks, Tim!)
func this_fails_1() {

    _ = Thingy.thingies {
        /// Static member 'thingy' cannot be used on instance of type 'Thingy'
        .thingy(with: "Thing one")

        .thingy(with: "Thing two")
    }
}

tdonnelly (JIRA User) calls out this is likely because the compiler is assuming chained member syntax.

It would be nice if there was a way to be smarter about this, or make the error message better somehow, but this also likely means making whitespace significant.

swift-ci commented 3 years ago

Comment by Kyle Van Essen (JIRA)

Also, a semicolon on the first line fixes this too:

func this_fails_1() {

    _ = Thingy.thingies {
        .thingy(with: "Thing one");        
        .thingy(with: "Thing two")
    }
}
swift-ci commented 3 years ago

Comment by Kyle Van Essen (JIRA)

(Also this is probably just like, generally unsolvable; so feel free to close it. Now that I get what's going on, I know why this happens, but it definitely took a bit to get why it was happening)