Skip to content

Commit 85d488d

Browse files
committed
[stdlib] Remove magnitude-based overload of abs(_:).
The standard library has two versions of the `abs(_:)` function: ``` func abs<T : SignedNumeric>(_ x: T) -> T where T.Magnitude == T func abs<T : SignedNumeric & Comparable>(_ x: T) -> T ``` The first is more specialized than the second because `T.Magnitude` is known to conform to `Comparable`. Indeed, it’s a more specialized implementation that returns `magnitude`. However, this overload behaves oddly: in the expression `abs(-8)`, the type checker will pick the first overload because it is more specialized. That’s a general guiding principle for overloading: pick the most specialized overload that works. However, to select that overload, it needs to pick a type for the literal “8” for which that overload works, and it chooses `Double`. The “obvious” answer, `Int`, doesn’t work because `Int.Magnitude == UInt`. There is a conflict between the two rules, here: we prefer more-specialized overloads (but we’ll fall back to less-specialized if those don’t work) and we prefer to use `Int` for integer literals (but we’ll fall back to `Double` if it doesn’t work). We have a few options from a type-checker perspective: 1. Consider the more-specialized-function rule to be more important 2. Consider the integer-literals-prefer-`Int` rule to be more important 3. Call the result ambiguous and make the user annotate it The type checker currently does #1, although at some point in the past it did #2. Moving forward, #1 is a better choice because it prunes the number of overloads that need to be considered: if the more-specialized overload succeeds its type-check, the others need not be considered. It’s also easier to reason about than the literal-scoring approach, because there can be a direct definition for “more specialized than” that can be reasoned about. I think we should dodge the issue by removing the more-specialized version of `abs(_:)`. Its use of `magnitude` seems unlikely to provide a significant performance benefit, and the presence of overloading either forces us to consider both overloads always (which is bad for type checker performance) or accept the regression that `abs(-8)` is `Double`. Better to eliminate the overloading and, if needed in the future, find a better way to introduce the more-specialized implementation without it being a separate signature. Fixes rdar://problem/42345366.
1 parent 58e855e commit 85d488d

File tree

5 files changed

+8
-23
lines changed

5 files changed

+8
-23
lines changed

stdlib/public/core/Integers.swift

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -318,17 +318,6 @@ extension SignedNumeric {
318318
}
319319
}
320320

321-
322-
/// Returns the absolute value of the given number.
323-
///
324-
/// - Parameter x: A signed number.
325-
/// - Returns: The absolute value of `x`.
326-
@inlinable
327-
public func abs<T : SignedNumeric>(_ x: T) -> T
328-
where T.Magnitude == T {
329-
return x.magnitude
330-
}
331-
332321
/// Returns the absolute value of the given number.
333322
///
334323
/// The absolute value of `x` must be representable in the same type. In

