swiftlang / swift

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

[SR-12259] Function Builders with types conforming to ExpressibleBy... #54687

Closed swift-ci closed 4 years ago

swift-ci commented 4 years ago
Previous ID SR-12259
Radar rdar://problem/59740177
Original Reporter mkao (JIRA User)
Type Bug
Status Resolved
Resolution Invalid

Attachment: Download

Environment Xcode 11.3 Playground / Swift 5.1.3
Additional Detail from JIRA | | | |------------------|-----------------| |Votes | 0 | |Component/s | Compiler | |Labels | Bug, FunctionBuilder, TypeChecker | |Assignee | None | |Priority | Medium | md5: f3685517f91c2b32760fd3296922b4f3

Issue Description:

When using a type conforming to ExpressibleByStringLiteral within the FunctionBuilder closure, the compiler seems to have issues.

Given the following type:

public enum Foo: ExpressibleByStringLiteral {
    case string(String)
    case other

    public init(stringLiteral value: String) {
        self = .string(value)
    }
}

And using the function builder:

@_functionBuilder
public class SomeBuilder {
    public static func buildBlock(_ values: Foo...) -> [Foo] {
        values
    }
}

public func call(@SomeBuilder values: () -> [Foo]) -> [Foo] {
    values()
}

public func call(@SomeBuilder values: () -> Foo) -> [Foo] {
    [values()]
}

I am able to get this working:

call {
    "foo"
}

But when adding another String, the compiler throws an error:

call {
    "foo"
    "bar" // Cannot invoke 'call' with an argument list of type '(@escaping () -> ())'
}

I could reproduce the same behavior when using ExpressibleByIntegerLiteral.

typesanitizer commented 4 years ago

@swift-ci create

theblixguy commented 4 years ago

On master:

/Users/suyashsrijan/Desktop/test.swift:26:3: error: cannot convert value of type 'String' to expected argument type 'Foo'
  "foo"
  ^
/Users/suyashsrijan/Desktop/test.swift:27:3: error: cannot convert value of type 'String' to expected argument type 'Foo'
  "bar"
  ^
theblixguy commented 4 years ago

The error seems to go away when you insert a cast:

call { 
  "foo" as Foo
  "bar" as Foo
}
swift-ci commented 4 years ago

Comment by Michael Kao (JIRA)

Ah ok, haven't tried that. But isn't that something the compiler should be able to figure out?

DougGregor commented 4 years ago

This is intended behavior. Add

static func buildExpression(_ value: Foo) -> Foo { value }

to your function builder to affect how the expressions are type checked. See https://github.com/DougGregor/swift-evolution/blob/function-builders/proposals/XXXX-function-builders.md for more information.