Files
swift-mirror/validation-test/stdlib/NewArray.swift.gyb
Andrew Trick 0b75ee975e Remove "illegal" UnsafePointer casts from the stdlib.
Update for SE-0107: UnsafeRawPointer

This adds a "mutating" initialize to UnsafePointer to make
Immutable -> Mutable conversions explicit.

These are quick fixes to stdlib, overlays, and test cases that are necessary
in order to remove arbitrary UnsafePointer conversions.

Many cases can be expressed better up by reworking the surrounding
code, but we first need a working starting point.
2016-07-28 20:42:23 -07:00

493 lines
14 KiB
Plaintext

%# -*- mode: swift -*-
//===--- NewArray.swift.gyb -----------------------------------*- swift -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
// RUN-DISABLED: %target-run-simple-swift | FileCheck %s
// RUN: rm -rf %t && mkdir -p %t && %gyb %s -o %t/NewArray.swift
// RUN: %line-directive %t/NewArray.swift -- %target-build-swift %t/NewArray.swift -o %t/a.out -Xfrontend -disable-access-control
// RUN: %target-run %t/a.out 2>&1 | %line-directive %t/NewArray.swift -- FileCheck %t/NewArray.swift --check-prefix=CHECK --check-prefix=CHECK-%target-runtime
// REQUIRES: executable_test
import StdlibUnittest
import StdlibCollectionUnittest
//===----------------------------------------------------------------------===//
func printSequence<T : Sequence>(_ x: T) {
print("[", terminator: "")
var prefix = ""
for a in x {
print(prefix, terminator: "")
print(a, terminator: "")
prefix = ", "
}
print("]")
}
typealias BufferID = UnsafeRawPointer?
func bufferID<T : _ArrayProtocol>(_ x: T) -> BufferID {
return x._buffer.identity
}
func checkReallocation<T : _ArrayProtocol>(
_ x: T, _ lastBuffer: BufferID, _ reallocationExpected: Bool
) -> BufferID {
let currentBuffer = bufferID(x)
if (currentBuffer != lastBuffer) != reallocationExpected {
let message = reallocationExpected ? "lack of" : ""
print("unexpected \(message) reallocation")
}
return currentBuffer
}
func checkEqual<
S1 : Sequence, S2 : Sequence
where
S1.Iterator.Element == S2.Iterator.Element,
S1.Iterator.Element : Equatable
>(_ a1: S1, _ a2: S2, _ expected: Bool) {
if a1.elementsEqual(a2) != expected {
let un = expected ? "un" : ""
print("unexpectedly \(un)equal sequences!")
}
}
func test<
T : _ArrayProtocol
where
T.Iterator.Element == T._Buffer.Element,
T._Buffer.Element == T.Element,
T.Element == LifetimeTracked,
T.Index == Int,
T : RandomAccessCollection
>(_: T.Type, _ label: String) {
print("test: \(label)...", terminator: "")
var x: T = [
LifetimeTracked(1),
LifetimeTracked(2),
LifetimeTracked(3),
LifetimeTracked(4),
LifetimeTracked(5)
]
checkEqual(x, LifetimeTracked(1)...LifetimeTracked(5), true)
x.reserveCapacity(x.count + 2)
checkEqual(x, LifetimeTracked(1)...LifetimeTracked(5), true)
let bufferId0 = bufferID(x)
// Append a range of integers
x += LifetimeTracked(0)..<LifetimeTracked(2)
let bufferId1 = checkReallocation(x, bufferId0, false)
for i in x.count..<(x.capacity + 1) {
let bufferId1a = checkReallocation(x, bufferId1, false)
x.append(LifetimeTracked(13))
}
let bufferId2 = checkReallocation(x, bufferId1, true)
let y = x
x[x.index(before: x.endIndex)] = LifetimeTracked(17)
let bufferId3 = checkReallocation(x, bufferId2, true)
checkEqual(x, y, false)
func checkReallocations(
_ a: T, _ growthDescription: String, _ growBy1: (_: inout T) -> ()
) {
var a = a
var reallocations = 0
// Note: right now this test is dependent on a growth factor of 2.
// It's possible that factor will change, but (cursory) testing
// has shown that using 1.5, the other popular growth factor,
// slows things down.
for _ in a.count..<(a.capacity * 4) {
let oldId = bufferID(a)
growBy1(&a)
if oldId != bufferID(a) {
reallocations += 1
}
}
if reallocations > 3 {
print(
"Unexpectedly found \(reallocations) reallocations "
+ "of \(label) when growing via \(growthDescription)")
}
}
checkReallocations(x, "append") {
(x: inout T) -> () in x.append(LifetimeTracked(42))
}
checkReallocations(x, "+=") {
(x: inout T) -> () in x.append(LifetimeTracked(42))
}
print("done.")
}
print("testing...")
// CHECK: testing...
test(ContiguousArray<LifetimeTracked>.self, "ContiguousArray")
// CHECK-NEXT: test: ContiguousArray...done
test(Array<LifetimeTracked>.self, "Array")
// CHECK-NEXT: test: Array...done
test(ArraySlice<LifetimeTracked>.self, "ArraySlice")
// CHECK-NEXT: test: ArraySlice...done
func testAsArray() {
print("== AsArray ==")
var w: ContiguousArray<LifetimeTracked>
= [LifetimeTracked(4), LifetimeTracked(2), LifetimeTracked(1)]
// CHECK: == AsArray ==
let x = ContiguousArray(w)
print(bufferID(w) == bufferID(x))
// CHECK-NEXT: true
let y = Array(x)
print(bufferID(x) == bufferID(y))
// CHECK-NEXT: true
// Because of their indirection, arrays of classes can share
// buffers.
let y1 = Array(y)
print(bufferID(y1) == bufferID(y))
// CHECK-NEXT: true
let z = ArraySlice(y)
print(bufferID(y) == bufferID(z))
// CHECK-NEXT: true
w = ContiguousArray(z)
print(bufferID(w) == bufferID(z))
// CHECK-NEXT: true
let zz = y[0..<2]
print(bufferID(zz))
// CHECK-NEXT: 0x
}
testAsArray()
#if _runtime(_ObjC)
import Foundation
func nsArrayOfStrings() -> Array<NSString> {
let src: ContiguousArray<NSString> = ["foo", "bar", "baz"]
return src.withUnsafeBufferPointer {
let ns = NSArray(
objects: unsafeBitCast($0.baseAddress!,
to: UnsafeMutablePointer<AnyObject>.self),
count: $0.count)
return ns as! [NSString]
}
}
func testCocoa() {
print("== Cocoa ==")
// CHECK-objc: == Cocoa ==
var a = nsArrayOfStrings()
printSequence(a)
// CHECK-objc-NEXT: [foo, bar, baz]
a.append("qux")
printSequence(a)
// CHECK-objc-NEXT: [foo, bar, baz, qux]
a = nsArrayOfStrings()
printSequence(a)
// CHECK-objc-NEXT: [foo, bar, baz]
let b = a
a[1] = "garply"
printSequence(a)
// CHECK-objc-NEXT: [foo, garply, baz]
// Mutating an element in a has no effect on b
printSequence(b)
// CHECK-objc-NEXT: [foo, bar, baz]
a = nsArrayOfStrings()
a.insert("bag", at: 2)
printSequence(a)
// CHECK-objc-NEXT: [foo, bar, bag, baz]
a = nsArrayOfStrings()
a.reserveCapacity(30)
printSequence(a)
// CHECK-objc-NEXT: [foo, bar, baz]
print(a.capacity >= 30)
// CHECK-objc-NEXT: true
// Prove that we create contiguous storage for an opaque NSArray
a.withUnsafeBufferPointer {
(p) -> () in
print(p[0])
// CHECK-objc-NEXT: foo
}
}
testCocoa()
#endif // _runtime(_ObjC)
extension ArraySlice {
mutating func qsort(_ compare: (Element, Element) -> Bool) {
_introSort(&self, subRange: Range(self.indices), by: compare)
}
}
func testSlice() {
print("== ArraySlice ==")
// CHECK: == ArraySlice ==
// do some tests on the shared semantics
var b = ContiguousArray(LifetimeTracked(0)..<LifetimeTracked(10))
// ArraySlice it
var bSlice = b[3..<8]
print("<\(bSlice.count)>")
// CHECK-NEXT: <5>
print("bSlice0: \(bSlice)") // CHECK-NEXT: bSlice0: [3, 4, 5, 6, 7]
// bSlice += LifetimeTracked(11)..<LifetimeTracked(13)
// Writing into b does not change bSlice
b[4] = LifetimeTracked(41)
print("bSlice1: \(bSlice)") // CHECK-NEXT: bSlice1: [3, 4, 5, 6, 7]
// Writing into bSlice does not change b
bSlice[3] = LifetimeTracked(32)
print("bSlice2: \(bSlice)") // CHECK-NEXT: bSlice2: [32, 4, 5, 6, 7]
printSequence(b) // CHECK-NEXT: [0, 1, 2, 3, 41, 5, 6, 7, 8, 9]
let c = b
b[b.startIndex..<b.endIndex].qsort(<)
printSequence(b) // CHECK-NEXT: [0, 1, 2, 3, 5, 6, 7, 8, 9, 41]
printSequence(c) // CHECK-NEXT: [0, 1, 2, 3, 41, 5, 6, 7, 8, 9]
#if _runtime(_ObjC)
// Now a bridged slice
var a = Array<NSString>(
_ArrayBuffer(nsArray: nsArrayOfStrings()._buffer._asCocoaArray()))
printSequence(a) // CHECK-objc-NEXT: [foo, bar, baz]
var aSlice = a[1..<3] // CHECK-objc-NEXT: [bar, baz]
printSequence(aSlice)
// Writing into aSlice works
aSlice[1] = "buzz" // CHECK-objc-NEXT: [buzz, baz]
printSequence(aSlice)
// ...and doesn't affect a
printSequence(a) // CHECK-objc-NEXT: [foo, bar, baz]
// Appending to aSlice works...
aSlice.append("fodder")
print("<\(aSlice.count)>") // CHECK-objc-NEXT: <3>
printSequence(aSlice) // CHECK-objc-NEXT: [buzz, baz, fodder]
// And doesn't change a
printSequence(a) // CHECK-objc-NEXT: [foo, bar, baz]
#endif
}
testSlice()
//===--- sub-range replacement --------------------------------------------===//
// Size of the array on which we're going to test "replace."
// testing time grows roughly as the cube of this constant
let testWidth = 11
%arrayTypes = ['ContiguousArray', 'Array', 'ArraySlice']
%for A in arrayTypes:
func testReplace(_ make: () -> ${A}<LifetimeTracked>) {
checkRangeReplaceable(
make, { LifetimeTracked(100)..<LifetimeTracked(100 + $0) })
}
func testReplace${A}(
makeOne: () -> ${A}<LifetimeTracked> = {
var x = ${A}<LifetimeTracked>()
// make sure some - but not all - replacements will have to grow the buffer
x.reserveCapacity(testWidth * 3 / 2)
x += LifetimeTracked(0)..<LifetimeTracked(testWidth)
return x
}
) {
testReplace(makeOne)
// Create one that will not be uniquely-referenced so we can test
// the out-of-place code paths.
let r = makeOne()
testReplace({ r })
// This test should ensure r's retain isn't dropped before we start testing.
if (r.count != testWidth) {
print("something bad happened!")
}
}
print("testing subrange replacement in ${A}")
testReplace${A}()
%end
// Also test with a sub-slice of some larger buffer. The "trailing"
// case is interesting because when the buffer is uniquely referenced
// we can expect to expand the slice in-place
for (maxValue, label) in [(testWidth, "trailing"), (testWidth*2, "interior")] {
print("testing subrange replacement in \(label) Sub-ArraySlice")
testReplaceArraySlice {
var a = ContiguousArray(LifetimeTracked(-testWidth)..<LifetimeTracked(maxValue))
a.reserveCapacity(a.count * 3 / 2)
return a[testWidth..<(2 * testWidth)]
}
}
// CHECK-NEXT: testing subrange replacement in ContiguousArray
// CHECK-NEXT: testing subrange replacement in Array
// CHECK-NEXT: testing subrange replacement in ArraySlice
// CHECK-NEXT: testing subrange replacement in trailing Sub-ArraySlice
// CHECK-NEXT: testing subrange replacement in interior Sub-ArraySlice
//===--- inout violations -------------------------------------------------===//
// The user has to obey certain rules when things are passed via
// inout, but in those cases we only guarantee memory-safety, not
// coherent semantics. Let's try to force a memory-safety problem
// here. This crashes when withUnsafeMutableBufferPointer is not
// sufficiently careful.
func testInoutViolation() {
var a: [LifetimeTracked] = [10, 8, 6, 4, 2, 0, 9, 7, 5, 3, 1].map { LifetimeTracked($0) }
%for A in arrayTypes:
do {
var b = ${A}(a)
_ = b.sorted { x, y in
b.removeAll()
return x < y
}
}
%end
// An overload of sorted for Arrays uses withUnsafeMutableBufferPointer,
// which disables bounds checks.
_ = a.sorted { x, y in
a = [] // Invalidate the whole array during sorting
return x < y
}
}
testInoutViolation()
//===--- single-element modifiers -----------------------------------------===//
%for A in arrayTypes:
func testSingleElementModifiers${A}() {
print("testing ${A} single-argument modifiers")
// CHECK-NEXT: testing ${A} single-argument modifiers
var a = ${A}(LifetimeTracked(0)..<LifetimeTracked(10))
print(a.removeLast().value) // CHECK-NEXT: 9
printSequence(a) // CHECK-NEXT: [0, 1, 2, 3, 4, 5, 6, 7, 8]
a.insert(LifetimeTracked(42), at: 4)
printSequence(a) // CHECK-NEXT: [0, 1, 2, 3, 42, 4, 5, 6, 7, 8]
print(a.remove(at: 2).value) // CHECK-NEXT: 2
printSequence(a) // CHECK-NEXT: [0, 1, 3, 42, 4, 5, 6, 7, 8]
}
testSingleElementModifiers${A}()
%end
//===--- isEmpty, first, last ---------------------------------------------===//
%for A in arrayTypes:
func testIsEmptyFirstLast${A}() {
print("testing ${A} isEmpty, first, and last")
// CHECK-NEXT: testing ${A} isEmpty, first, and last
print(${A}<Int>().isEmpty) // CHECK-NEXT: true
print(${A}(42...42).isEmpty) // CHECK-NEXT: false
print("<\(${A}(3..<43).first!)>") // CHECK-NEXT: <3>
print("<\(${A}(3..<43).last!)>") // CHECK-NEXT: <42>
print("<\(${A}<Int>().first)>") // CHECK-NEXT: nil
print("<\(${A}<Int>().last)>") // CHECK-NEXT: nil
var a = ${A}(LifetimeTracked(0)..<LifetimeTracked(10))
print(a.removeLast().value) // CHECK-NEXT: 9
printSequence(a) // CHECK-NEXT: [0, 1, 2, 3, 4, 5, 6, 7, 8]
a.insert(LifetimeTracked(42), at: 4)
printSequence(a) // CHECK-NEXT: [0, 1, 2, 3, 42, 4, 5, 6, 7, 8]
print(a.remove(at: 2).value) // CHECK-NEXT: 2
printSequence(a) // CHECK-NEXT: [0, 1, 3, 42, 4, 5, 6, 7, 8]
}
testIsEmptyFirstLast${A}()
%end
//===--- Regression Tests -------------------------------------------------===//
func rdar16958865() {
var a: [Int] = []
a += AnySequence([ 42, 4242 ])
// CHECK-NEXT: [42, 4242]
print(a)
}
rdar16958865()
#if _runtime(_ObjC)
#if os(OSX)
import AppKit
typealias OSColor = NSColor
#else
import UIKit
typealias OSColor = UIColor
#endif
class Rdar16914909 : NSObject {
var basicColorSet = [OSColor]()
func doColorStuff() {
basicColorSet.append(OSColor.lightGray())
print("appended")
}
}
Rdar16914909().doColorStuff()
// CHECK-objc-NEXT: appended
#endif
print("leaks = \(LifetimeTracked.instances)")
// CHECK-NEXT: leaks = 0
// CHECK-NEXT: all done.
print("all done.")
// ${'Local Variables'}:
// eval: (read-only-mode 1)
// End: