Skip to content

Commit d6e5805

Browse files
authored
Merge pull request #59960 from lorentey/duration-components-crash-5.7
[5.7][stdlib] Duration: Fix rare overflow trap in Int128.multipliedFullWidth
2 parents 36ae2b9 + 10d0b05 commit d6e5805

File tree

2 files changed

+62
-9
lines changed

2 files changed

+62
-9
lines changed

stdlib/public/core/Int128.swift.gyb

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -350,11 +350,24 @@ extension _${U}Int128: FixedWidthInteger {
350350
) -> (high: Self, low: Magnitude) {
351351
let isNegative = Self.isSigned && (self._isNegative != other._isNegative)
352352

353+
func sum(_ x: Low, _ y: Low) -> (high: Low, low: Low) {
354+
let (sum, overflow) = x.addingReportingOverflow(y)
355+
return (overflow ? 1 : 0, sum)
356+
}
357+
353358
func sum(_ x: Low, _ y: Low, _ z: Low) -> (high: Low, low: Low) {
354-
let (sum1, overflow1) = x.addingReportingOverflow(y)
355-
let (sum2, overflow2) = sum1.addingReportingOverflow(z)
356-
let carry: Low = (overflow1 ? 1 : 0) + (overflow2 ? 1 : 0)
357-
return (carry, sum2)
359+
let s1 = sum(x, y)
360+
let s2 = sum(s1.low, z)
361+
return (s1.high &+ s2.high, s2.low)
362+
}
363+
364+
func sum(
365+
_ x0: Low, _ x1: Low, _ x2: Low, _ x3: Low
366+
) -> (high: Low, low: Low) {
367+
let s1 = sum(x0, x1)
368+
let s2 = sum(x2, x3)
369+
let s = sum(s1.low, s2.low)
370+
return (s1.high &+ s2.high &+ s.high, s.low)
358371
}
359372

360373
let lhs = self.magnitude
@@ -366,12 +379,14 @@ extension _${U}Int128: FixedWidthInteger {
366379
let d = rhs.high.multipliedFullWidth(by: lhs.high)
367380

368381
let mid1 = sum(a.high, b.low, c.low)
369-
let mid2 = sum(b.high, c.high, d.low)
382+
let mid2 = sum(b.high, c.high, mid1.high, d.low)
370383

371-
let low = _UInt128(high: mid1.low, low: a.low)
372384
let high = _${U}Int128(
373-
high: High(mid2.high + d.high),
374-
low: mid1.high + mid2.low)
385+
high: High(d.high &+ mid2.high), // Note: this addition will never wrap
386+
low: mid2.low)
387+
let low = _UInt128(
388+
high: mid1.low,
389+
low: a.low)
375390

376391
if isNegative {
377392
let (lowComplement, overflow) = (~low).addingReportingOverflow(.one)

test/Concurrency/Runtime/clock.swift

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,44 @@ var tests = TestSuite("Time")
102102
expectEqual(twoSeconds, .seconds(2))
103103
}
104104

105+
tests.test("Duration components/whole second increments") {
106+
for i in 0 ..< 1_000_000 {
107+
let d = Duration.seconds(i)
108+
let comps = d.components
109+
expectEqual(comps.seconds, Int64(i))
110+
expectEqual(comps.attoseconds, 0)
111+
}
112+
}
113+
114+
tests.test("Duration components/1ms increments") {
115+
for i in 0 ..< 1_000_000 {
116+
let d = Duration.milliseconds(i)
117+
let comps = d.components
118+
expectEqual(comps.seconds, Int64(i / 1000))
119+
expectEqual(comps.attoseconds, Int64(i % 1000) * 1_000_000_000_000_000)
120+
}
121+
}
122+
123+
tests.test("Duration components/100µs increments") {
124+
for i in 0 ..< 1_000_000 {
125+
let ms = 100 * i
126+
let d = Duration.microseconds(ms)
127+
let comps = d.components
128+
expectEqual(comps.seconds, Int64(ms / 1_000_000))
129+
expectEqual(comps.attoseconds, Int64(ms % 1_000_000) * 1_000_000_000_000)
130+
}
131+
}
132+
133+
tests.test("Duration components/200ns increments") {
134+
for i in 0 ..< 1_000_000 {
135+
let ns = 200 * i
136+
let d = Duration.nanoseconds(ns)
137+
let comps = d.components
138+
expectEqual(comps.seconds, Int64(ns / 1_000_000_000))
139+
expectEqual(comps.attoseconds, Int64(ns % 1_000_000_000) * 1_000_000_000)
140+
}
141+
}
142+
105143
await runAllTestsAsync()
106144
}
107-
}
145+
}

0 commit comments

Comments
 (0)