Skip to content

Commit cdd9956

Browse files
committed
[Foundation] Fix potential backdeployment regression in append
Data.append now calls Data.count’s setter, which used to have a bug in previously shipped versions of the Foundation overlay (swiftlang#28918). To prevent working code from breaking when recompiled with the current version, avoid calling Data.count’s setter directly. Instead, extract its implementation (conveniently already packaged into a nested function) into a named method, marking it @_alwaysEmitIntoClient.
1 parent a6d7ee5 commit cdd9956

File tree

1 file changed

+65
-67
lines changed

1 file changed

+65
-67
lines changed

stdlib/public/Darwin/Foundation/Data.swift

Lines changed: 65 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1415,6 +1415,66 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
14151415
}
14161416
}
14171417

1418+
@inlinable
1419+
@usableFromInline
1420+
@_alwaysEmitIntoClient
1421+
internal mutating func _truncateOrZeroExtend(toCount newCount: Int) {
1422+
switch self {
1423+
case .empty:
1424+
if newCount == 0 {
1425+
return
1426+
} else if InlineData.canStore(count: newCount) {
1427+
self = .inline(InlineData(count: newCount))
1428+
} else if InlineSlice.canStore(count: newCount) {
1429+
self = .slice(InlineSlice(count: newCount))
1430+
} else {
1431+
self = .large(LargeSlice(count: newCount))
1432+
}
1433+
case .inline(var inline):
1434+
if newCount == 0 {
1435+
self = .empty
1436+
} else if InlineData.canStore(count: newCount) {
1437+
guard inline.count != newCount else { return }
1438+
inline.count = newCount
1439+
self = .inline(inline)
1440+
} else if InlineSlice.canStore(count: newCount) {
1441+
var slice = InlineSlice(inline)
1442+
slice.count = newCount
1443+
self = .slice(slice)
1444+
} else {
1445+
var slice = LargeSlice(inline)
1446+
slice.count = newCount
1447+
self = .large(slice)
1448+
}
1449+
case .slice(var slice):
1450+
if newCount == 0 && slice.startIndex == 0 {
1451+
self = .empty
1452+
} else if slice.startIndex == 0 && InlineData.canStore(count: newCount) {
1453+
self = .inline(InlineData(slice, count: newCount))
1454+
} else if InlineSlice.canStore(count: newCount + slice.startIndex) {
1455+
guard slice.count != newCount else { return }
1456+
self = .empty // TODO: remove this when mgottesman lands optimizations
1457+
slice.count = newCount
1458+
self = .slice(slice)
1459+
} else {
1460+
var newSlice = LargeSlice(slice)
1461+
newSlice.count = newCount
1462+
self = .large(newSlice)
1463+
}
1464+
case .large(var slice):
1465+
if newCount == 0 && slice.startIndex == 0 {
1466+
self = .empty
1467+
} else if slice.startIndex == 0 && InlineData.canStore(count: newCount) {
1468+
self = .inline(InlineData(slice, count: newCount))
1469+
} else {
1470+
guard slice.count != newCount else { return }
1471+
self = .empty // TODO: remove this when mgottesman lands optimizations
1472+
slice.count = newCount
1473+
self = .large(slice)
1474+
}
1475+
}
1476+
}
1477+
14181478
@inlinable // This is @inlinable as reasonably small.
14191479
var count: Int {
14201480
get {
@@ -1426,69 +1486,7 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
14261486
}
14271487
}
14281488
set(newValue) {
1429-
// HACK: The definition of this inline function takes an inout reference to self, giving the optimizer a unique referencing guarantee.
1430-
// This allows us to avoid excessive retain-release traffic around modifying enum values, and inlining the function then avoids the additional frame.
1431-
@inline(__always)
1432-
func apply(_ representation: inout _Representation, _ newValue: Int) -> _Representation? {
1433-
switch representation {
1434-
case .empty:
1435-
if newValue == 0 {
1436-
return nil
1437-
} else if InlineData.canStore(count: newValue) {
1438-
return .inline(InlineData(count: newValue))
1439-
} else if InlineSlice.canStore(count: newValue) {
1440-
return .slice(InlineSlice(count: newValue))
1441-
} else {
1442-
return .large(LargeSlice(count: newValue))
1443-
}
1444-
case .inline(var inline):
1445-
if newValue == 0 {
1446-
return .empty
1447-
} else if InlineData.canStore(count: newValue) {
1448-
guard inline.count != newValue else { return nil }
1449-
inline.count = newValue
1450-
return .inline(inline)
1451-
} else if InlineSlice.canStore(count: newValue) {
1452-
var slice = InlineSlice(inline)
1453-
slice.count = newValue
1454-
return .slice(slice)
1455-
} else {
1456-
var slice = LargeSlice(inline)
1457-
slice.count = newValue
1458-
return .large(slice)
1459-
}
1460-
case .slice(var slice):
1461-
if newValue == 0 && slice.startIndex == 0 {
1462-
return .empty
1463-
} else if slice.startIndex == 0 && InlineData.canStore(count: newValue) {
1464-
return .inline(InlineData(slice, count: newValue))
1465-
} else if InlineSlice.canStore(count: newValue + slice.startIndex) {
1466-
guard slice.count != newValue else { return nil }
1467-
representation = .empty // TODO: remove this when mgottesman lands optimizations
1468-
slice.count = newValue
1469-
return .slice(slice)
1470-
} else {
1471-
var newSlice = LargeSlice(slice)
1472-
newSlice.count = newValue
1473-
return .large(newSlice)
1474-
}
1475-
case .large(var slice):
1476-
if newValue == 0 && slice.startIndex == 0 {
1477-
return .empty
1478-
} else if slice.startIndex == 0 && InlineData.canStore(count: newValue) {
1479-
return .inline(InlineData(slice, count: newValue))
1480-
} else {
1481-
guard slice.count != newValue else { return nil}
1482-
representation = .empty // TODO: remove this when mgottesman lands optimizations
1483-
slice.count = newValue
1484-
return .large(slice)
1485-
}
1486-
}
1487-
}
1488-
1489-
if let rep = apply(&self, newValue) {
1490-
self = rep
1491-
}
1489+
_truncateOrZeroExtend(toCount: newValue)
14921490
}
14931491
}
14941492

@@ -2388,16 +2386,16 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
23882386
// Copy as much as we can in one shot.
23892387
let underestimatedCount = elements.underestimatedCount
23902388
let originalCount = _representation.count
2391-
_representation.count += underestimatedCount
2392-
var (iter, endIndex): (S.Iterator, Int) = _representation.withUnsafeMutableBytes { buffer in
2389+
_representation._truncateOrZeroExtend(toCount: originalCount + underestimatedCount)
2390+
var (iter, copiedCount): (S.Iterator, Int) = _representation.withUnsafeMutableBytes { buffer in
23932391
let start = buffer.baseAddress!.assumingMemoryBound(to: UInt8.self) + originalCount
23942392
let b = UnsafeMutableBufferPointer(start: start, count: buffer.count - originalCount)
23952393
return elements._copyContents(initializing: b)
23962394
}
2397-
guard endIndex == underestimatedCount else {
2395+
guard copiedCount == underestimatedCount else {
23982396
// We can't trap here. We have to allow an underfilled buffer
23992397
// to emulate the previous implementation.
2400-
_representation.replaceSubrange(originalCount + endIndex ..< _representation.endIndex, with: nil, count: 0)
2398+
_representation._truncateOrZeroExtend(toCount: originalCount + copiedCount)
24012399
return
24022400
}
24032401

0 commit comments

Comments
 (0)