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-3873] Overloading of concrete type extension methods does not behave like protocol extension methods. #46458

Open
swift-ci opened this issue Feb 6, 2017 · 6 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself type checker Area → compiler: Semantic analysis

Comments

@swift-ci
Copy link
Collaborator

swift-ci commented Feb 6, 2017

Previous ID SR-3873
Radar None
Original Reporter andersha (JIRA User)
Type Bug
Environment

Xcode 8.3 beta (8W109m)
macOS 10.12.3

Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug, TypeChecker
Assignee None
Priority Medium

md5: 57e0fc1a7bcacf22e75ab63b159d301f

Issue Description:

Concrete type extension methods do not exhibit the same overloading order as protocol extension methods.

Given these types:

enum NoError: Swift.Error {}
struct A<T1, T2: Swift.Error> {}

protocol P {
    associatedtype T1
    associatedtype T2: Swift.Error
    func test(_: T1.Type, _: T2.Type)
}

extension A: P {
    func test(_: T1.Type, _: T2.Type) {}
}

We currently have three protocol extension based method overloads:

// Protocol extension.
// Works normally in Swift 2.x, 3.0.x and 3.1 GM.
extension P {
    func foo<U>(_ arg: A<U, T2>) -> A<U, T2> { return A()}
    func foo<U>(_ arg: A<U, NoError>) -> A<U, T2> { return A() }
}

extension P where T2 == NoError {
    func foo<U, NewError: Swift.Error>(_ arg: A<U, NewError>) -> A<U, NewError> { return A() }
}

A<Int, NoError>().foo(A<Int, NoError>())

With the introduction of concrete same-type constraint, ideally we would be able to do:

// Concrete type extension.
extension A {
    func bar<U>(_ arg: A<U, T2>) -> A<U, T2> { return A<U, T2>() }
    func bar<U>(_ arg: A<U, NoError>) -> A<U, T2> { return A<U, T2>() }
}

extension A where T2 == NoError {
    func bar<U, NewError: Swift.Error>(_ arg: A<U, NewError>) -> A<U, NewError> { return A<U, NewError>() }
}

A<Int, NoError>().bar(A<Int, NoError>())

But the Swift 3.1 compiler complains that:

Playground execution failed: error: MyPlayground.playground:35:1: error: ambiguous use of 'bar'
A<Int, NoError>().bar(A<Int, NoError>())
^

MyPlayground.playground:23:7: note: found this candidate
        func bar<U>(_ arg: A<U, T2>) -> A<U, T2> { return A<U, T2>() }
             ^

MyPlayground.playground:27:7: note: found this candidate
        func bar<U>(_ arg: A<U, NoError>) -> A<U, T2> { return A<U, T2>() }
             ^

MyPlayground.playground:31:7: note: found this candidate
        func bar<U, NewError: Swift.Error>(_ arg: A<U, NewError>) -> A<U, NewError> { return A<U, NewError>() }
             ^

I was able to workaround it in Swift 3.1 GM by keeping the second and the third overload in the protocol `P`:

extension A {
    func bar<U>(_ arg: A<U, T2>) -> A<U, T2> { return A<U, T2>() }
}

extension P {
    func bar<U>(_ arg: A<U, NoError>) -> A<U, T2> { return A<U, T2>() }
}

extension P where T2 == NoError {
    func bar<U, NewError: Swift.Error>(_ arg: A<U, NewError>) -> A<U, NewError> { return A<U, NewError>() }
}

// As reported by SourceKit:
A<Int, NoError>().bar(A<Int, NoError>()) // Use the 1st overload.
A<Int, NoError>().bar(A<Int, CocoaError>()) // Use the 3rd overload.
A<Int, CocoaError>().bar(A<Int, NoError>()) // Use the 2nd overload.
A<Int, CocoaError>().bar(A<Int, CocoaError>()) // Use the 1st overload.

As a side note, if I replace the `U` parameter on the methods with `T1` in the protocol extension based version, the compiler would start complaining about ambiguous use of `foo` similarly. IOW, the overloading rules seem to be inconsistent among the presence and absence of generic parameters too.

extension P {
    func foo(_ arg: A<T1, T2>) -> A<T1, T2> { return A()}
    func foo(_ arg: A<T1, NoError>) -> A<T1, T2> { return A() }
}

extension P where T2 == NoError {
    func foo<NewError: Swift.Error>(_ arg: A<T1, NewError>) -> A<T1, NewError> { return A() }
}

A<Int, NoError>().foo(A<Int, NoError>())
Playground execution failed: error: MyPlayground.playground:17:1: error: ambiguous use of 'foo'
A<Int, NoError>().foo(A<Int, NoError>())
^

MyPlayground.playground:9:7: note: found this candidate
        func foo(_ arg: A<T1, T2>) -> A<T1, T2> { return A()}
             ^

MyPlayground.playground:10:7: note: found this candidate
        func foo(_ arg: A<T1, NoError>) -> A<T1, T2> { return A() }
             ^
@belkadan
Copy link
Contributor

belkadan commented Feb 7, 2017

I actually see this error in Swift 3.0.2 as well (if I comment out the same-type constrained extension). Are you sure this used to work?

@swift-ci
Copy link
Collaborator Author

swift-ci commented Feb 7, 2017

Comment by Anders Ha (JIRA)

If you meant the protocol extensions, it is functional (and is still functional in 3.1 beta), and we shipped it as part of ReactiveSwift 1.0. We are attempting to move these extensions from the the single implementation protocols to the concrete types, and this issue prevails.

@swift-ci
Copy link
Collaborator Author

swift-ci commented Feb 7, 2017

Comment by Anders Ha (JIRA)

Just in case, this is not a source compatibility issue with 3.0, since we do not have same type constraints for concrete type extensions in 3.0.

@belkadan
Copy link
Contributor

belkadan commented Feb 7, 2017

Ah, I understand. @DougGregor or @rudkx, any idea why the overload ordering would be different here?

@swift-ci
Copy link
Collaborator Author

swift-ci commented Feb 7, 2017

Comment by Anders Ha (JIRA)

I have updated a second test case that seems related.

@swift-ci
Copy link
Collaborator Author

Comment by Anders Ha (JIRA)

The problem persists in 3.1 GM. I have compacted the issue and the code snippets with the (kinda) workaround I am using.

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
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. compiler The Swift compiler in itself type checker Area → compiler: Semantic analysis
Projects
None yet
Development

No branches or pull requests

2 participants