mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Implements SE-0055: https://github.com/apple/swift-evolution/blob/master/proposals/0055-optional-unsafe-pointers.md - Add NULL as an extra inhabitant of Builtin.RawPointer (currently hardcoded to 0 rather than being target-dependent). - Import non-object pointers as Optional/IUO when nullable/null_unspecified (like everything else). - Change the type checker's *-to-pointer conversions to handle a layer of optional. - Use 'AutoreleasingUnsafeMutablePointer<NSError?>?' as the type of error parameters exported to Objective-C. - Drop NilLiteralConvertible conformance for all pointer types. - Update the standard library and then all the tests. I've decided to leave this commit only updating existing tests; any new tests will come in the following commits. (That may mean some additional implementation work to follow.) The other major piece that's missing here is migration. I'm hoping we get a lot of that with Swift 1.1's work for optional object references, but I still need to investigate.
810 lines
23 KiB
Plaintext
810 lines
23 KiB
Plaintext
// RUN: rm -rf %t
|
|
// RUN: mkdir -p %t
|
|
// RUN: %S/../../utils/gyb %s -o %t/ArrayTraps.swift
|
|
// RUN: %S/../../utils/line-directive %t/ArrayTraps.swift -- %target-build-swift %t/ArrayTraps.swift -o %t/a.out
|
|
//
|
|
// RUN: %S/../../utils/line-directive %t/ArrayTraps.swift -- %target-run %t/a.out
|
|
// REQUIRES: executable_test
|
|
|
|
import StdlibUnittest
|
|
import StdlibCollectionUnittest
|
|
|
|
|
|
//===--- struct MrMcRange -------------------------------------------------===//
|
|
// A wrapper around Range<Tracked> that allows us to detect when it is
|
|
// being treated as a Collection rather than merely a Sequence, which
|
|
// helps us to prove that an optimization is being used. In
|
|
// particular, when constructing a _ContiguousArrayBuffer from a
|
|
// Collection, the necessary storage should be pre-allocated.
|
|
struct MrMcRange : Collection {
|
|
static var timesStartIndexWasCalled = ResettableValue(0)
|
|
|
|
typealias Base = Range<Int>
|
|
|
|
init(_ base: Base) {
|
|
self.base = base
|
|
}
|
|
|
|
var startIndex: Int {
|
|
MrMcRange.timesStartIndexWasCalled.value += 1
|
|
return base.startIndex
|
|
}
|
|
|
|
var endIndex: Int {
|
|
return base.endIndex
|
|
}
|
|
|
|
subscript(i: Int) -> LifetimeTracked {
|
|
return LifetimeTracked(i)
|
|
}
|
|
|
|
var base: Base
|
|
}
|
|
|
|
let CopyToNativeArrayBufferTests = TestSuite("CopyToNativeArrayBufferTests")
|
|
|
|
CopyToNativeArrayBufferTests.test("Sequence._copyToNativeArrayBuffer()") {
|
|
let sequence: MinimalSequence<LifetimeTracked> =
|
|
MinimalSequence(elements: LifetimeTracked(10)..<LifetimeTracked(27))
|
|
let buffer = sequence._copyToNativeArrayBuffer()
|
|
expectEqualSequence(
|
|
Array(10..<27),
|
|
buffer.map { $0.value })
|
|
}
|
|
|
|
CopyToNativeArrayBufferTests.test("Collection._copyToNativeArrayBuffer()") {
|
|
let buffer = MrMcRange(3..<23)._copyToNativeArrayBuffer()
|
|
// 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).
|
|
expectNotEqual(0, MrMcRange.timesStartIndexWasCalled.value)
|
|
expectEqualSequence(
|
|
Array(3..<23),
|
|
buffer.map { $0.value })
|
|
}
|
|
|
|
%{
|
|
all_array_types = ['ContiguousArray', 'ArraySlice', 'Array']
|
|
}%
|
|
|
|
extension Array {
|
|
var identity: UnsafePointer<Void> {
|
|
return self._buffer.identity
|
|
}
|
|
}
|
|
|
|
extension ArraySlice {
|
|
var identity: UnsafePointer<Void> {
|
|
return self._buffer.identity
|
|
}
|
|
}
|
|
|
|
extension ContiguousArray {
|
|
var identity: UnsafePointer<Void> {
|
|
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 EvilCollection : Collection {
|
|
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)
|
|
}
|
|
}
|
|
|
|
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)"
|
|
|
|
let t = ArrayTestSuite.test("\(testPrefix)/Infrastructure")
|
|
(evilBoundsCheck && _isDebugAssertConfiguration()
|
|
? t.crashOutputMatches(evilBoundsError) : t)
|
|
.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 = EvilCollection(step, boundsChecked: evilBoundsCheck)
|
|
|
|
if step < 0 {
|
|
expectCrashLater()
|
|
}
|
|
let a = AnySequence(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:
|
|
% 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.addRandomAccessMutableCollectionTests(
|
|
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.addRandomAccessMutableCollectionTests(
|
|
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.addRandomAccessRangeReplaceable${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.addRandomAccessRangeReplaceable${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()
|