mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
* Migrate from `UnsafePointer<Void>` to `UnsafeRawPointer`. As proposed in SE-0107: UnsafeRawPointer. `void*` imports as `UnsafeMutableRawPointer`. `const void*` imports as `UnsafeRawPointer`. Occurrences of `UnsafePointer<Void>` are replaced with UnsafeRawPointer. * Migrate overlays from UnsafePointer<Void> to UnsafeRawPointer. This requires explicit memory binding in several places, particularly in NSData and CoreAudio. * Fix a bunch of test cases for Void->Raw migration. * qsort takes IUO values * Bridge `Unsafe[Mutable]RawPointer as `void [const] *`. * Parse #dsohandle as UnsafeMutableRawPointer * Update a bunch of test cases for Void->Raw migration. * Trivial fix for the SceneKit test case. * Add an UnsafeRawPointer self initializer. This is unfortunately necessary for assignment between types imported from C. * Tiny simplification of the initializer.
878 lines
25 KiB
Plaintext
878 lines
25 KiB
Plaintext
// RUN: %target-run-simple-swiftgyb
|
|
// REQUIRES: executable_test
|
|
|
|
import StdlibUnittest
|
|
import StdlibCollectionUnittest
|
|
|
|
let CopyToNativeArrayBufferTests = TestSuite("CopyToNativeArrayBufferTests")
|
|
|
|
CopyToNativeArrayBufferTests.test("Sequence._copyToContiguousArray()") {
|
|
do {
|
|
// Call from a static context.
|
|
let s =
|
|
MinimalSequence(elements: LifetimeTracked(10)..<LifetimeTracked(27))
|
|
|
|
expectEqual(0, s.timesMakeIteratorCalled.value)
|
|
let copy = s._copyToContiguousArray()
|
|
expectEqual(1, s.timesMakeIteratorCalled.value)
|
|
expectEqualSequence(
|
|
Array(10..<27),
|
|
copy.map { $0.value })
|
|
}
|
|
do {
|
|
// Call from a generic context.
|
|
let wrapped = MinimalSequence(elements: LifetimeTracked(10)..<LifetimeTracked(27))
|
|
let s = LoggingSequence(wrapping: wrapped)
|
|
|
|
expectEqual(0, wrapped.timesMakeIteratorCalled.value)
|
|
let copy = s._copyToContiguousArray()
|
|
expectEqual(1, wrapped.timesMakeIteratorCalled.value)
|
|
expectEqualSequence(
|
|
Array(10..<27),
|
|
copy.map { $0.value })
|
|
}
|
|
}
|
|
|
|
CopyToNativeArrayBufferTests.test("Collection._copyToContiguousArray()") {
|
|
// Check that collections are handled with the collection-specific API. This
|
|
// means that we are calling the right default implementation (one for
|
|
// collections, not the one for sequences).
|
|
|
|
do {
|
|
// Call from a static context.
|
|
let c =
|
|
DefaultedCollection(elements: LifetimeTracked(10)..<LifetimeTracked(27))
|
|
|
|
expectEqual(0, c.timesMakeIteratorCalled.value)
|
|
expectEqual(0, c.timesStartIndexCalled.value)
|
|
let copy = c._copyToContiguousArray()
|
|
expectEqual(0, c.timesMakeIteratorCalled.value)
|
|
expectNotEqual(0, c.timesStartIndexCalled.value)
|
|
expectEqualSequence(
|
|
Array(10..<27),
|
|
copy.map { $0.value })
|
|
}
|
|
do {
|
|
// Call from a generic context.
|
|
let wrapped =
|
|
DefaultedCollection(elements: LifetimeTracked(10)..<LifetimeTracked(27))
|
|
let s = LoggingSequence(wrapping: wrapped)
|
|
expectEqual(0, wrapped.timesMakeIteratorCalled.value)
|
|
expectEqual(0, wrapped.timesStartIndexCalled.value)
|
|
let copy = s._copyToContiguousArray()
|
|
expectEqual(0, wrapped.timesMakeIteratorCalled.value)
|
|
expectNotEqual(0, wrapped.timesStartIndexCalled.value)
|
|
|
|
expectEqualSequence(
|
|
Array(10..<27),
|
|
copy.map { $0.value })
|
|
}
|
|
}
|
|
|
|
%{
|
|
all_array_types = ['ContiguousArray', 'ArraySlice', 'Array']
|
|
}%
|
|
|
|
extension Array {
|
|
var identity: UnsafeRawPointer {
|
|
return self._buffer.identity
|
|
}
|
|
}
|
|
|
|
extension ArraySlice {
|
|
var identity: UnsafeRawPointer {
|
|
return self._buffer.identity
|
|
}
|
|
}
|
|
|
|
extension ContiguousArray {
|
|
var identity: UnsafeRawPointer {
|
|
return self._buffer.identity
|
|
}
|
|
}
|
|
|
|
var ArrayTestSuite = TestSuite("Array")
|
|
|
|
ArrayTestSuite.test("sizeof") {
|
|
var a = [ 10, 20, 30 ]
|
|
#if arch(i386) || arch(arm)
|
|
expectEqual(4, sizeofValue(a))
|
|
#else
|
|
expectEqual(8, sizeofValue(a))
|
|
#endif
|
|
}
|
|
|
|
ArrayTestSuite.test("valueDestruction") {
|
|
var a = [LifetimeTracked]()
|
|
for i in 100...110 {
|
|
a.append(LifetimeTracked(i))
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Native array tests
|
|
// FIXME: incomplete.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
ArrayTestSuite.test("Native/count/empty") {
|
|
let a = [LifetimeTracked]()
|
|
expectEqual(0, a.count)
|
|
}
|
|
|
|
ArrayTestSuite.test("Native/count") {
|
|
let a = [ LifetimeTracked(10), LifetimeTracked(20), LifetimeTracked(30) ]
|
|
expectEqual(3, a.count)
|
|
}
|
|
|
|
ArrayTestSuite.test("Native/isEmpty/empty") {
|
|
let a = [LifetimeTracked]()
|
|
expectTrue(a.isEmpty)
|
|
}
|
|
|
|
ArrayTestSuite.test("Native/isEmpty") {
|
|
let a = [ LifetimeTracked(10), LifetimeTracked(20), LifetimeTracked(30) ]
|
|
expectFalse(a.isEmpty)
|
|
}
|
|
|
|
protocol TestProtocol1 {}
|
|
|
|
% for array_type in all_array_types:
|
|
|
|
// Check that the generic parameter is called 'Element'.
|
|
extension ${array_type} where Element : TestProtocol1 {
|
|
var _elementIsTestProtocol1: Bool {
|
|
fatalError("not implemented")
|
|
}
|
|
}
|
|
|
|
/// Returns an ${array_type} that does not share its buffer with other arrays.
|
|
func getFresh${array_type}<S : Sequence>(_ sequence: S)
|
|
-> ${array_type}<S.Iterator.Element> {
|
|
var result: ${array_type}<S.Iterator.Element> = []
|
|
result.reserveCapacity(sequence.underestimatedCount)
|
|
for element in sequence {
|
|
result.append(element)
|
|
}
|
|
return result
|
|
}
|
|
|
|
% end
|
|
|
|
enum EnumWithoutPayloads : Equatable {
|
|
case A, B, C, D
|
|
}
|
|
|
|
func == (lhs: EnumWithoutPayloads, rhs: EnumWithoutPayloads) -> Bool {
|
|
switch (lhs, rhs) {
|
|
case (.A, .A), (.B, .B), (.C, .C), (.D, .D):
|
|
return true
|
|
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
struct SequenceWithCustomUnderestimatedCount : Sequence {
|
|
init(_ data: [Int]) {
|
|
self._data = MinimalSequence(elements: data.map(OpaqueValue.init))
|
|
}
|
|
|
|
func makeIterator() -> MinimalSequence<OpaqueValue<Int>>.Iterator {
|
|
return _data.makeIterator()
|
|
}
|
|
|
|
var underestimatedCount: Int {
|
|
SequenceWithCustomUnderestimatedCount.timesUnderestimatedCountWasCalled += 1
|
|
return _data.underestimatedCount
|
|
}
|
|
|
|
static var timesUnderestimatedCountWasCalled: Int = 0
|
|
|
|
let _data: MinimalSequence<OpaqueValue<Int>>
|
|
}
|
|
|
|
% for array_type in all_array_types:
|
|
|
|
ArrayTestSuite.test("${array_type}/init(Sequence)") {
|
|
let base = SequenceWithCustomUnderestimatedCount(
|
|
[ 0, 30, 10, 90 ])
|
|
|
|
SequenceWithCustomUnderestimatedCount.timesUnderestimatedCountWasCalled = 0
|
|
|
|
let result = ${array_type}(base)
|
|
|
|
expectEqual([ 0, 30, 10, 90 ], result.map { $0.value })
|
|
|
|
expectEqual(1, SequenceWithCustomUnderestimatedCount.timesUnderestimatedCountWasCalled)
|
|
|
|
expectEqualSequence(
|
|
[], Array(base).map { $0.value }, "sequence should be consumed")
|
|
}
|
|
|
|
ArrayTestSuite.test("${array_type}/Sliceable/Enums") {
|
|
typealias E = EnumWithoutPayloads
|
|
|
|
do {
|
|
let expected = [E.A, E.B, E.C, E.D]
|
|
let sliceable = ${array_type}(expected)
|
|
checkSliceableWithBidirectionalIndex(expected, sliceable)
|
|
}
|
|
|
|
/*
|
|
FIXME: add this test when Array<T> can be conditionally Equatable.
|
|
do {
|
|
let expected = [[E.A, E.B], [E.B, E.C], [E.D], [E.A, E.B, E.D]]
|
|
let sliceable = ${array_type}(expected)
|
|
checkSliceableWithBidirectionalIndex(
|
|
expected, sliceable, SourceLocStack().withCurrentLoc())
|
|
}
|
|
*/
|
|
}
|
|
|
|
ArrayTestSuite.test("${array_type}/appendNonUnique") {
|
|
var x: ${array_type}<Int> = []
|
|
x.reserveCapacity(10002)
|
|
let capacity = x.capacity
|
|
for _ in 1...10000 {
|
|
let y = x
|
|
x.append(1)
|
|
expectTrue(x.capacity == capacity)
|
|
let z = x
|
|
x.remove(at: 0)
|
|
}
|
|
}
|
|
|
|
ArrayTestSuite.test("${array_type}/emptyAllocation") {
|
|
let arr0 = ${array_type}<Int>()
|
|
let arr1 = ${array_type}<LifetimeTracked>(repeating: LifetimeTracked(0), count: 0)
|
|
// Empty arrays all use the same buffer
|
|
expectEqual(arr0._buffer.identity, arr1._buffer.identity)
|
|
|
|
let arr2: ${array_type}<LifetimeTracked> = []
|
|
let emptyLiteralsShareBuffer = arr0._buffer.identity == arr2._buffer.identity
|
|
expectTrue(emptyLiteralsShareBuffer)
|
|
}
|
|
|
|
ArrayTestSuite.test("${array_type}/filter") {
|
|
do {
|
|
let arr: ${array_type}<Int> = []
|
|
var result = arr.filter() {
|
|
(x: Int) -> Bool in
|
|
expectUnreachable()
|
|
return true
|
|
}
|
|
expectType(Array<Int>.self, &result)
|
|
expectEqual([], result)
|
|
expectEqual(0, result.capacity)
|
|
}
|
|
do {
|
|
let arr: ${array_type}<Int> = [ 0, 30, 10, 90 ]
|
|
let result = arr.filter() { (x: Int) -> Bool in true }
|
|
expectEqual([ 0, 30, 10, 90 ], result)
|
|
expectGE(2 * result.count, result.capacity)
|
|
}
|
|
do {
|
|
let arr: ${array_type}<Int> = [ 0, 30, 10, 90 ]
|
|
let result = arr.filter() { (x: Int) -> Bool in false }
|
|
expectEqual([], result)
|
|
expectGE(2 * result.count, result.capacity)
|
|
}
|
|
do {
|
|
let arr: ${array_type}<Int> = [ 0, 30, 10, 90 ]
|
|
let result = arr.filter() { $0 % 3 == 0 }
|
|
expectEqual([ 0, 30, 90 ], result)
|
|
expectGE(2 * result.count, result.capacity)
|
|
}
|
|
}
|
|
|
|
ArrayTestSuite.test("${array_type}/map") {
|
|
do {
|
|
let arr: ${array_type}<Int> = []
|
|
var result = arr.map() {
|
|
(x: Int) -> Int16 in
|
|
expectUnreachable()
|
|
return 42
|
|
}
|
|
expectType(Array<Int16>.self, &result)
|
|
expectEqual([], result)
|
|
expectEqual(0, result.capacity)
|
|
}
|
|
do {
|
|
let arr: ${array_type}<Int> = [ 0, 30, 10, 90 ]
|
|
let result = arr.map() { $0 + 1 }
|
|
expectEqual([ 1, 31, 11, 91 ], result)
|
|
expectGE(2 * result.count, result.capacity)
|
|
}
|
|
}
|
|
|
|
ArrayTestSuite.test("${array_type}/flatMap") {
|
|
let enumerate : (Int) -> ${array_type}<Int> =
|
|
{ return ${array_type}(1..<($0 + 1)) }
|
|
expectEqualSequence([], ${array_type}().flatMap(enumerate))
|
|
expectEqualSequence([ 1 ], ${array_type}([ 1 ]).flatMap(enumerate))
|
|
expectEqualSequence(
|
|
[ 1, 1, 2 ],
|
|
${array_type}([ 1, 2 ]).flatMap(enumerate))
|
|
expectEqualSequence(
|
|
[ 1, 1, 1, 2 ],
|
|
${array_type}([ 1, 2 ]).flatMap(enumerate).flatMap(enumerate))
|
|
}
|
|
|
|
ArrayTestSuite.test("${array_type}/Mirror") {
|
|
do {
|
|
let input: ${array_type}<Int> = []
|
|
var output = ""
|
|
dump(input, to: &output)
|
|
|
|
let expected =
|
|
"- 0 elements\n"
|
|
|
|
expectEqual(expected, output)
|
|
}
|
|
do {
|
|
let input: ${array_type}<Int> = [ 10, 20, 30, 40 ]
|
|
var output = ""
|
|
dump(input, to: &output)
|
|
|
|
let expected =
|
|
"▿ 4 elements\n" +
|
|
" - 10\n" +
|
|
" - 20\n" +
|
|
" - 30\n" +
|
|
" - 40\n"
|
|
|
|
expectEqual(expected, output)
|
|
}
|
|
% if array_type == 'ArraySlice':
|
|
do {
|
|
let base = [ 10, 20, 30, 40 ]
|
|
let input: ArraySlice<Int> = base[1..<3]
|
|
var output = ""
|
|
dump(input, to: &output)
|
|
|
|
let expected =
|
|
"▿ 2 elements\n" +
|
|
" - 20\n" +
|
|
" - 30\n"
|
|
|
|
expectEqual(expected, output)
|
|
}
|
|
% end
|
|
}
|
|
|
|
% end
|
|
|
|
% for Kind in ['Array', 'ContiguousArray']:
|
|
ArrayTestSuite.test("${Kind}/popLast") {
|
|
// Empty
|
|
do {
|
|
var a = ${Kind}<Int>()
|
|
let popped = a.popLast()
|
|
expectEmpty(popped)
|
|
expectTrue(a.isEmpty)
|
|
}
|
|
|
|
do {
|
|
var popped = [Int]()
|
|
var a: ${Kind}<Int> = [1010, 2020, 3030]
|
|
while let element = a.popLast() {
|
|
popped.append(element)
|
|
}
|
|
expectEqualSequence([1010, 2020, 3030], popped.reversed())
|
|
expectTrue(a.isEmpty)
|
|
}
|
|
}
|
|
% end
|
|
|
|
// Check how removeFirst() affects indices.
|
|
% for Kind in ['Array', 'ContiguousArray']:
|
|
ArrayTestSuite.test("${Kind}/removeFirst") {
|
|
do {
|
|
var a: ${Kind}<OpaqueValue<Int>> = ${Kind}([ 1 ].map(OpaqueValue.init))
|
|
a.removeFirst()
|
|
expectEqual(0, a.startIndex)
|
|
}
|
|
do {
|
|
var a: ${Kind}<OpaqueValue<Int>> = ${Kind}([ 1, 2 ].map(OpaqueValue.init))
|
|
a.removeFirst()
|
|
expectEqual(0, a.startIndex)
|
|
}
|
|
}
|
|
% end
|
|
|
|
ArrayTestSuite.test("ArraySlice/removeFirst") {
|
|
do {
|
|
let a: [OpaqueValue<Int>] = [ 99, 1010, 99 ].map(OpaqueValue.init)
|
|
var s = a[1..<2]
|
|
expectEqual(1, s.startIndex)
|
|
s.removeFirst()
|
|
expectEqual(2, s.startIndex)
|
|
}
|
|
do {
|
|
let a: [OpaqueValue<Int>] = [ 99, 1010, 2020, 99 ].map(OpaqueValue.init)
|
|
var s = a[1..<2]
|
|
expectEqual(1, s.startIndex)
|
|
s.removeFirst()
|
|
expectEqual(2, s.startIndex)
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// _withUnsafeMutableBufferPointerIfSupported()
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
struct WithUnsafeMutableBufferPointerIfSupportedTest {
|
|
let sequence: [Int]
|
|
let loc: SourceLoc
|
|
|
|
init(
|
|
_ sequence: [Int],
|
|
file: String = #file, line: UInt = #line
|
|
) {
|
|
self.sequence = sequence
|
|
self.loc = SourceLoc(file, line, comment: "test data")
|
|
}
|
|
}
|
|
|
|
let withUnsafeMutableBufferPointerIfSupportedTests = [
|
|
WithUnsafeMutableBufferPointerIfSupportedTest([]),
|
|
WithUnsafeMutableBufferPointerIfSupportedTest([ 10 ]),
|
|
WithUnsafeMutableBufferPointerIfSupportedTest([ 10, 20, 30, 40, 50 ]),
|
|
]
|
|
|
|
% for array_type in all_array_types:
|
|
|
|
ArrayTestSuite.test("${array_type}/_withUnsafeMutableBufferPointerIfSupported") {
|
|
for test in withUnsafeMutableBufferPointerIfSupportedTests {
|
|
var a = getFresh${array_type}(test.sequence.map(OpaqueValue.init))
|
|
do {
|
|
// Read.
|
|
var result = a._withUnsafeMutableBufferPointerIfSupported {
|
|
(baseAddress, count) -> OpaqueValue<[OpaqueValue<Int>]> in
|
|
let bufferPointer =
|
|
UnsafeMutableBufferPointer(start: baseAddress, count: count)
|
|
return OpaqueValue(Array(bufferPointer))
|
|
}
|
|
expectType(Optional<OpaqueValue<Array<OpaqueValue<Int>>>>.self, &result)
|
|
expectEqualSequence(test.sequence, result!.value.map { $0.value })
|
|
expectEqualSequence(test.sequence, a.map { $0.value })
|
|
}
|
|
do {
|
|
// Read and write.
|
|
var result = a._withUnsafeMutableBufferPointerIfSupported {
|
|
(baseAddress, count) -> OpaqueValue<Array<OpaqueValue<Int>>> in
|
|
let bufferPointer =
|
|
UnsafeMutableBufferPointer(start: baseAddress, count: count)
|
|
let result = OpaqueValue(Array(bufferPointer))
|
|
for i in bufferPointer.indices {
|
|
bufferPointer[i] = OpaqueValue(bufferPointer[i].value * 10)
|
|
}
|
|
return result
|
|
}
|
|
expectType(Optional<OpaqueValue<Array<OpaqueValue<Int>>>>.self, &result)
|
|
expectEqualSequence(test.sequence, result!.value.map { $0.value })
|
|
expectEqualSequence(
|
|
test.sequence.map { $0 * 10 },
|
|
a.map { $0.value })
|
|
}
|
|
}
|
|
// FIXME: tests for arrays bridged from Objective-C.
|
|
}
|
|
|
|
ArrayTestSuite.test("${array_type}/_withUnsafeMutableBufferPointerIfSupported/ReplacingTheBufferTraps/1") {
|
|
var a = getFresh${array_type}([ OpaqueValue(10) ])
|
|
var result = a._withUnsafeMutableBufferPointerIfSupported {
|
|
(baseAddress, count) -> OpaqueValue<Int> in
|
|
// buffer = UnsafeMutableBufferPointer(start: buffer.baseAddress, count: 0)
|
|
// FIXME: does not trap since the buffer is not passed inout.
|
|
// expectCrashLater()
|
|
return OpaqueValue(42)
|
|
}
|
|
}
|
|
|
|
ArrayTestSuite.test("${array_type}/_withUnsafeMutableBufferPointerIfSupported/ReplacingTheBufferTraps/2") {
|
|
var a = getFresh${array_type}([ OpaqueValue(10) ])
|
|
var result = a._withUnsafeMutableBufferPointerIfSupported {
|
|
(baseAddress, count) -> OpaqueValue<Int> in
|
|
// buffer = UnsafeMutableBufferPointer(start: nil, count: 1)
|
|
// FIXME: does not trap since the buffer is not passed inout.
|
|
// expectCrashLater()
|
|
return OpaqueValue(42)
|
|
}
|
|
}
|
|
|
|
//===---
|
|
// Check that iterators traverse a snapshot of the collection.
|
|
//===---
|
|
|
|
ArrayTestSuite.test(
|
|
"${array_type}/mutationDoesNotAffectIterator/subscript/store") {
|
|
var arr: ${array_type}<Int> = [ 1010, 1020, 1030 ]
|
|
var iter = arr.makeIterator()
|
|
arr[0] = 1011
|
|
expectEqual([ 1010, 1020, 1030 ], Array(IteratorSequence(iter)))
|
|
}
|
|
|
|
ArrayTestSuite.test(
|
|
"${array_type}/mutationDoesNotAffectIterator/subscript/append") {
|
|
var arr: ${array_type}<Int> = [ 1010, 1020, 1030 ]
|
|
var iter = arr.makeIterator()
|
|
arr.append(1040)
|
|
expectEqual([ 1010, 1020, 1030 ], Array(IteratorSequence(iter)))
|
|
}
|
|
|
|
ArrayTestSuite.test(
|
|
"${array_type}/mutationDoesNotAffectIterator/subscript/replaceSubrange") {
|
|
var arr: ${array_type}<Int> = [ 1010, 1020, 1030 ]
|
|
var iter = arr.makeIterator()
|
|
arr.replaceSubrange(1..<3, with: [ 1040, 1050, 1060 ])
|
|
expectEqual([ 1010, 1020, 1030 ], Array(IteratorSequence(iter)))
|
|
}
|
|
|
|
% end
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Array and EvilCollection that changes its size while we are not looking
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
let evilBoundsError = "EvilCollection: index out of range"
|
|
|
|
final class EvilSequence : Sequence {
|
|
init(_ growth: Int) {
|
|
self.growth = growth
|
|
}
|
|
|
|
var growth: Int
|
|
var _count: Int = 20
|
|
|
|
var underestimatedCount: Int {
|
|
defer { _count += growth }
|
|
return _count
|
|
}
|
|
|
|
func makeIterator() -> AnyIterator<LifetimeTracked> {
|
|
var i = 0
|
|
return AnyIterator {
|
|
if i >= self._count { return nil }
|
|
let result = LifetimeTracked(i)
|
|
i += 1
|
|
return result
|
|
}
|
|
}
|
|
}
|
|
|
|
final class EvilCollection : Collection {
|
|
func index(after i: Int) -> Int {
|
|
return i + 1
|
|
}
|
|
|
|
init(_ growth: Int, boundsChecked: Bool) {
|
|
self.growth = growth
|
|
self.boundsChecked = boundsChecked
|
|
}
|
|
|
|
var growth: Int
|
|
var _count: Int = 20
|
|
var boundsChecked: Bool
|
|
|
|
var startIndex : Int {
|
|
_count += growth
|
|
return 0
|
|
}
|
|
|
|
var endIndex : Int {
|
|
return _count
|
|
}
|
|
|
|
subscript(i: Int) -> LifetimeTracked {
|
|
if boundsChecked {
|
|
precondition(i >= 0 && i < _count, evilBoundsError)
|
|
}
|
|
return LifetimeTracked(i)
|
|
}
|
|
|
|
// Default implementation will call _failEarlyRangeCheck,
|
|
// passing in a startIndex that will grow _count faster than
|
|
// necessary.
|
|
func formIndex(after i: inout Int) {
|
|
i += 1
|
|
}
|
|
}
|
|
|
|
for (step, evilBoundsCheck) in [ (1, true), (-1, false), (-1, true) ] {
|
|
|
|
let message = step < 0 && evilBoundsCheck
|
|
? evilBoundsError
|
|
: "invalid Collection: count differed in successive traversals"
|
|
|
|
let constructionMessage =
|
|
/*_isStdlibInternalChecksEnabled() && !evilBoundsCheck && step <= 0
|
|
? "_UnsafePartiallyInitializedContiguousArrayBuffer has no more capacity"
|
|
:*/ message
|
|
|
|
// The invalid Collection error is a _debugPreconditon that will only fire
|
|
// in a Debug assert configuration.
|
|
let expectedToFail = (step < 0 && evilBoundsCheck) ||
|
|
_isDebugAssertConfiguration()
|
|
|
|
let natureOfEvil = step > 0 ? "Growth" : "Shrinkage"
|
|
let boundsChecked = evilBoundsCheck ? "BoundsChecked" : "NoBoundsCheck"
|
|
let testPrefix = "MemorySafety/\(boundsChecked)/Evil\(natureOfEvil)"
|
|
|
|
ArrayTestSuite.test("\(testPrefix)/Infrastructure/EvilSequence") {
|
|
let evil = EvilSequence(step)
|
|
let count0 = evil.underestimatedCount
|
|
let count1 = evil.underestimatedCount
|
|
expectNotEqual(count0, count1)
|
|
if step > 0 {
|
|
expectLE(count0, count1)
|
|
}
|
|
else {
|
|
expectGE(count0, count1)
|
|
}
|
|
}
|
|
|
|
let t1 = ArrayTestSuite.test("\(testPrefix)/Infrastructure/EvilCollection")
|
|
(evilBoundsCheck && _isDebugAssertConfiguration()
|
|
? t1.crashOutputMatches(evilBoundsError) : t1)
|
|
.code {
|
|
let evil = EvilCollection(step, boundsChecked: evilBoundsCheck)
|
|
let count0 = evil.count
|
|
let count1 = evil.count
|
|
expectNotEqual(count0, count1)
|
|
if step > 0 {
|
|
expectLE(count0, count1)
|
|
}
|
|
else {
|
|
expectGE(count0, count1)
|
|
}
|
|
if evilBoundsCheck {
|
|
expectCrashLater()
|
|
}
|
|
let x = evil[-1]
|
|
_blackHole(x)
|
|
}
|
|
|
|
let t2 = ArrayTestSuite.test("\(testPrefix)/Construction")
|
|
(_isDebugAssertConfiguration() && expectedToFail
|
|
? t2.crashOutputMatches(constructionMessage) : t2)
|
|
.code {
|
|
let evil = EvilCollection(step, boundsChecked: evilBoundsCheck)
|
|
|
|
if expectedToFail {
|
|
expectCrashLater()
|
|
}
|
|
|
|
let a = Array(evil)
|
|
_blackHole(a)
|
|
}
|
|
|
|
for (op, rangeMax) in ["Grow":0, "Shrink":200] {
|
|
let t3 = ArrayTestSuite.test("\(testPrefix)/replaceSubrange/\(op)Unique")
|
|
(_isDebugAssertConfiguration() ? t3.crashOutputMatches(message) : t3)
|
|
.code {
|
|
let evil = EvilCollection(step, boundsChecked: evilBoundsCheck)
|
|
var a = Array((0..<200).lazy.map { LifetimeTracked($0) })
|
|
if expectedToFail {
|
|
expectCrashLater()
|
|
}
|
|
a.replaceSubrange(0..<rangeMax, with: evil)
|
|
}
|
|
|
|
let t4 = ArrayTestSuite.test("\(testPrefix)/replaceSubrange/\(op)NonUnique")
|
|
(_isDebugAssertConfiguration() ? t4.crashOutputMatches(message) : t4)
|
|
.code {
|
|
let evil = EvilCollection(step, boundsChecked: evilBoundsCheck)
|
|
var a = Array((0..<200).lazy.map { LifetimeTracked($0) })
|
|
var b = a
|
|
if expectedToFail {
|
|
expectCrashLater()
|
|
}
|
|
a.replaceSubrange(0..<rangeMax, with: evil)
|
|
_fixLifetime(b)
|
|
}
|
|
}
|
|
|
|
ArrayTestSuite.test("\(testPrefix)/SequenceMap")
|
|
.skip(.custom(
|
|
{ _isFastAssertConfiguration() },
|
|
reason: "this trap is not guaranteed to happen in -Ounchecked"))
|
|
.code {
|
|
let evil = EvilSequence(step)
|
|
|
|
if step < 0 {
|
|
expectCrashLater()
|
|
}
|
|
let a = evil.map { $0 }
|
|
_blackHole(a)
|
|
}
|
|
|
|
ArrayTestSuite.test("\(testPrefix)/CollectionMap")
|
|
.code {
|
|
let evil = EvilCollection(step, boundsChecked: evilBoundsCheck)
|
|
|
|
if expectedToFail {
|
|
expectCrashLater()
|
|
}
|
|
|
|
let a = evil.map { $0 }
|
|
_blackHole(a)
|
|
}
|
|
|
|
ArrayTestSuite.test("\(testPrefix)/FilterAll")
|
|
.code {
|
|
let evil = EvilCollection(step, boundsChecked: evilBoundsCheck)
|
|
|
|
let a = evil.filter { _ in true }
|
|
_blackHole(a)
|
|
}
|
|
|
|
ArrayTestSuite.test("\(testPrefix)/FilterNone")
|
|
.code {
|
|
let evil = EvilCollection(step, boundsChecked: evilBoundsCheck)
|
|
|
|
let a = evil.filter { _ in false }
|
|
_blackHole(a)
|
|
}
|
|
}
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Special cases and one-off tests.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
% for array_type in all_array_types:
|
|
|
|
ArrayTestSuite.test("${array_type}<Void>/map") {
|
|
// This code used to crash because it generated an array of Void with
|
|
// stride == 0.
|
|
do {
|
|
let input: ${array_type}<Void> = [ (), (), () ]
|
|
let result = input.map { (_) -> Void in return () }
|
|
expectEqual(3, result.count)
|
|
}
|
|
|
|
do {
|
|
let input: ${array_type}<OpaqueValue<Int>> = [
|
|
OpaqueValue(10), OpaqueValue(20), OpaqueValue(30)
|
|
]
|
|
let result = input.map { (_) -> Void in return () }
|
|
expectEqual(3, result.count)
|
|
}
|
|
}
|
|
|
|
% end
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// MutableCollectionType and RangeReplaceableCollectionType conformance tests.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
% for array_type in all_array_types:
|
|
ArrayTestSuite.test("${array_type}/AssociatedTypes") {
|
|
typealias Collection = ${array_type}<OpaqueValue<Int>>
|
|
typealias CollectionSlice = ArraySlice<OpaqueValue<Int>>
|
|
expectCollectionAssociatedTypes(
|
|
collectionType: Collection.self,
|
|
iteratorType: IndexingIterator<Collection>.self,
|
|
subSequenceType: CollectionSlice.self,
|
|
indexType: Int.self,
|
|
indexDistanceType: Int.self,
|
|
indicesType: CountableRange<Int>.self)
|
|
}
|
|
% end
|
|
|
|
% for array_type in all_array_types:
|
|
% collection_or_slice = 'Slice' if 'Slice' in array_type else 'Collection'
|
|
|
|
do {
|
|
// `Array`, `ArraySlice`, and `ContiguousArrayBuffer` have no expectation of
|
|
// failure for advancing their indexes "out of bounds", because they are just
|
|
// `Int`.
|
|
var resiliencyChecks = CollectionMisuseResiliencyChecks.all
|
|
resiliencyChecks.creatingOutOfBoundsIndicesBehavior = .none
|
|
|
|
// Test MutableCollectionType conformance with value type elements.
|
|
ArrayTestSuite.addMutableRandomAccessCollectionTests(
|
|
makeCollection: { (elements: [OpaqueValue<Int>]) in
|
|
return ${array_type}(elements)
|
|
},
|
|
wrapValue: identity,
|
|
extractValue: identity,
|
|
makeCollectionOfEquatable: { (elements: [MinimalEquatableValue]) in
|
|
return ${array_type}(elements)
|
|
},
|
|
wrapValueIntoEquatable: identityEq,
|
|
extractValueFromEquatable: identityEq,
|
|
makeCollectionOfComparable: { (elements: [MinimalComparableValue]) in
|
|
return ${array_type}(elements)
|
|
},
|
|
wrapValueIntoComparable: identityComp,
|
|
extractValueFromComparable: identityComp,
|
|
resiliencyChecks: resiliencyChecks,
|
|
withUnsafeMutableBufferPointerIsSupported: true,
|
|
isFixedLengthCollection: false)
|
|
|
|
|
|
// Test MutableCollectionType conformance with reference type elements.
|
|
ArrayTestSuite.addMutableRandomAccessCollectionTests(
|
|
makeCollection: { (elements: [LifetimeTracked]) in
|
|
return ${array_type}(elements)
|
|
},
|
|
wrapValue: { (element: OpaqueValue<Int>) in
|
|
LifetimeTracked(element.value, identity: element.identity)
|
|
},
|
|
extractValue: { (element: LifetimeTracked) in
|
|
OpaqueValue(element.value, identity: element.identity)
|
|
},
|
|
makeCollectionOfEquatable: { (elements: [MinimalEquatableValue]) in
|
|
// FIXME: use LifetimeTracked.
|
|
return ${array_type}(elements)
|
|
},
|
|
wrapValueIntoEquatable: identityEq,
|
|
extractValueFromEquatable: identityEq,
|
|
makeCollectionOfComparable: { (elements: [MinimalComparableValue]) in
|
|
// FIXME: use LifetimeTracked.
|
|
return ${array_type}(elements)
|
|
},
|
|
wrapValueIntoComparable: identityComp,
|
|
extractValueFromComparable: identityComp,
|
|
resiliencyChecks: resiliencyChecks,
|
|
withUnsafeMutableBufferPointerIsSupported: true,
|
|
isFixedLengthCollection: false)
|
|
|
|
|
|
// Test RangeReplaceableCollectionType conformance with value type elements.
|
|
ArrayTestSuite.addRangeReplaceableRandomAccess${collection_or_slice}Tests(
|
|
makeCollection: { (elements: [OpaqueValue<Int>]) in
|
|
return ${array_type}(elements)
|
|
},
|
|
wrapValue: identity,
|
|
extractValue: identity,
|
|
makeCollectionOfEquatable: { (elements: [MinimalEquatableValue]) in
|
|
return ${array_type}(elements)
|
|
},
|
|
wrapValueIntoEquatable: identityEq,
|
|
extractValueFromEquatable: identityEq,
|
|
resiliencyChecks: resiliencyChecks)
|
|
|
|
|
|
// Test RangeReplaceableCollectionType conformance with reference type elements.
|
|
ArrayTestSuite.addRangeReplaceableRandomAccess${collection_or_slice}Tests(
|
|
makeCollection: { (elements: [LifetimeTracked]) in
|
|
return ${array_type}(elements)
|
|
},
|
|
wrapValue: { (element: OpaqueValue<Int>) in LifetimeTracked(element.value) },
|
|
extractValue: { (element: LifetimeTracked) in OpaqueValue(element.value) },
|
|
makeCollectionOfEquatable: { (elements: [MinimalEquatableValue]) in
|
|
// FIXME: use LifetimeTracked.
|
|
return ${array_type}(elements)
|
|
},
|
|
wrapValueIntoEquatable: identityEq,
|
|
extractValueFromEquatable: identityEq,
|
|
resiliencyChecks: resiliencyChecks)
|
|
}
|
|
|
|
% end
|
|
|
|
runAllTests()
|