diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt index 90986da95c369..cead6ecdd2dcb 100644 --- a/benchmark/CMakeLists.txt +++ b/benchmark/CMakeLists.txt @@ -39,7 +39,9 @@ set(SWIFT_BENCH_MODULES single-source/ArrayOfRef single-source/ArrayRemoveAll single-source/ArraySetElement + single-source/ArraySliceTests single-source/ArraySubscript + single-source/ArrayTests single-source/AsyncTree single-source/BinaryFloatingPointConversionFromBinaryInteger single-source/BinaryFloatingPointProperties @@ -65,6 +67,7 @@ set(SWIFT_BENCH_MODULES single-source/ClassArrayGetter single-source/CodableTest single-source/Combos + single-source/ContiguousArrayTests single-source/CountAlgo single-source/DataBenchmarks single-source/DeadArray diff --git a/benchmark/single-source/ArraySliceTests.swift b/benchmark/single-source/ArraySliceTests.swift new file mode 100644 index 0000000000000..f67a43eaaa20f --- /dev/null +++ b/benchmark/single-source/ArraySliceTests.swift @@ -0,0 +1,46 @@ +//===--- ArraySliceTests.swift --------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +import TestsUtils + +public let benchmarks = [ + BenchmarkInfo(name: "ArraySliceEqualUnique", runFunction: run_ArraySliceEqualUnique, tags: [.validation, .api, .Array]), + BenchmarkInfo(name: "ArraySliceEqualShared", runFunction: run_ArraySliceEqualShared, tags: [.validation, .api, .Array]), + BenchmarkInfo(name: "ArraySliceIdentical", runFunction: run_ArraySliceIdentical, tags: [.validation, .api, .Array]), +] + +@inline(never) +public func run_ArraySliceEqualUnique(_ n: Int) { + let a1 = ArraySlice(0 ..< n) + let a2 = ArraySlice(0 ..< n) + for _ in 0 ..< 100_000 { + check(a1 == a2) + } +} + +@inline(never) +public func run_ArraySliceEqualShared(_ n: Int) { + let a1 = ArraySlice(0 ..< n) + let a2 = a1 + for _ in 0 ..< 100_000 { + check(a1 == a2) + } +} + +@inline(never) +public func run_ArraySliceIdentical(_ n: Int) { + let a1 = ArraySlice(0 ..< n) + let a2 = a1 + for _ in 0 ..< 100_000 { + check(a1.isTriviallyIdentical(to: a2)) + } +} diff --git a/benchmark/single-source/ArrayTests.swift b/benchmark/single-source/ArrayTests.swift new file mode 100644 index 0000000000000..aec93680a973a --- /dev/null +++ b/benchmark/single-source/ArrayTests.swift @@ -0,0 +1,46 @@ +//===--- ArrayTests.swift -------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +import TestsUtils + +public let benchmarks = [ + BenchmarkInfo(name: "ArrayEqualUnique", runFunction: run_ArrayEqualUnique, tags: [.validation, .api, .Array]), + BenchmarkInfo(name: "ArrayEqualShared", runFunction: run_ArrayEqualShared, tags: [.validation, .api, .Array]), + BenchmarkInfo(name: "ArrayIdentical", runFunction: run_ArrayIdentical, tags: [.validation, .api, .Array]), +] + +@inline(never) +public func run_ArrayEqualUnique(_ n: Int) { + let a1 = Array(0 ..< n) + let a2 = Array(0 ..< n) + for _ in 0 ..< 100_000 { + check(a1 == a2) + } +} + +@inline(never) +public func run_ArrayEqualShared(_ n: Int) { + let a1 = Array(0 ..< n) + let a2 = a1 + for _ in 0 ..< 100_000 { + check(a1 == a2) + } +} + +@inline(never) +public func run_ArrayIdentical(_ n: Int) { + let a1 = Array(0 ..< n) + let a2 = a1 + for _ in 0 ..< 100_000 { + check(a1.isTriviallyIdentical(to: a2)) + } +} diff --git a/benchmark/single-source/ContiguousArrayTests.swift b/benchmark/single-source/ContiguousArrayTests.swift new file mode 100644 index 0000000000000..c4beb131d15f3 --- /dev/null +++ b/benchmark/single-source/ContiguousArrayTests.swift @@ -0,0 +1,46 @@ +//===--- ContiguousArrayTests.swift ---------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +import TestsUtils + +public let benchmarks = [ + BenchmarkInfo(name: "ContiguousArrayEqualUnique", runFunction: run_ContiguousArrayEqualUnique, tags: [.validation, .api, .Array]), + BenchmarkInfo(name: "ContiguousArrayEqualShared", runFunction: run_ContiguousArrayEqualShared, tags: [.validation, .api, .Array]), + BenchmarkInfo(name: "ContiguousArrayIdentical", runFunction: run_ContiguousArrayIdentical, tags: [.validation, .api, .Array]), +] + +@inline(never) +public func run_ContiguousArrayEqualUnique(_ n: Int) { + let a1 = ContiguousArray(0 ..< n) + let a2 = ContiguousArray(0 ..< n) + for _ in 0 ..< 100_000 { + check(a1 == a2) + } +} + +@inline(never) +public func run_ContiguousArrayEqualShared(_ n: Int) { + let a1 = ContiguousArray(0 ..< n) + let a2 = a1 + for _ in 0 ..< 100_000 { + check(a1 == a2) + } +} + +@inline(never) +public func run_ContiguousArrayIdentical(_ n: Int) { + let a1 = ContiguousArray(0 ..< n) + let a2 = a1 + for _ in 0 ..< 100_000 { + check(a1.isTriviallyIdentical(to: a2)) + } +} diff --git a/benchmark/utils/main.swift b/benchmark/utils/main.swift index 0fcb72ba1b6a7..3f62a8763d4ee 100644 --- a/benchmark/utils/main.swift +++ b/benchmark/utils/main.swift @@ -27,7 +27,9 @@ import ArrayOfPOD import ArrayOfRef import ArrayRemoveAll import ArraySetElement +import ArraySliceTests import ArraySubscript +import ArrayTests import AsyncTree import BinaryFloatingPointConversionFromBinaryInteger import BinaryFloatingPointProperties @@ -53,6 +55,7 @@ import Chars import ClassArrayGetter import CodableTest import Combos +import ContiguousArrayTests import CountAlgo import CreateObjects // rdar://128520766 @@ -227,7 +230,9 @@ register(ArrayOfPOD.benchmarks) register(ArrayOfRef.benchmarks) register(ArrayRemoveAll.benchmarks) register(ArraySetElement.benchmarks) +register(ArraySliceTests.benchmarks) register(ArraySubscript.benchmarks) +register(ArrayTests.benchmarks) register(AsyncTree.benchmarks) register(BinaryFloatingPointConversionFromBinaryInteger.benchmarks) register(BinaryFloatingPointProperties.benchmarks) @@ -252,6 +257,7 @@ register(CharacterRecognizer.benchmarks) register(Chars.benchmarks) register(CodableTest.benchmarks) register(Combos.benchmarks) +register(ContiguousArrayTests.benchmarks) register(CountAlgo.benchmarks) register(ClassArrayGetter.benchmarks) register(CreateObjects.benchmarks) diff --git a/stdlib/public/core/Array.swift b/stdlib/public/core/Array.swift index a590a5ba2782c..22af0ca4afdff 100644 --- a/stdlib/public/core/Array.swift +++ b/stdlib/public/core/Array.swift @@ -2160,3 +2160,44 @@ internal struct _ArrayAnyHashableBox } extension Array: @unchecked Sendable where Element: Sendable { } + +extension Array { + /// Returns a boolean value indicating whether this array is identical to + /// `other`. + /// + /// Two array values are identical if there is no way to distinguish between + /// them. + /// + /// For any values `a`, `b`, and `c`: + /// + /// - `a.isTriviallyIdentical(to: a)` is always `true`. (Reflexivity) + /// - `a.isTriviallyIdentical(to: b)` implies `b.isTriviallyIdentical(to: a)`. + /// (Symmetry) + /// - If `a.isTriviallyIdentical(to: b)` and `b.isTriviallyIdentical(to: c)` + /// are both `true`, then `a.isTriviallyIdentical(to: c)` is also `true`. + /// (Transitivity) + /// - If `a` and `b` are `Equatable`, then `a.isTriviallyIdentical(b)` implies + /// `a == b` + /// - `a == b` does not imply `a.isTriviallyIdentical(b)` + /// + /// Values produced by copying the same value, with no intervening mutations, + /// will compare identical: + /// + /// ```swift + /// let d = c + /// print(c.isTriviallyIdentical(to: d)) + /// // Prints true + /// ``` + /// + /// Comparing arrays this way includes comparing (normally) hidden + /// implementation details such as the memory location of any underlying + /// array storage object. Therefore, identical arrays are guaranteed to + /// compare equal with `==`, but not all equal arrays are considered + /// identical. + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + public func isTriviallyIdentical(to other: Self) -> Bool { + unsafe self._buffer.identity == other._buffer.identity + } +} diff --git a/stdlib/public/core/ArraySlice.swift b/stdlib/public/core/ArraySlice.swift index 5a054b0185a66..1a85d4fa3df90 100644 --- a/stdlib/public/core/ArraySlice.swift +++ b/stdlib/public/core/ArraySlice.swift @@ -1607,3 +1607,44 @@ extension ArraySlice { } } #endif + +extension ArraySlice { + /// Returns a boolean value indicating whether this array is identical to + /// `other`. + /// + /// Two array values are identical if there is no way to distinguish between + /// them. + /// + /// For any values `a`, `b`, and `c`: + /// + /// - `a.isTriviallyIdentical(to: a)` is always `true`. (Reflexivity) + /// - `a.isTriviallyIdentical(to: b)` implies `b.isTriviallyIdentical(to: a)`. + /// (Symmetry) + /// - If `a.isTriviallyIdentical(to: b)` and `b.isTriviallyIdentical(to: c)` + /// are both `true`, then `a.isTriviallyIdentical(to: c)` is also `true`. + /// (Transitivity) + /// - If `a` and `b` are `Equatable`, then `a.isTriviallyIdentical(b)` implies + /// `a == b` + /// - `a == b` does not imply `a.isTriviallyIdentical(b)` + /// + /// Values produced by copying the same value, with no intervening mutations, + /// will compare identical: + /// + /// ```swift + /// let d = c + /// print(c.isTriviallyIdentical(to: d)) + /// // Prints true + /// ``` + /// + /// Comparing arrays this way includes comparing (normally) hidden + /// implementation details such as the memory location of any underlying + /// array storage object. Therefore, identical arrays are guaranteed to + /// compare equal with `==`, but not all equal arrays are considered + /// identical. + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + public func isTriviallyIdentical(to other: Self) -> Bool { + self._buffer.isTriviallyIdentical(to: other._buffer) + } +} diff --git a/stdlib/public/core/ContiguousArray.swift b/stdlib/public/core/ContiguousArray.swift index a5f66fe850f81..55cadb0bceac7 100644 --- a/stdlib/public/core/ContiguousArray.swift +++ b/stdlib/public/core/ContiguousArray.swift @@ -1515,3 +1515,44 @@ extension ContiguousArray { extension ContiguousArray: @unchecked Sendable where Element: Sendable { } + +extension ContiguousArray { + /// Returns a boolean value indicating whether this array is identical to + /// `other`. + /// + /// Two array values are identical if there is no way to distinguish between + /// them. + /// + /// For any values `a`, `b`, and `c`: + /// + /// - `a.isTriviallyIdentical(to: a)` is always `true`. (Reflexivity) + /// - `a.isTriviallyIdentical(to: b)` implies `b.isTriviallyIdentical(to: a)`. + /// (Symmetry) + /// - If `a.isTriviallyIdentical(to: b)` and `b.isTriviallyIdentical(to: c)` + /// are both `true`, then `a.isTriviallyIdentical(to: c)` is also `true`. + /// (Transitivity) + /// - If `a` and `b` are `Equatable`, then `a.isTriviallyIdentical(b)` implies + /// `a == b` + /// - `a == b` does not imply `a.isTriviallyIdentical(b)` + /// + /// Values produced by copying the same value, with no intervening mutations, + /// will compare identical: + /// + /// ```swift + /// let d = c + /// print(c.isTriviallyIdentical(to: d)) + /// // Prints true + /// ``` + /// + /// Comparing arrays this way includes comparing (normally) hidden + /// implementation details such as the memory location of any underlying + /// array storage object. Therefore, identical arrays are guaranteed to + /// compare equal with `==`, but not all equal arrays are considered + /// identical. + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + public func isTriviallyIdentical(to other: Self) -> Bool { + unsafe self._buffer.identity == other._buffer.identity + } +} diff --git a/stdlib/public/core/SliceBuffer.swift b/stdlib/public/core/SliceBuffer.swift index ba4956fdbab0d..0a89f0fae5b2b 100644 --- a/stdlib/public/core/SliceBuffer.swift +++ b/stdlib/public/core/SliceBuffer.swift @@ -511,3 +511,29 @@ extension _SliceBuffer { return ContiguousArray(_buffer: result) } } + +extension _SliceBuffer { + @_alwaysEmitIntoClient + internal func isTriviallyIdentical(to other: Self) -> Bool { +#if $Embedded + if + self.owner == other.owner, + unsafe (self.subscriptBaseAddress == other.subscriptBaseAddress), + self.startIndex == other.startIndex, + self.endIndexAndFlags == other.endIndexAndFlags + { + return true + } +#else + if + self.owner === other.owner, + unsafe (self.subscriptBaseAddress == other.subscriptBaseAddress), + self.startIndex == other.startIndex, + self.endIndexAndFlags == other.endIndexAndFlags + { + return true + } +#endif + return false + } +} diff --git a/test/stdlib/Inputs/CommonArrayTests.gyb b/test/stdlib/Inputs/CommonArrayTests.gyb index 9ccdaaf2f1b43..15ba84fa0c0b2 100644 --- a/test/stdlib/Inputs/CommonArrayTests.gyb +++ b/test/stdlib/Inputs/CommonArrayTests.gyb @@ -696,3 +696,27 @@ ${Suite}.test("${ArrayType}/AssociatedTypes") { indexType: Int.self, indicesType: CountableRange.self) } + +//===----------------------------------------------------------------------===// +// Trivially Identical +//===----------------------------------------------------------------------===// + +${Suite}.test("${ArrayType}/isTriviallyIdentical") { + let a1: ${ArrayType} = [ 10, 20, 30, 40 ] + expectTrue(a1.isTriviallyIdentical(to: a1)) + + let a2: ${ArrayType} = a1 + expectTrue(a1.isTriviallyIdentical(to: a2)) + + var a3: ${ArrayType} = a2 + a3.reserveCapacity(0) + expectFalse(a1.isTriviallyIdentical(to: a3)) + + let a4: ${ArrayType} = [ 10, 20, 30, 40 ] + expectFalse(a1.isTriviallyIdentical(to: a4)) + + let a5: ArraySlice = a1[...] + let a6: ArraySlice = a1[1..<3] + + expectFalse(a5.isTriviallyIdentical(to: a6)) +}