-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Fix recoverable [U]Int128 division-by-zero #77854
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a609335
43d98e6
13fac3a
09d91ca
cdd2006
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) | ||
} | ||
|
||
|
@@ -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)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This case is somewhat interesting because the remainder does not overflow; rather the analogous division does. It's entirely plausible to me that this was originally defined "wrong" on the other integer types, but we're probably stuck with the current behavior. In any case, I think it merits a comment here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I return the equivalent of (0, false) in my project. So, I considered asking about this specific interaction, but I figured Swift chose C++ semantics ± trapping on undefined behavior. Is there a particular comment you have in mind?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was the specified behavior—I was the one who aligned the actual behavior with the specification in #14219—but for this reason the wording of the corresponding precondition failure (at least at the time) was that "division results in overflow in remainder operation." The current corresponding documentation for this operation on the other integer types correctly documents the subtlety in this regard: "Returns the remainder after dividing this value by the given value, along with a Boolean value indicating whether overflow occurred during division." [Edit: Ah, so, refreshing my memory—my involvement in this was to make the |
||
return (0, true) | ||
} | ||
return (Self(Builtin.srem_Int128(self._value, other._value)), false) | ||
} | ||
} | ||
|
@@ -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, *) | ||
|
@@ -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, *) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comment seems ironic now, doesn't it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In a way, I suppose. I would say that the ironic part is the overflow return label, however, since it represents both proper overflow and another kind of failure (division by zero). Still, I can remove it if you find it unfitting. Generally, I don't mess with comments I did not write. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's fine to leave it, I think. |
||
return (Self(Builtin.udiv_Int128(self._value, other._value)), false) | ||
} | ||
|
@@ -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) | ||
} | ||
|
@@ -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, *) | ||
|
@@ -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, *) | ||
|
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was there an observed performance issue that these
_slowPath
annotations are intended to address, or were they added speculatively to match the implementations for other Integer types?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They are speculative but match the implementation of smaller binary integer types. I can remove them if you believe they are unnecessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More of a matter of my own curiosity. I'm ok with leaving them in, though they shouldn't matter much.