Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-11792] Function Builder not working with single item in closure #54202

Closed
swift-ci opened this issue Nov 15, 2019 · 10 comments
Closed

[SR-11792] Function Builder not working with single item in closure #54202

swift-ci opened this issue Nov 15, 2019 · 10 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior.

Comments

@swift-ci
Copy link
Collaborator

Previous ID SR-11792
Radar rdar://problem/57246848
Original Reporter cbfullerx (JIRA User)
Type Bug
Status Resolved
Resolution Duplicate
Additional Detail from JIRA
Votes 0
Component/s
Labels Bug
Assignee None
Priority Medium

md5: e95e737358935b21a7b4720012df6a58

duplicates:

  • SR-11628 Function Builder not working when only one value?

Issue Description:

Provided:

public protocol Step {}

public typealias Script = Array<Step>

@_functionBuilder
public struct ScriptBuilder {

    public static func buildBlock(_ steps: Step...) -> Script {
        print("buildBlock called", steps)
        return steps
    }

    public static func buildIf(_ script: Script?) -> Script {
        script ?? []
    }

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

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

extension Script: Step {

    public init(@ScriptBuilder script: () -> Script) {
        self = script()
    }
}


struct ExampleStep: Step {}

let testMultiple = Script {
    ExampleStep()
    ExampleStep()
}

print("testMultiple", testMultiple)

let testSingle = Script {
    ExampleStep() // <- this is where the error occurs
}

print("testSingle", testSingle)

Expected:

buildBlock called [__lldb_expr_1.ExampleStep(), __lldb_expr_1.ExampleStep()]
testMultiple [__lldb_expr_1.ExampleStep(), __lldb_expr_1.ExampleStep()]
buildBlock called [__lldb_expr_1.ExampleStep()]
testSingle [__lldb_expr_1.ExampleStep()]

Actual:

error: Function Builder Issue.playground:96:5: error: cannot convert value of type 'ExampleStep' to closure result type 'Script' (aka 'Array<Step>')
    ExampleStep()
    ^~~~~~~~~~~~~

This can be solved by adding this initializer:

extension Script {
    public init(step: () -> Step) {
        print("single step initializer called")
        self = [step()]
    }
}

Then you would get this output:

buildBlock called [__lldb_expr_1.ExampleStep(), __lldb_expr_1.ExampleStep()]
testMultiple [__lldb_expr_1.ExampleStep(), __lldb_expr_1.ExampleStep()]
single step initializer called
testSingle [__lldb_expr_1.ExampleStep()]

But my expectation is that the convenience initializer is not needed and that the buildBlock method in the builder should be called in the single case.

Please advise. Thanks so much!

@swift-ci
Copy link
Collaborator Author

Comment by Christopher Fuller (JIRA)

One thought I had was that possibly we need `buildExpression` to make this work. So the builder could become:

@_functionBuilder
public struct ScriptBuilder {

    public static func buildExpression(_ step: Step) -> Script {
        [step]
    }

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

    public static func buildIf(_ script: Script?) -> Script {
        script ?? []
    }

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

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

But I cannot seem to get `buildExpression` to work … possibly it's not implemented … so am unable to test this theory.

@theblixguy
Copy link
Collaborator

Hmm, I thought adding a buildBlock that takes a single item and returns an array would work but apparently not. I think this is just something that isn't supported yet. In the meantime, you can do two things:

1. Define a new buildBlock that takes a single Step and returns Script and return [step] in the body. Then, in singleStep, invoke it directly by doing ScriptBuilder.buildBlock(ExampleStep()).
2. Simply return [ExampleStep()] in testSingle.

@theblixguy
Copy link
Collaborator

buildExpression has been implemented and is now supported on master: #27716

@beccadax
Copy link
Contributor

@swift-ci create

@swift-ci
Copy link
Collaborator Author

Comment by Christopher Fuller (JIRA)

@theblixguy Thanks for the quick reply! 👍

I tested both #1 and #2, and they both work. Out of those, #2 seems preferred since it is more concise. But overall I prefer the convenience initializer approach to either of those two options, as it allows the call-site to look as expected.

And thanks for the link and info on `buildExpression`.

@swift-ci
Copy link
Collaborator Author

Comment by Christopher Fuller (JIRA)

Does anyone know if `buildExpression` is required to make this work?

@theblixguy
Copy link
Collaborator

I tried it and it didn’t work. I think there’s some issues with single expression closures that are yet to be resolved,

@swift-ci
Copy link
Collaborator Author

Comment by Christopher Fuller (JIRA)

Ah, okay. And I can see that single expression functions do in fact work…

@ScriptBuilder
func makeScript() -> Script {
    ExampleStep()
}

output:

buildBlock called [__lldb_expr_1.ExampleStep()]
[__lldb_expr_1.ExampleStep()]

So the issue indeed appears to be limited to the single expression closures specifically.

@swift-ci
Copy link
Collaborator Author

Comment by Christopher Fuller (JIRA)

updated the ticket title to reflect closures specifically

@DougGregor
Copy link
Member

This is covered by SR-11628

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior.
Projects
None yet
Development

No branches or pull requests

4 participants