test/SourceKit/CodeComplete/complete_moduleimportdepth.swift

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,6 @@ func test() {
2323
// CHECK: key.modulename: "Swift"
2424
// CHECK-NEXT: },
2525

26-
// CHECK-LABEL: key.name: "abs(:)",
27-
// CHECK-NEXT: key.sourcetext: "abs(<#T##x: Comparable & SignedNumeric##Comparable & SignedNumeric#>)",
28-
// CHECK-NEXT: key.description: "abs(x: Comparable & SignedNumeric)",
29-
// CHECK-NEXT: key.typename: "Comparable & SignedNumeric",
30-
// CHECK-NEXT: key.doc.brief: "Returns the absolute value of the given number.",
31-
// CHECK-NEXT: key.context: source.codecompletion.context.othermodule,
32-
// CHECK-NEXT: key.moduleimportdepth: 1,
33-
// CHECK-NEXT: key.num_bytes_to_erase: 0,
34-
// CHECK-NOT: key.modulename
35-
// CHECK: key.modulename: "Swift"
36-
// CHECK-NEXT: },
37-
3826
// FooHelper.FooHelperExplicit == 1
3927
// CHECK-LABEL: key.name: "fooHelperExplicitFrameworkFunc1(:)",
4028
// CHECK-NEXT: key.sourcetext: "fooHelperExplicitFrameworkFunc1(<#T##a: Int32##Int32#>)",

test/api-digester/Outputs/stability-stdlib-abi.swift.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Func _int64ToStringImpl(_:_:_:_:_:) has been removed
1717
Func _uint64ToStringImpl(_:_:_:_:_:) has been removed
1818
Func _typeByMangledName(_:substitutions:) has been removed
1919
Func _withUninitializedString(_:) has been removed
20+
Func abs(_:) has been removed
2021

2122
Func Optional.unwrappedOrError() has been removed
2223
Struct _UnwrappingFailed has been removed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ Func UnsafeMutableRawPointer.copyBytes(from:count:) has been removed (deprecated
9797
Func UnsafeMutableRawPointer.deallocate(bytes:alignedTo:) has been removed (deprecated)
9898
Func UnsafeMutableRawPointer.initializeMemory(as:at:count:to:) has been removed (deprecated)
9999
Func UnsafeMutableRawPointer.initializeMemory(as:from:) has been removed (deprecated)
100+
Func abs(_:) has been removed
100101
Protocol Collection has generic signature change from <Self : Sequence, Self.Index : Comparable, Self.Index == Self.Indices.Element, Self.Indices : Collection, Self.Indices == Self.Indices.SubSequence, Self.SubSequence : Collection, Self.Indices.Element == Self.Indices.Index, Self.Indices.Index == Self.SubSequence.Index, Self.SubSequence.Index == Self.Indices.Indices.Element, Self.Indices.Indices.Element == Self.Indices.Indices.Index, Self.Indices.Indices.Index == Self.SubSequence.Indices.Element, Self.SubSequence.Indices.Element == Self.SubSequence.Indices.Index, Self.SubSequence.Indices.Index == Self.SubSequence.Indices.Indices.Element, Self.SubSequence.Indices.Indices.Element == Self.SubSequence.Indices.Indices.Index> to <Self : Sequence, Self.Element == Self.SubSequence.Element, Self.Index : Comparable, Self.Index == Self.Indices.Element, Self.Indices : Collection, Self.Indices == Self.Indices.SubSequence, Self.SubSequence : Collection, Self.SubSequence == Self.SubSequence.SubSequence, Self.Indices.Element == Self.Indices.Index, Self.Indices.Index == Self.SubSequence.Index, Self.SubSequence.Index == Self.Indices.Indices.Element, Self.Indices.Indices.Element == Self.Indices.Indices.Index, Self.Indices.Indices.Index == Self.SubSequence.Indices.Element, Self.SubSequence.Indices.Element == Self.SubSequence.Indices.Index, Self.SubSequence.Indices.Index == Self.SubSequence.Indices.Indices.Element, Self.SubSequence.Indices.Indices.Element == Self.SubSequence.Indices.Indices.Index>
101102
Protocol Sequence has generic signature change from <Self.Element == Self.Iterator.Element, Self.Iterator : IteratorProtocol, Self.SubSequence : Sequence, Self.SubSequence == Self.SubSequence.SubSequence, Self.Iterator.Element == Self.SubSequence.Element, Self.SubSequence.Element == Self.SubSequence.Iterator.Element> to <Self.Element == Self.Iterator.Element, Self.Iterator : IteratorProtocol>
102103
Protocol _SequenceWrapper has been removed

test/stdlib/IntegerCompatibility.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,9 @@ func sr5176(description: String = "unambiguous Int32.init(bitPattern:)") {
5555
func sr6634(x: UnsafeBufferPointer<UInt8>) -> Int {
5656
return x.lazy.filter { $0 > 127 || $0 == 0 }.count // should be unambiguous
5757
}
58+
59+
// abs of an integer literal
60+
func returnIntAbs() -> Int {
61+
let x = abs(-8)
62+
return x
63+
}

0 commit comments

Comments
 (0)