Expected Behavior of Async Overloads in Protocol Conformance Contexts?

Async method overloads in the context of protocol conformance behave differently than the Async/await proposal and subsequent amendment Allow overloads that differ only in async suggest they do.

This overload works as expected; there are no compiler errors and calling context inference resolves as outlined in the proposal.

struct Foo { 
  func doSomething() -> String { "hello" }
  func doSomething() async -> String { "world" }
}

However, things fall apart when a protocol is introduced.

protocol P { // ✅ this is fine
  func doSomething() -> String
  func doSomething() async -> String
}

struct Bar: P { // ❌ Type `Bar` does not conform to protocol `P`
  func doSomething() -> String { "hello" }  // ❌ candidate exactly matches
  func doSomething() async -> String { "world" } // ❌ candidate exactly matches
}

This seems to be due to the fact that sync methods can satisfy async method conformance requirements, as outlined in the Protocol Conformance section of the Async/await proposal.

I don't believe this is expected behavior based on the acceptance reasoning of Allow overloads that differ only in async. Specifically, creating an exception to the async overloading rule for protocol conformance violates this idea:

Without overloading on async , the Swift ecosystem will end up with a nontrivial number of APIs with non-ideal names (e.g., an Async suffix) that won't be able to be fixed later.

2 Likes

This may well be [SR-15150] Async overloading in protocol implementation fails · Issue #57476 · apple/swift · GitHub.

Yes, that's the same issue. Thanks for the link!
I really hope this gets addressed; the current state prevents transitioning to using async / await in certain circumstances.

Yes. The issue was turned into an Apple "Radar" bug, so it is tracked somewhere. This does not mean it will be fixed. You can vote for the issue on bugs.swfit.com, and report a duplicate on http://feedbackassistant.apple.com. SE-0296 authors did not answer, so :crossed_fingers:

1 Like

Interestingly, the compiler's fixit recommendations auto-generate both the sync and async methods.

struct Bar: P { }
// click `fix` or ctrl + alt + cmd + f 
// produces
struct Bar: P {
  func doSomething() -> String { }
    
  func doSomething() async -> String { }
} 

This was recently fixed:

Prefer witnesses with no difference effects to ones that have fewer effects by DougGregor · Pull Request #40088 · apple/swift · GitHub

Please don't. It makes busywork for the people who would rather fix issues.

Doug

2 Likes

Thank you for the the fix and for the update here!