Question about SWIFT_UPCOMING_FEATURE_INFER_SENDABLE_FROM_CAPTURES

Hello! I'm trying to update my project to Swift 6 and have a question about the SWIFT_UPCOMING_FEATURE_INFER_SENDABLE_FROM_CAPTURES flag. Here's my code:

I have an enum in Lib A

@frozen
public enum ImageName {
	case agent20Color
	case airplaneRegular
	case alertUrgent20Filled
	case alertUrgent20Regular
}

and some code like this in Lib B

func testFunction() -> ImageName {
	values.map { _ in
		.agent20Color // error: Cannot infer contextual base in reference to member 'agent20Color'
	}.first!
}

This code breaks immediately after enabling SWIFT_UPCOMING_FEATURE_INFER_SENDABLE_FROM_CAPTURES, even when I explicitly add Sendable to ImageName. The only workaround I've found so far is prefixing .agent20Color with ImageName.

Our project contains thousands of similar usages, so it's difficult for me to modify them one by one.. Does anyone know why auto-inference stopped working and if there's a simpler solution?

Here is a smallest Demo GitHub - miku1958/Sendable_Caputure_Bug: A bug

Thanks in advance.

anyone?

I’m not sure exactly what you are trying to do but for your example I would do one of the following:

values.first(where: {$0 == .agent20Color})
values.map{$0 == .agent20Color}.first!

Ideally the first one

It just a min demo for reproduction, what I mean is, the inference system broke after enabling this flag.

In that case someone else might be able to give more insight however if you need to end up changing it I would do a find and replace on _ in .case with _ in Name.case

This is exactly what I want to avoid—it completely ruins Swift's syntax and makes it look like TypeScript.

i haven't looked too deeply into exactly why the inference behavior seems to change and fail to typecheck in this case, but reproducing this doesn't appear to require separate targets, or frozen enums. the following example works in swift 5 without the 'infer sendable from captures' feature enabled, and fails when it's turned on:

enum ImageName {
    case one
    case two
}

func test(_ arr: [Int]) -> ImageName {
    arr.map { _ in .one }.first!
}
error: cannot infer contextual base in reference to member 'one'
 5 | 
 6 | func test(_ arr: [Int]) -> ImageName {
 7 |     arr.map { _ in .one }.first!
   |                     `- error: cannot infer contextual base in reference to member 'one'
 8 | }
 9 | 

the following workarounds might be of interest, though none of them seem ideal since they require source modifications:

  1. use a subscript instead of the first method:
func subscriptWorkaround(_ arr: [Int]) -> ImageName {
    arr.map { _ in return .one }[0] // ✅
}
  1. provide a custom first implementation to resolve potential ambiguity
extension Array where Element == ImageName {
    var firstImageName: ImageName? { self.first }
}

func customFirstWorkaround(_ arr: [Int]) -> ImageName {
    arr.map { _ in return .one }.firstImageName! // ✅
}
  1. provide a custom map implementation to resolve potential ambiguity
extension Array {
    func mapToImageName(_ transform: (Element) -> ImageName) -> [ImageName] {
        self.map(transform)
    }
}

func customMapWorkaround(_ arr: [Int]) -> ImageName {
    arr.mapToImageName { _ in return .one }.first! // ✅
}

here's some of these examples in godbolt, which is also set up to spew out the typechecker's constraint solver debug output, so maybe someone will be able to tell exactly why this is breaking, and if there are better options available.

the feature proposal does note that there is potential for source breakage (hence its being gated behind a new language mode). however, i'm not sure if this particular failure mode is expected, so you may wish to file a GitHub issue.

2 Likes