Skip to content

Commit

Permalink
[6.1][stdlib] Fix recoverable [U]Int128 division-by-zero (#78045)
Browse files Browse the repository at this point in the history
* Fix recoverable [U]Int128 division-by-zero

This patch fixes the division-by-zero case in the following methods:

- `Int128/dividedReportingOverflow(by:)`
- `Int128/remainderReportingOverflow(dividingBy:)`
- `UInt128/dividedReportingOverflow(by:)`
- `UInt128/remainderReportingOverflow(dividingBy:)`

* Add preconditions to trapping [U]Int128 division methods.

* Make consistent use of `_slowPath(_:)`.

Jumping on the `_slowPath(_:)` bandwagon like all other integer types.

* Copy existing UInt128 division comments to division operators.

* Add a comment about signed remainder overflow semantics as requested.

I have paraphrased @stephentyrone's and @xwu's review comments to the best of my ability.
  • Loading branch information
oscbyspro authored Dec 8, 2024
1 parent 7c59fa6 commit 332b24a
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 10 deletions.
38 changes: 32 additions & 6 deletions stdlib/public/core/Int128.swift
Original file line number Diff line number Diff line change
Expand Up @@ -297,8 +297,12 @@ extension Int128 {
public func dividedReportingOverflow(
by other: Self
) -> (partialValue: Self, overflow: Bool) {
_precondition(other != .zero, "Division by zero")
if self == .min && other == -1 { return (.min, true) }
if _slowPath(other == .zero) {
return (self, true)
}
if _slowPath(self == .min && other == (-1 as Self)) {
return (.min, true)
}
return (Self(Builtin.sdiv_Int128(self._value, other._value)), false)
}

Expand All @@ -307,8 +311,15 @@ extension Int128 {
public func remainderReportingOverflow(
dividingBy other: Self
) -> (partialValue: Self, overflow: Bool) {
_precondition(other != .zero, "Division by zero in remainder operation")
if self == .min && other == -1 { return (0, true) }
if _slowPath(other == .zero) {
return (self, true)
}
// This case is interesting because the remainder does not overflow; the
// analogous division does. Counting it as overflowing is consistent with
// documented behavior.
if _slowPath(self == .min && other == (-1 as Self)) {
return (0, true)
}
return (Self(Builtin.srem_Int128(self._value, other._value)), false)
}
}
Expand Down Expand Up @@ -366,7 +377,13 @@ extension Int128 {
@available(SwiftStdlib 6.0, *)
@_transparent
public static func /(a: Self, b: Self) -> Self {
a.dividedReportingOverflow(by: b).partialValue
if _slowPath(b == .zero) {
_preconditionFailure("Division by zero")
}
if _slowPath(a == .min && b == (-1 as Self)) {
_preconditionFailure("Division results in an overflow")
}
return Self(Builtin.sdiv_Int128(a._value, b._value))
}

@available(SwiftStdlib 6.0, *)
Expand All @@ -378,7 +395,16 @@ extension Int128 {
@available(SwiftStdlib 6.0, *)
@_transparent
public static func %(a: Self, b: Self) -> Self {
a.remainderReportingOverflow(dividingBy: b).partialValue
if _slowPath(b == .zero) {
_preconditionFailure("Division by zero in remainder operation")
}
// This case is interesting because the remainder does not overflow; the
// analogous division does. Counting it as overflowing is consistent with
// documented behavior.
if _slowPath(a == .min && b == (-1 as Self)) {
_preconditionFailure("Division results in an overflow in remainder operation")
}
return Self(Builtin.srem_Int128(a._value, b._value))
}

@available(SwiftStdlib 6.0, *)
Expand Down
20 changes: 16 additions & 4 deletions stdlib/public/core/UInt128.swift
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,9 @@ extension UInt128 {
public func dividedReportingOverflow(
by other: Self
) -> (partialValue: Self, overflow: Bool) {
_precondition(other != .zero, "Division by zero")
if _slowPath(other == .zero) {
return (self, true)
}
// Unsigned divide never overflows.
return (Self(Builtin.udiv_Int128(self._value, other._value)), false)
}
Expand All @@ -290,7 +292,9 @@ extension UInt128 {
public func remainderReportingOverflow(
dividingBy other: Self
) -> (partialValue: Self, overflow: Bool) {
_precondition(other != .zero, "Division by zero in remainder operation")
if _slowPath(other == .zero) {
return (self, true)
}
// Unsigned divide never overflows.
return (Self(Builtin.urem_Int128(self._value, other._value)), false)
}
Expand Down Expand Up @@ -349,7 +353,11 @@ extension UInt128 {
@available(SwiftStdlib 6.0, *)
@_transparent
public static func /(a: Self, b: Self) -> Self {
a.dividedReportingOverflow(by: b).partialValue
if _slowPath(b == .zero) {
_preconditionFailure("Division by zero")
}
// Unsigned divide never overflows.
return Self(Builtin.udiv_Int128(a._value, b._value))
}

@available(SwiftStdlib 6.0, *)
Expand All @@ -361,7 +369,11 @@ extension UInt128 {
@available(SwiftStdlib 6.0, *)
@_transparent
public static func %(a: Self, b: Self) -> Self {
a.remainderReportingOverflow(dividingBy: b).partialValue
if _slowPath(b == .zero) {
_preconditionFailure("Division by zero in remainder operation")
}
// Unsigned divide never overflows.
return Self(Builtin.urem_Int128(a._value, b._value))
}

@available(SwiftStdlib 6.0, *)
Expand Down

0 comments on commit 332b24a

Please sign in to comment.