//===----------------------------------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2025 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // RUN: %target-run-simple-swift( \ // RUN: -enable-experimental-feature ValueGenerics \ // RUN: -parse-as-library \ // RUN: ) // REQUIRES: executable_test // REQUIRES: swift_feature_ValueGenerics // UNSUPPORTED: use_os_stdlib // END. // //===----------------------------------------------------------------------===// import StdlibUnittest import Synchronization protocol P {} @available(SwiftStdlib 6.2, *) @main enum InlineArrayTests { @available(SwiftStdlib 6.2, *) static func main() { let testSuite = TestSuite("InlineArrayTests") testSuite.test("Empty", testEmpty) testSuite.test("Nonempty", testNonempty) testSuite.test("Copyable", testCopyable) testSuite.test("Noncopyable", testNoncopyable) testSuite.test("Uninhabited", testUninhabited) testSuite.test("Throws", testThrows) testSuite.test("Closures", testClosures) runAllTests() } /// An *empty* array's size is zero. Its stride and alignment are one byte. @available(SwiftStdlib 6.2, *) static func testEmpty() { expectEqual(MemoryLayout>.size, 0) expectEqual(MemoryLayout>.stride, 1) expectEqual(MemoryLayout>.alignment, 1) } /// A *nonempty* array's size and stride are equal to the element's stride /// multiplied by the number of elements. Its alignment is equal to the /// element's alignment. @available(SwiftStdlib 6.2, *) static func testNonempty() { // StaticString has { Word, Word, Byte } stored properties. let _ = MemoryLayout.size // e.g. 17 bytes. let s = MemoryLayout.stride // e.g. 24 bytes. let a = MemoryLayout.alignment // e.g. 8 bytes. expectEqual(MemoryLayout>.size, s * 1) expectEqual(MemoryLayout>.stride, s * 1) expectEqual(MemoryLayout>.alignment, a) expectEqual(MemoryLayout>.size, s * 2) expectEqual(MemoryLayout>.stride, s * 2) expectEqual(MemoryLayout>.alignment, a) } /// An array and its elements can be of *copyable* type. @available(SwiftStdlib 6.2, *) static func testCopyable() { do { let a: InlineArray<4, Int> = [1, 2, 4, 8] let b: InlineArray<_, Int> = [1, 2, 4, 8] let c: InlineArray<4, _> = [1, 2, 4, 8] let d: InlineArray = [1, 2, 4, 8] let e: InlineArray<0, Int> = [] let f: InlineArray<_, Int> = [] _checkInlineArray(a, oracle: [1, 2, 4, 8]) _checkInlineArray(b, oracle: [1, 2, 4, 8]) _checkInlineArray(c, oracle: [1, 2, 4, 8]) _checkInlineArray(d, oracle: [1, 2, 4, 8]) _checkInlineArray(e, oracle: []) _checkInlineArray(f, oracle: []) } do { let a = InlineArray<4, Int> { 1 << $0 } let b = InlineArray<4, Int>(first: 1) { $0 << 1 } var c = InlineArray<4, Int>(repeating: 9) var d = InlineArray<4, Int>(repeating: 9) let e = InlineArray<0, Int>(repeating: 9) _checkInlineArray(c, oracle: [9, 9, 9, 9]) _checkInlineArray(d, oracle: [9, 9, 9, 9]) c[0] = 1 c[1] = 2 c[2] = 4 c[3] = 8 d[unchecked: 0] = 1 d[unchecked: 1] = 2 d[unchecked: 2] = 4 d[unchecked: 3] = 8 _checkInlineArray(a, oracle: [1, 2, 4, 8]) _checkInlineArray(b, oracle: [1, 2, 4, 8]) _checkInlineArray(c, oracle: [1, 2, 4, 8]) _checkInlineArray(d, oracle: [1, 2, 4, 8]) _checkInlineArray(e, oracle: []) } } /// An array and its elements can be of *noncopyable* type. @available(SwiftStdlib 6.2, *) static func testNoncopyable() { var a = InlineArray<4, Atomic> { Atomic(1 << $0) } var b = InlineArray<4, Atomic>(first: Atomic(1)) { Atomic($0.load(ordering: .relaxed) << 1) } let c: InlineArray = [Atomic(8), Atomic(4), Atomic(2), Atomic(1)] expectEqual(a.indices, c.indices) expectEqual(b.indices, c.indices) // Perform in-place reversals to test the `swapAt(_:_:)` method. a._reverse() b._reverse() for i in c.indices { expectEqual( a[i].load(ordering: .relaxed), c[i].load(ordering: .relaxed) ) expectEqual( b[i].load(ordering: .relaxed), c[i].load(ordering: .relaxed) ) expectEqual( a[unchecked: i].load(ordering: .relaxed), c[unchecked: i].load(ordering: .relaxed) ) expectEqual( b[unchecked: i].load(ordering: .relaxed), c[unchecked: i].load(ordering: .relaxed) ) } } /// An *empty* array's elements can be of *uninhabited* type. @available(SwiftStdlib 6.2, *) static func testUninhabited() { do { let e: InlineArray<0, Never> = [] let f: InlineArray<_, Never> = [] _checkInlineArray(e, oracle: []) _checkInlineArray(f, oracle: []) } do { let e = InlineArray<0, Never> { _ in fatalError() } let f = InlineArray<0, _> { _ in fatalError() } _checkInlineArray(e, oracle: []) _checkInlineArray(f, oracle: []) } } /// The closure is allowed to throw an error at any point during /// initialization at which point the array will stop initialization, /// deinitialize every currently initialized element, and throw the given /// error back out to the caller. @available(SwiftStdlib 6.2, *) static func testThrows() { let error = CancellationError() do { expectDoesNotThrow { let a = try InlineArray<0, String>({ _ in throw error }) _checkInlineArray(a, oracle: []) } _expectThrows { let _ = try InlineArray<1, String>({ _ in throw error }) } _expectThrows { let _ = try InlineArray<1, any P>({ _ in throw error }) } _expectThrows { let _ = try InlineArray<2, String> { index in if index == 0 { "first" } else { throw error } } } } do { expectDoesNotThrow { let a = try InlineArray<0, String>(first: "first") { _ in throw error } _checkInlineArray(a, oracle: []) } expectDoesNotThrow { let a = try InlineArray<1, String>(first: "first") { _ in throw error } _checkInlineArray(a, oracle: ["first"]) } _expectThrows { let _ = try InlineArray<2, String>(first: "first") { _ in throw error } } _expectThrows { let _ = try InlineArray<3, String>(first: "first") { element in if element == "first" { "second" } else { throw error } } } } } /// Test the handling of closure values in InlineArray literals @available(SwiftStdlib 6.2, *) static func testClosures() { let ia: InlineArray<_, (Int) -> Int> = [{$0 * 2}] for i in 0 ..< 10 { expectEqual(i * 2, ia[0](i)) } var x = 0 let ia2: InlineArray<_, () -> ()> = [{ x += 1 }] for _ in 0 ..< 10 { ia2[0]() } expectEqual(x, 10) } } //===----------------------------------------------------------------------===// // MARK: - InlineArray Additions //===----------------------------------------------------------------------===// @available(SwiftStdlib 6.2, *) extension InlineArray where Element: ~Copyable { /// Reverses the elements of the array in place. /// /// - Complexity: O(*n*), where *n* is the number of elements in the array. @available(SwiftStdlib 6.2, *) mutating func _reverse() { if isEmpty { return } var i = startIndex var j = index(before: endIndex) while i < j { swapAt(i, j) i = index(after: i) j = index(before: j) } } } //===----------------------------------------------------------------------===// // MARK: - StdlibUnittest Additions //===----------------------------------------------------------------------===// /// Tests that the given closure always throws an error. func _expectThrows( stackTrace: SourceLocStack = SourceLocStack(), showFrame: Bool = true, file: String = #file, line: UInt = #line, _ body: () throws(Failure) -> Void ) { do throws(Failure) { try body() expectUnreachable( stackTrace: stackTrace.pushIf(showFrame, file: file, line: line) ) } catch { return } } /// Tests the properties and subscripts of an `InlineArray` instance, by /// comparing them against an `Array` oracle with the expected elements. @available(SwiftStdlib 6.2, *) func _checkInlineArray( _ inlineArray: InlineArray, oracle: [Element], stackTrace: SourceLocStack = SourceLocStack(), showFrame: Bool = true, file: String = #file, line: UInt = #line ) { // Check the properties. expectEqual( inlineArray.count, oracle.count, stackTrace: stackTrace.pushIf(showFrame, file: file, line: line) ) expectEqual( inlineArray.isEmpty, oracle.isEmpty, stackTrace: stackTrace.pushIf(showFrame, file: file, line: line) ) expectEqual( inlineArray.startIndex, oracle.startIndex, stackTrace: stackTrace.pushIf(showFrame, file: file, line: line) ) expectEqual( inlineArray.endIndex, oracle.endIndex, stackTrace: stackTrace.pushIf(showFrame, file: file, line: line) ) expectEqual( inlineArray.indices, oracle.indices, stackTrace: stackTrace.pushIf(showFrame, file: file, line: line) ) // Check the subscripts. for (i, j) in zip(inlineArray.indices, oracle.indices) { expectEqual( inlineArray[i], oracle[j], stackTrace: stackTrace.pushIf(showFrame, file: file, line: line) ) expectEqual( inlineArray[unchecked: i], oracle[j], stackTrace: stackTrace.pushIf(showFrame, file: file, line: line) ) } }