Skip to content

Commit 332b24a

Browse files
authored
[6.1][stdlib] Fix recoverable [U]Int128 division-by-zero (#78045)
* 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.
1 parent 7c59fa6 commit 332b24a

File tree

2 files changed

+48
-10
lines changed

2 files changed

+48
-10
lines changed

stdlib/public/core/Int128.swift

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -297,8 +297,12 @@ extension Int128 {
297297
public func dividedReportingOverflow(
298298
by other: Self
299299
) -> (partialValue: Self, overflow: Bool) {
300-
_precondition(other != .zero, "Division by zero")
301-
if self == .min && other == -1 { return (.min, true) }
300+
if _slowPath(other == .zero) {
301+
return (self, true)
302+
}
303+
if _slowPath(self == .min && other == (-1 as Self)) {
304+
return (.min, true)
305+
}
302306
return (Self(Builtin.sdiv_Int128(self._value, other._value)), false)
303307
}
304308

@@ -307,8 +311,15 @@ extension Int128 {
307311
public func remainderReportingOverflow(
308312
dividingBy other: Self
309313
) -> (partialValue: Self, overflow: Bool) {
310-
_precondition(other != .zero, "Division by zero in remainder operation")
311-
if self == .min && other == -1 { return (0, true) }
314+
if _slowPath(other == .zero) {
315+
return (self, true)
316+
}
317+
// This case is interesting because the remainder does not overflow; the
318+
// analogous division does. Counting it as overflowing is consistent with
319+
// documented behavior.
320+
if _slowPath(self == .min && other == (-1 as Self)) {
321+
return (0, true)
322+
}
312323
return (Self(Builtin.srem_Int128(self._value, other._value)), false)
313324
}
314325
}
@@ -366,7 +377,13 @@ extension Int128 {
366377
@available(SwiftStdlib 6.0, *)
367378
@_transparent
368379
public static func /(a: Self, b: Self) -> Self {
369-
a.dividedReportingOverflow(by: b).partialValue
380+
if _slowPath(b == .zero) {
381+
_preconditionFailure("Division by zero")
382+
}
383+
if _slowPath(a == .min && b == (-1 as Self)) {
384+
_preconditionFailure("Division results in an overflow")
385+
}
386+
return Self(Builtin.sdiv_Int128(a._value, b._value))
370387
}
371388

372389
@available(SwiftStdlib 6.0, *)
@@ -378,7 +395,16 @@ extension Int128 {
378395
@available(SwiftStdlib 6.0, *)
379396
@_transparent
380397
public static func %(a: Self, b: Self) -> Self {
381-
a.remainderReportingOverflow(dividingBy: b).partialValue
398+
if _slowPath(b == .zero) {
399+
_preconditionFailure("Division by zero in remainder operation")
400+
}
401+
// This case is interesting because the remainder does not overflow; the
402+
// analogous division does. Counting it as overflowing is consistent with
403+
// documented behavior.
404+
if _slowPath(a == .min && b == (-1 as Self)) {
405+
_preconditionFailure("Division results in an overflow in remainder operation")
406+
}
407+
return Self(Builtin.srem_Int128(a._value, b._value))
382408
}
383409

384410
@available(SwiftStdlib 6.0, *)

stdlib/public/core/UInt128.swift

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,9 @@ extension UInt128 {
280280
public func dividedReportingOverflow(
281281
by other: Self
282282
) -> (partialValue: Self, overflow: Bool) {
283-
_precondition(other != .zero, "Division by zero")
283+
if _slowPath(other == .zero) {
284+
return (self, true)
285+
}
284286
// Unsigned divide never overflows.
285287
return (Self(Builtin.udiv_Int128(self._value, other._value)), false)
286288
}
@@ -290,7 +292,9 @@ extension UInt128 {
290292
public func remainderReportingOverflow(
291293
dividingBy other: Self
292294
) -> (partialValue: Self, overflow: Bool) {
293-
_precondition(other != .zero, "Division by zero in remainder operation")
295+
if _slowPath(other == .zero) {
296+
return (self, true)
297+
}
294298
// Unsigned divide never overflows.
295299
return (Self(Builtin.urem_Int128(self._value, other._value)), false)
296300
}
@@ -349,7 +353,11 @@ extension UInt128 {
349353
@available(SwiftStdlib 6.0, *)
350354
@_transparent
351355
public static func /(a: Self, b: Self) -> Self {
352-
a.dividedReportingOverflow(by: b).partialValue
356+
if _slowPath(b == .zero) {
357+
_preconditionFailure("Division by zero")
358+
}
359+
// Unsigned divide never overflows.
360+
return Self(Builtin.udiv_Int128(a._value, b._value))
353361
}
354362

355363
@available(SwiftStdlib 6.0, *)
@@ -361,7 +369,11 @@ extension UInt128 {
361369
@available(SwiftStdlib 6.0, *)
362370
@_transparent
363371
public static func %(a: Self, b: Self) -> Self {
364-
a.remainderReportingOverflow(dividingBy: b).partialValue
372+
if _slowPath(b == .zero) {
373+
_preconditionFailure("Division by zero in remainder operation")
374+
}
375+
// Unsigned divide never overflows.
376+
return Self(Builtin.urem_Int128(a._value, b._value))
365377
}
366378

367379
@available(SwiftStdlib 6.0, *)

0 commit comments

Comments
 (0)