Skip to content

Commit 0f99458

Browse files
Allow Comparable: ~Copyable (#85747)
Builds on #85746 which covers Equatable
1 parent a7f9966 commit 0f99458

File tree

7 files changed

+148
-24
lines changed

7 files changed

+148
-24
lines changed

stdlib/public/core/Comparable.swift

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@
135135
/// (`FloatingPoint.nan`) compares as neither less than, greater than, nor
136136
/// equal to any normal floating-point value. Exceptional values need not
137137
/// take part in the strict total order.
138-
public protocol Comparable: Equatable {
138+
public protocol Comparable: Equatable, ~Copyable {
139139
/// Returns a Boolean value indicating whether the value of the first
140140
/// argument is less than that of the second argument.
141141
///
@@ -146,34 +146,34 @@ public protocol Comparable: Equatable {
146146
/// - Parameters:
147147
/// - lhs: A value to compare.
148148
/// - rhs: Another value to compare.
149-
static func < (lhs: Self, rhs: Self) -> Bool
149+
static func < (lhs: borrowing Self, rhs: borrowing Self) -> Bool
150150

151151
/// Returns a Boolean value indicating whether the value of the first
152152
/// argument is less than or equal to that of the second argument.
153153
///
154154
/// - Parameters:
155155
/// - lhs: A value to compare.
156156
/// - rhs: Another value to compare.
157-
static func <= (lhs: Self, rhs: Self) -> Bool
157+
static func <= (lhs: borrowing Self, rhs: borrowing Self) -> Bool
158158

159159
/// Returns a Boolean value indicating whether the value of the first
160160
/// argument is greater than or equal to that of the second argument.
161161
///
162162
/// - Parameters:
163163
/// - lhs: A value to compare.
164164
/// - rhs: Another value to compare.
165-
static func >= (lhs: Self, rhs: Self) -> Bool
165+
static func >= (lhs: borrowing Self, rhs: borrowing Self) -> Bool
166166

167167
/// Returns a Boolean value indicating whether the value of the first
168168
/// argument is greater than that of the second argument.
169169
///
170170
/// - Parameters:
171171
/// - lhs: A value to compare.
172172
/// - rhs: Another value to compare.
173-
static func > (lhs: Self, rhs: Self) -> Bool
173+
static func > (lhs: borrowing Self, rhs: borrowing Self) -> Bool
174174
}
175175

176-
extension Comparable {
176+
extension Comparable where Self: ~Copyable {
177177
/// Returns a Boolean value indicating whether the value of the first argument
178178
/// is greater than that of the second argument.
179179
///
@@ -184,7 +184,8 @@ extension Comparable {
184184
/// - lhs: A value to compare.
185185
/// - rhs: Another value to compare.
186186
@inlinable
187-
public static func > (lhs: Self, rhs: Self) -> Bool {
187+
@_preInverseGenerics
188+
public static func > (lhs: borrowing Self, rhs: borrowing Self) -> Bool {
188189
return rhs < lhs
189190
}
190191

@@ -198,7 +199,8 @@ extension Comparable {
198199
/// - lhs: A value to compare.
199200
/// - rhs: Another value to compare.
200201
@inlinable
201-
public static func <= (lhs: Self, rhs: Self) -> Bool {
202+
@_preInverseGenerics
203+
public static func <= (lhs: borrowing Self, rhs: borrowing Self) -> Bool {
202204
return !(rhs < lhs)
203205
}
204206

@@ -214,7 +216,8 @@ extension Comparable {
214216
/// - Returns: `true` if `lhs` is greater than or equal to `rhs`; otherwise,
215217
/// `false`.
216218
@inlinable
217-
public static func >= (lhs: Self, rhs: Self) -> Bool {
219+
@_preInverseGenerics
220+
public static func >= (lhs: borrowing Self, rhs: borrowing Self) -> Bool {
218221
return !(lhs < rhs)
219222
}
220223
}

test/Constraints/diagnostics.swift

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1472,14 +1472,12 @@ func testUnwrapFixIts(x: Int?) throws {
14721472
// expected-note@-2 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{11-11=!}}
14731473
foo(y: x ?? 0)
14741474

1475-
let _ = x < 2 // expected-error {{value of optional type 'Int?' must be unwrapped to a value of type 'Int'}}
1476-
// expected-note@-1 {{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{12-12= ?? <#default value#>}}
1477-
// expected-note@-2 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{12-12=!}}
1475+
let _ = x < 2 // expected-error {{binary operator '<' cannot be applied to operands of type 'Int?' and 'Int'}}
1476+
// expected-note@-1 {{overloads for '<' exist with these partially matching parameter lists: (Int, Int)}}
14781477
let _ = x ?? 0 < 2
14791478

1480-
let _ = 2 < x // expected-error {{value of optional type 'Int?' must be unwrapped to a value of type 'Int'}}
1481-
// expected-note@-1 {{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{16-16= ?? <#default value#>}}
1482-
// expected-note@-2 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{16-16=!}}
1479+
let _ = 2 < x // expected-error {{binary operator '<' cannot be applied to operands of type 'Int' and 'Int?'}}
1480+
// expected-note@-1 {{overloads for '<' exist with these partially matching parameter lists: (Int, Int)}}
14831481
let _ = 2 < x ?? 0
14841482

14851483
let _: Int = (.optionalIntMember) // expected-error {{value of optional type 'Int?' must be unwrapped to a value of type 'Int'}}
@@ -1497,9 +1495,8 @@ func testUnwrapFixIts(x: Int?) throws {
14971495
// expected-note@-2 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{36-36=!}}
14981496
let _ = try (.optionalThrowsMember ?? 0) + 1
14991497

1500-
let _ = .optionalIntMember?.bitWidth > 0 // expected-error {{value of optional type 'Int?' must be unwrapped to a value of type 'Int'}}
1501-
// expected-note@-1 {{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{39-39= ?? <#default value#>}}
1502-
// expected-note@-2 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{11-11=(}} {{39-39=)!}}
1498+
let _ = .optionalIntMember?.bitWidth > 0 // expected-error {{binary operator '>' cannot be applied to operands of type 'Int?' and 'Int'}}
1499+
// expected-note@-1 {{overloads for '>' exist with these partially matching parameter lists: (Int, Int)}}
15031500
let _ = (.optionalIntMember?.bitWidth)! > 0
15041501
let _ = .optionalIntMember?.bitWidth ?? 0 > 0
15051502

test/SILOptimizer/mandatory_performance_optimizations.sil

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ bb0:
8484
sil [no_allocation] [ossa] @deserialize_and_inline_after_devirtualize : $@convention(thin) (@in Int) -> () {
8585
bb0(%0 : $*Int):
8686
%1 = metatype $@thick Int.Type
87-
%2 = witness_method $Int, #Comparable."<" : <Self where Self : Comparable> (Self.Type) -> (Self, Self) -> Bool : $@convention(witness_method: Comparable) <τ_0_0 where τ_0_0 : Comparable> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, @thick τ_0_0.Type) -> Bool
87+
%2 = witness_method $Int, #Comparable."<" : <Self where Self : Comparable, Self : ~Copyable> (Self.Type) -> (borrowing Self, borrowing Self) -> Bool : $@convention(witness_method: Comparable) <τ_0_0 where τ_0_0 : Comparable> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, @thick τ_0_0.Type) -> Bool
8888
%3 = apply %2<Int>(%0, %0, %1) : $@convention(witness_method: Comparable) <τ_0_0 where τ_0_0 : Comparable> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, @thick τ_0_0.Type) -> Bool
8989
%4 = tuple()
9090
return %4 : $()
@@ -124,7 +124,7 @@ bb0(%0 : $*Int, %1 : $*Int, %2 : $@thick Int.Type):
124124

125125
sil_witness_table public_external [serialized] Int: Comparable module Swift {
126126
base_protocol Equatable: Int: Equatable module Swift
127-
method #Comparable."<": <Self where Self : Comparable> (Self.Type) -> (Self, Self) -> Bool : @$sSiSLsSL1loiySbx_xtFZTW
127+
method #Comparable."<": <Self where Self : Comparable, Self : ~Copyable> (Self.Type) -> (borrowing Self, borrowing Self) -> Bool : @$sSiSLsSL1loiySbx_xtFZTW
128128
}
129129

130130
sil [ossa] @get_int_value : $@convention(thin) () -> Int32 {

test/api-digester/Outputs/stability-stdlib-source-base.swift.expected

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ Protocol CodingKey has added inherited protocol Copyable
7171
Protocol CodingKey has added inherited protocol Escapable
7272
Protocol Collection has added inherited protocol Copyable
7373
Protocol Collection has added inherited protocol Escapable
74-
Protocol Comparable has added inherited protocol Copyable
7574
Protocol Comparable has added inherited protocol Escapable
7675
Protocol CustomDebugStringConvertible has added inherited protocol Copyable
7776
Protocol CustomDebugStringConvertible has added inherited protocol Escapable
@@ -387,3 +386,18 @@ Func Equatable.==(_:_:) has generic signature change from <Self where Self : Swi
387386
Func Equatable.==(_:_:) has parameter 0 changing from Default to Shared
388387
Func Equatable.==(_:_:) has parameter 1 changing from Default to Shared
389388

389+
// Comparable: ~Copyable
390+
Protocol Comparable has generic signature change from <Self : Swift.Equatable> to <Self : Swift.Equatable, Self : ~Copyable>
391+
Func Comparable.<(_:_:) has generic signature change from <Self where Self : Swift.Comparable> to <Self where Self : Swift.Comparable, Self : ~Copyable>
392+
Func Comparable.<(_:_:) has parameter 0 changing from Default to Shared
393+
Func Comparable.<(_:_:) has parameter 1 changing from Default to Shared
394+
Func Comparable.<=(_:_:) has generic signature change from <Self where Self : Swift.Comparable> to <Self where Self : Swift.Comparable, Self : ~Copyable>
395+
Func Comparable.<=(_:_:) has parameter 0 changing from Default to Shared
396+
Func Comparable.<=(_:_:) has parameter 1 changing from Default to Shared
397+
Func Comparable.>(_:_:) has generic signature change from <Self where Self : Swift.Comparable> to <Self where Self : Swift.Comparable, Self : ~Copyable>
398+
Func Comparable.>(_:_:) has parameter 0 changing from Default to Shared
399+
Func Comparable.>(_:_:) has parameter 1 changing from Default to Shared
400+
Func Comparable.>=(_:_:) has generic signature change from <Self where Self : Swift.Comparable> to <Self where Self : Swift.Comparable, Self : ~Copyable>
401+
Func Comparable.>=(_:_:) has parameter 0 changing from Default to Shared
402+
Func Comparable.>=(_:_:) has parameter 1 changing from Default to Shared
403+

test/api-digester/stability-stdlib-abi-without-asserts.test

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,6 @@ Protocol CodingKey has added inherited protocol Copyable
189189
Protocol CodingKey has added inherited protocol Escapable
190190
Protocol Collection has added inherited protocol Copyable
191191
Protocol Collection has added inherited protocol Escapable
192-
Protocol Comparable has added inherited protocol Copyable
193192
Protocol Comparable has added inherited protocol Escapable
194193
Protocol CustomDebugStringConvertible has added inherited protocol Copyable
195194
Protocol CustomDebugStringConvertible has added inherited protocol Escapable
@@ -892,4 +891,26 @@ Func Equatable.==(_:_:) has generic signature change from <Self where Self : Swi
892891
Func Equatable.==(_:_:) has parameter 0 changing from Default to Shared
893892
Func Equatable.==(_:_:) has parameter 1 changing from Default to Shared
894893

894+
// Comparable: ~Copyable
895+
Protocol Comparable has generic signature change from <Self : Swift.Equatable> to <Self : Swift.Equatable, Self : ~Copyable>
896+
Func Comparable.<(_:_:) has generic signature change from <Self where Self : Swift.Comparable> to <Self where Self : Swift.Comparable, Self : ~Copyable>
897+
Func Comparable.<(_:_:) has parameter 0 changing from Default to Shared
898+
Func Comparable.<(_:_:) has parameter 1 changing from Default to Shared
899+
Func Comparable.<=(_:_:) has generic signature change from <Self where Self : Swift.Comparable> to <Self where Self : Swift.Comparable, Self : ~Copyable>
900+
Func Comparable.<=(_:_:) has mangled name changing from 'static (extension in Swift):Swift.Comparable.<= infix(A, A) -> Swift.Bool' to 'static (extension in Swift):Swift.Comparable< where A: ~Swift.Copyable>.<= infix(A, A) -> Swift.Bool'
901+
Func Comparable.<=(_:_:) has parameter 0 changing from Default to Shared
902+
Func Comparable.<=(_:_:) has parameter 1 changing from Default to Shared
903+
Func Comparable.<=(_:_:) is now with @_preInverseGenerics
904+
Func Comparable.>(_:_:) has generic signature change from <Self where Self : Swift.Comparable> to <Self where Self : Swift.Comparable, Self : ~Copyable>
905+
Func Comparable.>(_:_:) has mangled name changing from 'static (extension in Swift):Swift.Comparable.> infix(A, A) -> Swift.Bool' to 'static (extension in Swift):Swift.Comparable< where A: ~Swift.Copyable>.> infix(A, A) -> Swift.Bool'
906+
Func Comparable.>(_:_:) has parameter 0 changing from Default to Shared
907+
Func Comparable.>(_:_:) has parameter 1 changing from Default to Shared
908+
Func Comparable.>(_:_:) is now with @_preInverseGenerics
909+
Func Comparable.>=(_:_:) has generic signature change from <Self where Self : Swift.Comparable> to <Self where Self : Swift.Comparable, Self : ~Copyable>
910+
Func Comparable.>=(_:_:) has mangled name changing from 'static (extension in Swift):Swift.Comparable.>= infix(A, A) -> Swift.Bool' to 'static (extension in Swift):Swift.Comparable< where A: ~Swift.Copyable>.>= infix(A, A) -> Swift.Bool'
911+
Func Comparable.>=(_:_:) has parameter 0 changing from Default to Shared
912+
Func Comparable.>=(_:_:) has parameter 1 changing from Default to Shared
913+
Func Comparable.>=(_:_:) is now with @_preInverseGenerics
914+
915+
895916
// *** DO NOT DISABLE OR XFAIL THIS TEST. *** (See comment above.)
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
//===--- NoncopyableComparable.swift - tests for Comparable: ~Copyable -------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
// RUN: %target-run-simple-swift
13+
// REQUIRES: executable_test
14+
15+
import StdlibUnittest
16+
17+
let NoncopyableComparableTests = TestSuite("NoncopyableComparable")
18+
19+
struct Noncopyable<Wrapped: ~Copyable>: ~Copyable {
20+
var wrapped: Wrapped
21+
}
22+
23+
extension Noncopyable: Equatable where Wrapped: Equatable & ~Copyable { }
24+
25+
extension Noncopyable: Comparable where Wrapped: Comparable & ~Copyable {
26+
static func < (lhs: borrowing Self, rhs: borrowing Self) -> Bool { lhs.wrapped < rhs.wrapped }
27+
}
28+
29+
extension Comparable where Self: ~Copyable {
30+
func isLessThan(_ other: borrowing Self) -> Bool {
31+
self < other
32+
}
33+
}
34+
35+
func isLessOrEqual<T: Comparable & ~Copyable>(_ lhs: borrowing T, _ rhs: borrowing T) -> Bool {
36+
lhs <= rhs
37+
}
38+
39+
@available(SwiftStdlib 6.2, *)
40+
extension InlineArray where Element: Comparable & ~Copyable {
41+
func minIndex() -> Int? {
42+
indices.min { self[$0] < self[$1] }
43+
}
44+
45+
func inReverseOrder() -> Bool {
46+
for i in indices.dropFirst() {
47+
if self[i] > self[i-1] { return false }
48+
}
49+
return true
50+
}
51+
}
52+
53+
NoncopyableComparableTests.test("comparing noncopyables") {
54+
let a = Noncopyable(wrapped: 0)
55+
let b = Noncopyable(wrapped: 1)
56+
let c = Noncopyable(wrapped: 2)
57+
58+
expectTrue(a < b)
59+
expectTrue(c > a)
60+
expectFalse(a < a)
61+
expectFalse(a > c)
62+
expectFalse(c <= a)
63+
expectTrue(a <= a)
64+
expectTrue(a < b && b < c)
65+
66+
expectTrue(a.isLessThan(b))
67+
expectTrue(isLessOrEqual(a,b))
68+
expectFalse(b.isLessThan(a))
69+
70+
let nc2 = Noncopyable(wrapped: Noncopyable(wrapped: "1"))
71+
expectFalse(nc2 < nc2)
72+
expectFalse(nc2 > nc2)
73+
expectTrue(nc2 >= nc2)
74+
expectTrue(nc2 < .init(wrapped: .init(wrapped: "2")))
75+
76+
guard #available(SwiftStdlib 6.2, *) else { return }
77+
78+
var array: [3 of Noncopyable] = [c,b,a]
79+
expectTrue(array.minIndex() == 2)
80+
expectTrue(array.inReverseOrder())
81+
array.swapAt(1, 2)
82+
expectFalse(array.inReverseOrder())
83+
}
84+
85+
runAllTests()

test/stdlib/StringDiagnostics.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,18 @@ func testAmbiguousStringComparisons(s: String) {
3434
let nsString = s as NSString
3535
let a1 = s as NSString == nsString
3636
let a2 = s as NSString != nsString
37-
let a3 = s < nsString // expected-error{{'NSString' is not implicitly convertible to 'String'; did you mean to use 'as' to explicitly convert?}} {{24-24= as String}}
37+
let a3 = s < nsString
38+
// expected-error@-1{{binary operator '<' cannot be applied to operands of type 'String' and 'NSString'}}
39+
// expected-note@-2{{overloads for '<' exist with these partially matching parameter lists: (String, String)}}
3840
let a4 = s <= nsString // expected-error{{'NSString' is not implicitly convertible to 'String'; did you mean to use 'as' to explicitly convert?}} {{25-25= as String}}
3941
let a5 = s >= nsString // expected-error{{'NSString' is not implicitly convertible to 'String'; did you mean to use 'as' to explicitly convert?}} {{25-25= as String}}
4042
let a6 = s > nsString // expected-error{{'NSString' is not implicitly convertible to 'String'; did you mean to use 'as' to explicitly convert?}} {{24-24= as String}}
4143
// now the other way
4244
let a7 = nsString == s as NSString
4345
let a8 = nsString != s as NSString
44-
let a9 = nsString < s // expected-error{{'NSString' is not implicitly convertible to 'String'; did you mean to use 'as' to explicitly convert?}} {{20-20= as String}}
46+
let a9 = nsString < s
47+
// expected-error@-1{{binary operator '<' cannot be applied to operands of type 'NSString' and 'String'}}
48+
// expected-note@-2{{overloads for '<' exist with these partially matching parameter lists: (String, String)}}
4549
let a10 = nsString <= s // expected-error{{'NSString' is not implicitly convertible to 'String'; did you mean to use 'as' to explicitly convert?}} {{21-21= as String}}
4650
let a11 = nsString >= s // expected-error{{'NSString' is not implicitly convertible to 'String'; did you mean to use 'as' to explicitly convert?}} {{21-21= as String}}
4751
let a12 = nsString > s // expected-error{{'NSString' is not implicitly convertible to 'String'; did you mean to use 'as' to explicitly convert?}} {{21-21= as String}}

0 commit comments

Comments
 (0)