-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[stdlib][SR-4818] Add overflow assignment operators #15144
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
Conversation
This seems like it requires an evolution proposal. |
I wrote a pitch some days ago and my overall feeling from the comments was that their absence was merely a bug. But If you guys think it will be better if this takes the proposal route, I'll gladly arrange one |
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.
Glad you've gone ahead and looked into making the change. Some minor comments. I do suspect we'll probably need a Swift Evolution proposal, but it shouldn't be controversial.
@@ -2740,8 +2797,18 @@ ${unsafeOperationComment(x.operator)} | |||
${operatorComment('&' + x.operator, True)} | |||
@_inlineable // FIXME(sil-serialize-all) | |||
@_transparent | |||
public static func &${x.operator} (lhs: Self, rhs: Self) -> Self { | |||
return lhs.${x.name}ReportingOverflow(${callLabel}rhs).partialValue |
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.
These implementations can be left alone. No need to have the compiler do more work than is necessary to arrive at the same result.
@_transparent | ||
public static func &${x.operator}=(lhs: inout Self, rhs: Self) { | ||
let result = lhs.${x.name}ReportingOverflow(${callLabel}rhs).partialValue | ||
lhs = result |
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.
This can be a one-liner; no need for the temporary result.
utils/SwiftIntTypes.py
Outdated
@@ -122,7 +122,7 @@ def all_integer_or_real_binary_operator_names(): | |||
|
|||
|
|||
def all_integer_assignment_operator_names(): | |||
return ['%=', '<<=', '>>=', '&=', '^=', '|='] | |||
return ['%=', '<<=', '>>=', '&*=', '&=', '&+=', '&-=', '^=', '|='] |
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.
I don't think these changes are required here. For example, &<<=
isn't present.
@@ -588,6 +588,63 @@ def assignmentOperatorComment(operator, fixedWidth): | |||
/// - Parameters: | |||
/// - lhs: The value to divide. | |||
/// - rhs: The value to divide `lhs` by. `rhs` must not be zero. | |||
""", | |||
'&+': """\ | |||
/// Stores the sum of the two given values in the left-hand-size variable, discarding any overflow. |
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.
Please wrap to 80 columns and follow the style of the documentation above; that is, "Adds the two values and stores..."
'&+': """\ | ||
/// Stores the sum of the two given values in the left-hand-size variable, discarding any overflow. | ||
/// | ||
/// The masking addition operator (`&+`) silently discards any overflow that |
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.
Please modify this text so that it talks about the assignment operator you're documenting.
Very well then. I'll arrange a formal proposal soon. |
…their documentation
@rockbruno Per Ben Cohen this might not need a formal review. I would email the core team list to get a definite answer though. |
Was going to ask that you add tests for these alongside the tests for the non-assignment masking operators, but can't find those anywhere. Maybe |
@natecook1000 When I first implemented this I located operator tests in If you guys point me in the right direction, I'll gladly write proper tests for them! (By the way, I sent an e-mail to the core team asking about the review-needs for this feature.) |
Discussed this amongst the core team and the agreement is the omission of these was an oversight and there's no need to go through a review to add them. Thanks! |
@natecook1000, @rockbruno https://github.com/apple/swift/blob/master/test/stdlib/Integers.swift.gyb looks OK for the tests, does it not? |
@moiseev That looks like a good place! But since it's my first time doing this, before I pushed anything I wanted to ask: What should be the test coverage for a feature like this? Looking at the tests from this file, it seems like this could be written in two ways:
What's usually expected? |
Something like this should be OK: tests.test("${Type}/MaskingAdd") {
expectEqual(${Type}.max &+ 1, ${Type}.min)
do {
var x = ${Type}.max
x &+= 1
expectEqual(x, ${Type}.min)
}
} The trick with an |
Thanks a lot @moiseev. I took the liberty of separating the operators into pairs of tests (non-assignment/assignment) to match the style of the previous tests in that file, but if that's not ideal just give me a heads up and I'll do them like in your example. (There were no tests for |
test/stdlib/Integers.swift.gyb
Outdated
tests.test("${Type}/MaskingMultiply") { | ||
var x = ${Type}.max | ||
x &*= 2 | ||
expectEqual(x, ${Type}.max &* 2) |
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.
Since these operators are defined in terms of one another, it makes the test ... not testing anything =)
How about something like expectEquals(${Type}.min, (${Type}.max / 2 + 1) &* 2)
?
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.
Good catch, I couldn't think of a guaranteed single way to test multiplication overflow across all types 🙂
What about this?
tests.test("${Type}/Multiply/MaskingOverflow") {
expectEqual((${Type}.max / 2 + 1) &* 2, ${Type}.min)
}
tests.test("${Type}/MultiplyInPlace/MaskingOverflow") {
var x = ${Type}.max / 2 + 1
x &*= 2
expectEqual(x, ${Type}.min)
}
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.
in expectXXX
functions, the expected value is the first parameter.
Sorry, I did not notice it in the first pass through the code. Can you please update the usages?
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.
Got it.
@swift-ci Please smoke test |
Thanks @rockbruno for your contribution! |
This is fantastic. Glad it's finally done. Thanks @rockbruno! |
Awesome. Thanks a lot guys! |
This adds the overflow assignment operators (
&+= &-= &*=
) that were missing from integer types.To match how other operators are written, I moved the regular overflow operator's implementation to the new assignment operators. I am unsure if this will have performance implications, so please let me know if this is a bad idea! I also removed the trailing white space from it's declaration since the other operators did not have one.
Resolves SR-4818.
Resolves: rdar://problem/32035586