swiftlang / swift

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

[SR-14665] Synthesised Equatable may be incorrect for manually implemented Comparable #57016

Open WowbaggersLiquidLunch opened 3 years ago

WowbaggersLiquidLunch commented 3 years ago
Previous ID SR-14665
Radar None
Original Reporter @WowbaggersLiquidLunch
Type Bug
Additional Detail from JIRA | | | |------------------|-----------------| |Votes | 0 | |Component/s | Compiler | |Labels | Bug, DerivedConformance | |Assignee | None | |Priority | Medium | md5: 7c4663ac0f4ead1b689e43c0ea8393a3

relates to:

Issue Description:

Currently Comparable inherits from Equatable, but does not provide a default implementation for ==, so the compiler synthesizes one composed of member-wise ==s. This leads to a problem where if a type's < is not composed of member-wise inequalities, then <, >, and == can all evaluate to false for some pairs of values, contradicting Comparable's documentation:

Types with Comparable conformance implement the less-than operator (<) and the equal-to operator (==). These two operations impose a strict total order on the values of a type, in which exactly one of the following must be true for any two values a and b:

  • a == b

  • a < b

  • b < a

For example:

struct Length: Comparable {
    enum Unit: Double, Comparable {
        case mm = 0.001
        case m = 1
        case banana = 0.178
    }

    let magnitude: Double
    let unit: Unit

    static func < (lhs: Self, rhs: Self) -> Bool {
        lhs.magnitude * lhs.unit.rawValue < rhs.magnitude * rhs.unit.rawValue
    }
}

let aBanana = Length(magnitude: 1, unit: .banana)
let oneBanana = Length(magnitude: 0.178, unit: .m)

print(aBanana < oneBanana)  // prints "false", because Length's < says so.
print(aBanana > oneBanana)  // prints "false", because Comparable's default implementation of >(a,b) is <(b,a).
print(aBanana == oneBanana) // prints "false", because the 2 Length instances are not member-wise equal.

Forums discussion: https://forums.swift.org/t/add-default-implementation-of-to-comparable/48832

WowbaggersLiquidLunch commented 3 years ago

submitted a draft PR #39047 https://github.com/apple/swift/pull/39047

WowbaggersLiquidLunch commented 2 years ago

I haven't had time in the past few months to continue working on this, and don't think I will be anytime soon, so I unassigned myself. If anyone wants to take over, please feel free to do so.