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-13356] == Method on Protocol Overrides Synthesized == Method On Enum #55796

Closed
SlaunchaMan opened this issue Aug 6, 2020 · 4 comments
Closed
Assignees
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior.

Comments

@SlaunchaMan
Copy link
Contributor

Previous ID SR-13356
Radar rdar://problem/67361230
Original Reporter @SlaunchaMan
Type Bug
Status Resolved
Resolution Won't Do
Environment

Xcode 11.6
macOS 10.15.6

Additional Detail from JIRA
Votes 0
Component/s
Labels Bug
Assignee @SlaunchaMan
Priority Medium

md5: 789acddb3405b4be56cd1dd9b9b62cef

Issue Description:

Given this code to switch between view edges using a trait collection:

import UIKit

public protocol ViewEdgeConvertible {

    func fixedViewEdge(
        using traitCollection: UITraitCollection?
    ) -> FixedViewEdge

    func directionalViewEdge(
        using traitCollection: UITraitCollection?
    ) -> DirectionalViewEdge

}

public func == (lhs: ViewEdgeConvertible, rhs: ViewEdgeConvertible) -> Bool {
    return lhs.fixedViewEdge(using: nil) == rhs.fixedViewEdge(using: nil)
}

public enum FixedViewEdge: Equatable {

    case top
    case left
    case right
    case bottom

}

extension FixedViewEdge: ViewEdgeConvertible {

    public func fixedViewEdge(
        using traitCollection: UITraitCollection?
    ) -> FixedViewEdge {
        self
    }

    public func directionalViewEdge(
        using traitCollection: UITraitCollection?
    ) -> DirectionalViewEdge {
        switch (self, traitCollection?.layoutDirection) {
        case (.top, _): return .top
        case (.left, .rightToLeft): return .trailing
        case (.left, _): return .leading
        case (.right, .rightToLeft): return .leading
        case (.right, _): return .trailing
        case (.bottom, _): return .bottom
        }
    }

}

public enum DirectionalViewEdge: Equatable {

    case top
    case leading
    case trailing
    case bottom

}

extension DirectionalViewEdge: ViewEdgeConvertible {

    public func fixedViewEdge(
        using traitCollection: UITraitCollection?
    ) -> FixedViewEdge {
        switch (self, traitCollection?.layoutDirection) {
        case (.top, _): return .top
        case (.leading, .rightToLeft): return .right
        case (.leading, _): return .left
        case (.trailing, .rightToLeft): return .left
        case (.trailing, _): return .right
        case (.bottom, _): return .bottom
        }
    }

    public func directionalViewEdge(
        using traitCollection: UITraitCollection?
    ) -> DirectionalViewEdge {
        self
    }

}

The == method produces this warning: “All paths through this function will call itself.” At runtime, this creates an infinite loop. I expected the == method to use the synthesized Equatable conformance of FixedViewEdge instead of the protocol method.

As a workaround, I can implement == directly on FixedViewEdge:

public func == (lhs: FixedViewEdge, rhs: FixedViewEdge) -> Bool {
    switch (lhs, rhs) {
    case (.top, .top), (.bottom, .bottom), (.left, .left), (.right, .right):
        return true
    default:
        return false
    }
}

This avoids the issue.

@SlaunchaMan
Copy link
Contributor Author

Here is a Swift Forums post where some workarounds are discussed: https://forums.swift.org/t/all-paths-through-this-function-will-call-itself-on-protocol-equality-method/39131/15

@typesanitizer
Copy link

@swift-ci create

@CodaFi
Copy link
Member

CodaFi commented Aug 24, 2020

This semantics seems intended, if unfortunate given the set of protocol conformances here. Equatable is not a more specific protocol than ViewEdgeConvertible from the perspective of the witness matching algorithm. Others have noted on the forums that this should be expressed as a defaulted requirement in a protocol extension for ViewEdgeConvertible instead.

@hborla
Copy link
Member

hborla commented Aug 25, 2020

Yeah, unfortunately this is correct behavior. The reason is because == on Equatable is generic over Self, whereas the == that you've written here is not, and the compiler will always prefer non-generic overloads over generic ones. It works when you implement == for FixedViewEdge because that new overload is more specific than the one that takes in existential types, so lhs.fixedViewEdge(using: nil) == rhs.fixedViewEdge(using: nil) will choose that overload.

@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