mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[stdlib] API additions for basic noncopyable primitives
- Add `exchange(_:with:)` - Add `Optional.take()` - Add `Unsafe[Mutable]BufferPointer.extracting(_:)` - Finish generalizing `withExtendedLifetime` - Radically simplify the implementation of `swap(_:_:)`
This commit is contained in:
@@ -20,10 +20,10 @@
|
|||||||
/// return value for the `withExtendedLifetime(_:_:)` method.
|
/// return value for the `withExtendedLifetime(_:_:)` method.
|
||||||
/// - Returns: The return value, if any, of the `body` closure parameter.
|
/// - Returns: The return value, if any, of the `body` closure parameter.
|
||||||
@_alwaysEmitIntoClient
|
@_alwaysEmitIntoClient
|
||||||
public func withExtendedLifetime<T: ~Copyable, Result: ~Copyable>(
|
public func withExtendedLifetime<T: ~Copyable, E: Error, Result: ~Copyable>(
|
||||||
_ x: borrowing T,
|
_ x: borrowing T,
|
||||||
_ body: () throws -> Result // FIXME: Typed throws rdar://126576356
|
_ body: () throws(E) -> Result
|
||||||
) rethrows -> Result {
|
) throws(E) -> Result {
|
||||||
defer { _fixLifetime(x) }
|
defer { _fixLifetime(x) }
|
||||||
return try body()
|
return try body()
|
||||||
}
|
}
|
||||||
@@ -32,8 +32,7 @@ public func withExtendedLifetime<T: ~Copyable, Result: ~Copyable>(
|
|||||||
@_silgen_name("$ss20withExtendedLifetimeyq_x_q_yKXEtKr0_lF")
|
@_silgen_name("$ss20withExtendedLifetimeyq_x_q_yKXEtKr0_lF")
|
||||||
@usableFromInline
|
@usableFromInline
|
||||||
internal func __abi_withExtendedLifetime<T, Result>(
|
internal func __abi_withExtendedLifetime<T, Result>(
|
||||||
_ x: T,
|
_ x: T, _ body: () throws -> Result
|
||||||
_ body: () throws -> Result // FIXME: Typed throws rdar://126576356
|
|
||||||
) rethrows -> Result {
|
) rethrows -> Result {
|
||||||
defer { _fixLifetime(x) }
|
defer { _fixLifetime(x) }
|
||||||
return try body()
|
return try body()
|
||||||
@@ -49,9 +48,10 @@ internal func __abi_withExtendedLifetime<T, Result>(
|
|||||||
/// return value for the `withExtendedLifetime(_:_:)` method.
|
/// return value for the `withExtendedLifetime(_:_:)` method.
|
||||||
/// - Returns: The return value, if any, of the `body` closure parameter.
|
/// - Returns: The return value, if any, of the `body` closure parameter.
|
||||||
@_alwaysEmitIntoClient
|
@_alwaysEmitIntoClient
|
||||||
public func withExtendedLifetime<T, Result: ~Copyable>(
|
public func withExtendedLifetime<T: ~Copyable, E: Error, Result: ~Copyable>(
|
||||||
_ x: T, _ body: (T) throws -> Result // FIXME: Typed throws rdar://126576356
|
_ x: borrowing T,
|
||||||
) rethrows -> Result {
|
_ body: (borrowing T) throws(E) -> Result
|
||||||
|
) throws(E) -> Result {
|
||||||
defer { _fixLifetime(x) }
|
defer { _fixLifetime(x) }
|
||||||
return try body(x)
|
return try body(x)
|
||||||
}
|
}
|
||||||
@@ -60,7 +60,7 @@ public func withExtendedLifetime<T, Result: ~Copyable>(
|
|||||||
@_silgen_name("$ss20withExtendedLifetimeyq_x_q_xKXEtKr0_lF")
|
@_silgen_name("$ss20withExtendedLifetimeyq_x_q_xKXEtKr0_lF")
|
||||||
@usableFromInline
|
@usableFromInline
|
||||||
internal func __abi_withExtendedLifetime<T, Result>(
|
internal func __abi_withExtendedLifetime<T, Result>(
|
||||||
_ x: T, _ body: (T) throws -> Result // FIXME: Typed throws rdar://126576356
|
_ x: T, _ body: (T) throws -> Result
|
||||||
) rethrows -> Result {
|
) rethrows -> Result {
|
||||||
defer { _fixLifetime(x) }
|
defer { _fixLifetime(x) }
|
||||||
return try body(x)
|
return try body(x)
|
||||||
|
|||||||
@@ -529,24 +529,24 @@ extension MutableCollection {
|
|||||||
@inlinable
|
@inlinable
|
||||||
@_preInverseGenerics
|
@_preInverseGenerics
|
||||||
public func swap<T: ~Copyable>(_ a: inout T, _ b: inout T) {
|
public func swap<T: ~Copyable>(_ a: inout T, _ b: inout T) {
|
||||||
// Semantically equivalent to (a, b) = (b, a).
|
let temp = consume a
|
||||||
// Microoptimized to avoid retain/release traffic.
|
a = consume b
|
||||||
#if $BuiltinUnprotectedAddressOf
|
b = consume temp
|
||||||
let p1 = Builtin.unprotectedAddressOf(&a)
|
|
||||||
let p2 = Builtin.unprotectedAddressOf(&b)
|
|
||||||
#else
|
|
||||||
let p1 = Builtin.addressof(&a)
|
|
||||||
let p2 = Builtin.addressof(&b)
|
|
||||||
#endif
|
|
||||||
_debugPrecondition(
|
|
||||||
p1 != p2,
|
|
||||||
"swapping a location with itself is not supported")
|
|
||||||
|
|
||||||
// Take from P1.
|
|
||||||
let tmp: T = Builtin.take(p1)
|
|
||||||
// Transfer P2 into P1.
|
|
||||||
Builtin.initialize(Builtin.take(p2) as T, p1)
|
|
||||||
// Initialize P2.
|
|
||||||
Builtin.initialize(tmp, p2)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Replaces the value of a mutable value with the supplied new value,
|
||||||
|
/// returning the original.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - item: A mutable binding.
|
||||||
|
/// - newValue: The new value of `item`.
|
||||||
|
/// - Returns: The original value of `item`.
|
||||||
|
@_alwaysEmitIntoClient
|
||||||
|
public func exchange<T: ~Copyable>(
|
||||||
|
_ item: inout T,
|
||||||
|
with newValue: consuming T
|
||||||
|
) -> T {
|
||||||
|
let oldValue = consume item
|
||||||
|
item = consume newValue
|
||||||
|
return oldValue
|
||||||
|
}
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ extension Unicode {
|
|||||||
|
|
||||||
// If we have a leftover composee, make sure to return it.
|
// If we have a leftover composee, make sure to return it.
|
||||||
// We may still have things in the buffer which are not complete segments.
|
// We may still have things in the buffer which are not complete segments.
|
||||||
return composee._take() ?? buffer.next()?.scalar
|
return composee.take() ?? buffer.next()?.scalar
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -279,7 +279,7 @@ extension Unicode._NFDNormalizer {
|
|||||||
// The buffer contains the decomposed segment *prior to*
|
// The buffer contains the decomposed segment *prior to*
|
||||||
// any pending starter we might have.
|
// any pending starter we might have.
|
||||||
|
|
||||||
return buffer.next() ?? pendingStarter._take()
|
return buffer.next() ?? pendingStarter.take()
|
||||||
}
|
}
|
||||||
|
|
||||||
@inline(__always)
|
@inline(__always)
|
||||||
@@ -287,7 +287,7 @@ extension Unicode._NFDNormalizer {
|
|||||||
_ nextFromSource: () -> Unicode.Scalar?
|
_ nextFromSource: () -> Unicode.Scalar?
|
||||||
) -> ScalarAndNormData? {
|
) -> ScalarAndNormData? {
|
||||||
|
|
||||||
if let pendingStarter = pendingStarter._take() {
|
if let pendingStarter = pendingStarter.take() {
|
||||||
return pendingStarter
|
return pendingStarter
|
||||||
} else if let nextScalar = nextFromSource() {
|
} else if let nextScalar = nextFromSource() {
|
||||||
return (nextScalar, Unicode._NormData(nextScalar))
|
return (nextScalar, Unicode._NormData(nextScalar))
|
||||||
|
|||||||
@@ -232,7 +232,7 @@ extension Optional where Wrapped: ~Copyable {
|
|||||||
) throws(E) -> U? {
|
) throws(E) -> U? {
|
||||||
#if compiler(>=6.0) && $NoncopyableGenerics
|
#if compiler(>=6.0) && $NoncopyableGenerics
|
||||||
switch self {
|
switch self {
|
||||||
case .some(_borrowing y):
|
case .some(let y):
|
||||||
return .some(try transform(y))
|
return .some(try transform(y))
|
||||||
case .none:
|
case .none:
|
||||||
return .none
|
return .none
|
||||||
@@ -310,7 +310,7 @@ extension Optional where Wrapped: ~Copyable {
|
|||||||
) throws(E) -> U? {
|
) throws(E) -> U? {
|
||||||
#if compiler(>=6.0) && $NoncopyableGenerics
|
#if compiler(>=6.0) && $NoncopyableGenerics
|
||||||
switch self {
|
switch self {
|
||||||
case .some(_borrowing y):
|
case .some(let y):
|
||||||
return try transform(y)
|
return try transform(y)
|
||||||
case .none:
|
case .none:
|
||||||
return .none
|
return .none
|
||||||
@@ -418,8 +418,8 @@ extension Optional where Wrapped: ~Copyable {
|
|||||||
///
|
///
|
||||||
/// - Returns: The wrapped value being stored in this instance. If this
|
/// - Returns: The wrapped value being stored in this instance. If this
|
||||||
/// instance is `nil`, returns `nil`.
|
/// instance is `nil`, returns `nil`.
|
||||||
@_alwaysEmitIntoClient // FIXME(NCG): Make this public.
|
@_alwaysEmitIntoClient
|
||||||
public mutating func _take() -> Self {
|
public mutating func take() -> Self {
|
||||||
let result = consume self
|
let result = consume self
|
||||||
self = nil
|
self = nil
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ extension Result where Success: ~Copyable {
|
|||||||
_ transform: (borrowing Success) -> NewSuccess
|
_ transform: (borrowing Success) -> NewSuccess
|
||||||
) -> Result<NewSuccess, Failure> {
|
) -> Result<NewSuccess, Failure> {
|
||||||
switch self {
|
switch self {
|
||||||
case .success(borrowing success):
|
case .success(let success):
|
||||||
return .success(transform(success))
|
return .success(transform(success))
|
||||||
case let .failure(failure):
|
case let .failure(failure):
|
||||||
return .failure(failure)
|
return .failure(failure)
|
||||||
@@ -241,7 +241,7 @@ extension Result where Success: ~Copyable {
|
|||||||
_ transform: (borrowing Success) -> Result<NewSuccess, Failure>
|
_ transform: (borrowing Success) -> Result<NewSuccess, Failure>
|
||||||
) -> Result<NewSuccess, Failure> {
|
) -> Result<NewSuccess, Failure> {
|
||||||
switch self {
|
switch self {
|
||||||
case .success(borrowing success):
|
case .success(let success):
|
||||||
return transform(success)
|
return transform(success)
|
||||||
case let .failure(failure):
|
case let .failure(failure):
|
||||||
return .failure(failure)
|
return .failure(failure)
|
||||||
|
|||||||
@@ -456,6 +456,79 @@ extension Unsafe${Mutable}BufferPointer where Element: ~Copyable {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Unsafe${Mutable}BufferPointer where Element: ~Copyable {
|
||||||
|
/// Constructs a standalone buffer pointer over the items within the supplied
|
||||||
|
/// range of positions in the memory region addressed by this buffer pointer.
|
||||||
|
///
|
||||||
|
/// The returned buffer's first item is always at index 0; unlike buffer
|
||||||
|
/// slices, extracted buffers do not generally share their indices with the
|
||||||
|
/// original buffer pointer.
|
||||||
|
///
|
||||||
|
/// withUnsafeTemporaryAllocation(of: Int.self, capacity: 5) { buffer in
|
||||||
|
/// buffer.initialize(repeating: 0)
|
||||||
|
/// // buffer contains [0, 0, 0, 0, 0]
|
||||||
|
/// let part = buffer.extracting(2 ..< 4)
|
||||||
|
/// part[0] = 1
|
||||||
|
/// part[1] = 2
|
||||||
|
/// // buffer now contains [0, 0, 1, 2, 0]
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// When `Element` is copyable, the `extracting` operation is equivalent to
|
||||||
|
/// slicing the buffer then rebasing the resulting buffer slice:
|
||||||
|
///
|
||||||
|
/// let a = buffer.extracting(i ..< j)
|
||||||
|
/// let b = UnsafeBufferPointer(rebasing: buffer[i ..< j])
|
||||||
|
/// // `a` and `b` are now holding the same buffer
|
||||||
|
///
|
||||||
|
/// However, unlike slicing, the `extracting` operation remains available even
|
||||||
|
/// if `Element` happens to be noncopyable.
|
||||||
|
///
|
||||||
|
/// - Parameter bounds: A valid range of indices within this buffer.
|
||||||
|
/// - Returns: A new buffer pointer over the items at `bounds`.
|
||||||
|
@_alwaysEmitIntoClient
|
||||||
|
public func extracting(_ bounds: Range<Int>) -> Self {
|
||||||
|
_precondition(bounds.lowerBound >= 0 && bounds.upperBound <= count,
|
||||||
|
"Index out of range")
|
||||||
|
guard let start = self.baseAddress else {
|
||||||
|
return Self(start: nil, count: 0)
|
||||||
|
}
|
||||||
|
return Self(start: start + bounds.lowerBound, count: bounds.count)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs a standalone buffer pointer over the items within the supplied
|
||||||
|
/// range of positions in the memory region addressed by this buffer pointer.
|
||||||
|
///
|
||||||
|
/// The returned buffer's first item is always at index 0; unlike buffer
|
||||||
|
/// slices, extracted buffers do not generally share their indices with the
|
||||||
|
/// original buffer pointer.
|
||||||
|
///
|
||||||
|
/// withUnsafeTemporaryAllocation(of: Int.self, capacity: 5) { buffer in
|
||||||
|
/// buffer.initialize(repeating: 0)
|
||||||
|
/// // buffer contains [0, 0, 0, 0, 0]
|
||||||
|
/// let part = buffer.extracting(2...)
|
||||||
|
/// part[0] = 1
|
||||||
|
/// part[1] = 2
|
||||||
|
/// // buffer now contains [0, 0, 1, 2, 0]
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// When `Element` is copyable, the `extracting` operation is equivalent to
|
||||||
|
/// slicing the buffer then rebasing the resulting buffer slice:
|
||||||
|
///
|
||||||
|
/// let a = buffer.extracting(i ..< j)
|
||||||
|
/// let b = UnsafeBufferPointer(rebasing: buffer[i ..< j])
|
||||||
|
/// // `a` and `b` are now holding the same buffer
|
||||||
|
///
|
||||||
|
/// However, unlike slicing, the `extracting` operation remains available even
|
||||||
|
/// if `Element` happens to be noncopyable.
|
||||||
|
///
|
||||||
|
/// - Parameter bounds: A valid range of indices within this buffer.
|
||||||
|
/// - Returns: A new buffer pointer over the items at `bounds`.
|
||||||
|
@_alwaysEmitIntoClient
|
||||||
|
public func extracting(_ bounds: some RangeExpression<Int>) -> Self {
|
||||||
|
extracting(bounds.relative(to: Range(uncheckedBounds: (0, count))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@_disallowFeatureSuppression(NoncopyableGenerics)
|
@_disallowFeatureSuppression(NoncopyableGenerics)
|
||||||
extension Unsafe${Mutable}BufferPointer {
|
extension Unsafe${Mutable}BufferPointer {
|
||||||
/// Accesses the element at the specified position.
|
/// Accesses the element at the specified position.
|
||||||
|
|||||||
@@ -13,6 +13,6 @@ struct Foo<T> {
|
|||||||
// in an optional
|
// in an optional
|
||||||
let x: Foo<Bar>? = Foo<Bar>()
|
let x: Foo<Bar>? = Foo<Bar>()
|
||||||
x.#^FOO_OPTIONAL_1^#
|
x.#^FOO_OPTIONAL_1^#
|
||||||
// FOO_OPTIONAL_1: Begin completions, 7 items
|
// FOO_OPTIONAL_1: Begin completions, 8 items
|
||||||
// FOO_OPTIONAL_1-DAG: Decl[InstanceMethod]/CurrNominal/Erase[1]: ?.myFunction({#(foobar): Bar#})[#Void#]; name=myFunction(:)
|
// FOO_OPTIONAL_1-DAG: Decl[InstanceMethod]/CurrNominal/Erase[1]: ?.myFunction({#(foobar): Bar#})[#Void#]; name=myFunction(:)
|
||||||
// FOO_OPTIONAL_1-DAG: Keyword[self]/CurrNominal: self[#Foo<Bar>?#]; name=self
|
// FOO_OPTIONAL_1-DAG: Keyword[self]/CurrNominal: self[#Foo<Bar>?#]; name=self
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ class C4 {
|
|||||||
// UNRESOLVED_3-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#]; name=hash(:)
|
// UNRESOLVED_3-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#]; name=hash(:)
|
||||||
|
|
||||||
// Exhaustive to make sure we don't include `init({#(some):` or `init({#nilLiteral:` entries
|
// Exhaustive to make sure we don't include `init({#(some):` or `init({#nilLiteral:` entries
|
||||||
// UNRESOLVED_3_OPT: Begin completions, 9 items
|
// UNRESOLVED_3_OPT: Begin completions, 10 items
|
||||||
// UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: North[#SomeEnum1#];
|
// UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: North[#SomeEnum1#];
|
||||||
// UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: South[#SomeEnum1#];
|
// UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: South[#SomeEnum1#];
|
||||||
// UNRESOLVED_3_OPT-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#];
|
// UNRESOLVED_3_OPT-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#];
|
||||||
@@ -160,7 +160,7 @@ class C4 {
|
|||||||
// UNRESOLVED_3_OPT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem/TypeRelation[Invalid]: hash({#(self): Optional<SomeEnum1>#})[#(into: inout Hasher) -> Void#];
|
// UNRESOLVED_3_OPT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem/TypeRelation[Invalid]: hash({#(self): Optional<SomeEnum1>#})[#(into: inout Hasher) -> Void#];
|
||||||
|
|
||||||
// Exhaustive to make sure we don't include `init({#(some):` or `init({#nilLiteral:` entries
|
// Exhaustive to make sure we don't include `init({#(some):` or `init({#nilLiteral:` entries
|
||||||
// UNRESOLVED_3_OPTOPTOPT: Begin completions, 9 items
|
// UNRESOLVED_3_OPTOPTOPT: Begin completions, 10 items
|
||||||
// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: North[#SomeEnum1#];
|
// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: North[#SomeEnum1#];
|
||||||
// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: South[#SomeEnum1#];
|
// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: South[#SomeEnum1#];
|
||||||
// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#];
|
// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#];
|
||||||
@@ -169,6 +169,7 @@ class C4 {
|
|||||||
// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/CurrNominal/IsSystem/TypeRelation[Convertible]: some({#SomeEnum1??#})[#Optional<SomeEnum1??>#];
|
// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/CurrNominal/IsSystem/TypeRelation[Convertible]: some({#SomeEnum1??#})[#Optional<SomeEnum1??>#];
|
||||||
// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: map({#(self): Optional<SomeEnum1??>#})[#((SomeEnum1??) throws(Error) -> ~Copyable) -> ~Copyable?#];
|
// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: map({#(self): Optional<SomeEnum1??>#})[#((SomeEnum1??) throws(Error) -> ~Copyable) -> ~Copyable?#];
|
||||||
// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: flatMap({#(self): Optional<SomeEnum1??>#})[#((SomeEnum1??) throws(Error) -> ~Copyable?) -> ~Copyable?#];
|
// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: flatMap({#(self): Optional<SomeEnum1??>#})[#((SomeEnum1??) throws(Error) -> ~Copyable?) -> ~Copyable?#];
|
||||||
|
// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: take({#(self): &Optional<SomeEnum1??>#})[#() -> Optional<SomeEnum1??>#];
|
||||||
// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem/TypeRelation[Invalid]: hash({#(self): Optional<SomeEnum1??>#})[#(into: inout Hasher) -> Void#];
|
// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem/TypeRelation[Invalid]: hash({#(self): Optional<SomeEnum1??>#})[#(into: inout Hasher) -> Void#];
|
||||||
|
|
||||||
enum Somewhere {
|
enum Somewhere {
|
||||||
@@ -180,7 +181,7 @@ extension Optional where Wrapped == Somewhere {
|
|||||||
}
|
}
|
||||||
func testOptionalWithCustomExtension() {
|
func testOptionalWithCustomExtension() {
|
||||||
var _: Somewhere? = .#^UNRESOLVED_OPT_4^#
|
var _: Somewhere? = .#^UNRESOLVED_OPT_4^#
|
||||||
// UNRESOLVED_OPT_4: Begin completions, 11 items
|
// UNRESOLVED_OPT_4: Begin completions, 12 items
|
||||||
// UNRESOLVED_OPT_4-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: earth[#Somewhere#];
|
// UNRESOLVED_OPT_4-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: earth[#Somewhere#];
|
||||||
// UNRESOLVED_OPT_4-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: mars[#Somewhere#];
|
// UNRESOLVED_OPT_4-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: mars[#Somewhere#];
|
||||||
// UNRESOLVED_OPT_4-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): Somewhere#})[#(into: inout Hasher) -> Void#];
|
// UNRESOLVED_OPT_4-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): Somewhere#})[#(into: inout Hasher) -> Void#];
|
||||||
@@ -191,6 +192,7 @@ func testOptionalWithCustomExtension() {
|
|||||||
// UNRESOLVED_OPT_4-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Convertible]: nowhere[#Optional<Somewhere>#]; name=nowhere
|
// UNRESOLVED_OPT_4-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Convertible]: nowhere[#Optional<Somewhere>#]; name=nowhere
|
||||||
// UNRESOLVED_OPT_4-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: map({#(self): Optional<Somewhere>#})[#((Somewhere) throws(Error) -> ~Copyable) -> ~Copyable?#];
|
// UNRESOLVED_OPT_4-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: map({#(self): Optional<Somewhere>#})[#((Somewhere) throws(Error) -> ~Copyable) -> ~Copyable?#];
|
||||||
// UNRESOLVED_OPT_4-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: flatMap({#(self): Optional<Somewhere>#})[#((Somewhere) throws(Error) -> ~Copyable?) -> ~Copyable?#];
|
// UNRESOLVED_OPT_4-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: flatMap({#(self): Optional<Somewhere>#})[#((Somewhere) throws(Error) -> ~Copyable?) -> ~Copyable?#];
|
||||||
|
// UNRESOLVED_OPT_4-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: take({#(self): &Optional<Somewhere>#})[#() -> Optional<Somewhere>#];
|
||||||
// UNRESOLVED_OPT_4-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem/TypeRelation[Invalid]: hash({#(self): Optional<Somewhere>#})[#(into: inout Hasher) -> Void#];
|
// UNRESOLVED_OPT_4-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem/TypeRelation[Invalid]: hash({#(self): Optional<Somewhere>#})[#(into: inout Hasher) -> Void#];
|
||||||
// UNRESOLVED_OPT_4-NOT: init({#(some):
|
// UNRESOLVED_OPT_4-NOT: init({#(some):
|
||||||
// UNRESOLVED_OPT_4-NOT: init({#nilLiteral:
|
// UNRESOLVED_OPT_4-NOT: init({#nilLiteral:
|
||||||
@@ -688,7 +690,7 @@ func testSameType() {
|
|||||||
|
|
||||||
testSugarType(.#^SUGAR_TYPE^#
|
testSugarType(.#^SUGAR_TYPE^#
|
||||||
// Ensure results aren't duplicated.
|
// Ensure results aren't duplicated.
|
||||||
// SUGAR_TYPE: Begin completions, 9 items
|
// SUGAR_TYPE: Begin completions, 10 items
|
||||||
// SUGAR_TYPE-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: South[#SomeEnum1#];
|
// SUGAR_TYPE-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: South[#SomeEnum1#];
|
||||||
// SUGAR_TYPE-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: North[#SomeEnum1#];
|
// SUGAR_TYPE-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: North[#SomeEnum1#];
|
||||||
// SUGAR_TYPE-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#];
|
// SUGAR_TYPE-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#];
|
||||||
@@ -697,6 +699,7 @@ func testSameType() {
|
|||||||
// SUGAR_TYPE-DAG: Decl[EnumElement]/CurrNominal/IsSystem/TypeRelation[Convertible]: some({#SomeEnum1#})[#Optional<SomeEnum1>#];
|
// SUGAR_TYPE-DAG: Decl[EnumElement]/CurrNominal/IsSystem/TypeRelation[Convertible]: some({#SomeEnum1#})[#Optional<SomeEnum1>#];
|
||||||
// SUGAR_TYPE-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: map({#(self): Optional<SomeEnum1>#})[#((SomeEnum1) throws(Error) -> ~Copyable) -> ~Copyable?#];
|
// SUGAR_TYPE-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: map({#(self): Optional<SomeEnum1>#})[#((SomeEnum1) throws(Error) -> ~Copyable) -> ~Copyable?#];
|
||||||
// SUGAR_TYPE-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: flatMap({#(self): Optional<SomeEnum1>#})[#((SomeEnum1) throws(Error) -> ~Copyable?) -> ~Copyable?#];
|
// SUGAR_TYPE-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: flatMap({#(self): Optional<SomeEnum1>#})[#((SomeEnum1) throws(Error) -> ~Copyable?) -> ~Copyable?#];
|
||||||
|
// SUGAR_TYPE-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: take({#(self): &Optional<SomeEnum1>#})[#() -> Optional<SomeEnum1>#];
|
||||||
// SUGAR_TYPE-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem/TypeRelation[Invalid]: hash({#(self): Optional<SomeEnum1>#})[#(into: inout Hasher) -> Void#];
|
// SUGAR_TYPE-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem/TypeRelation[Invalid]: hash({#(self): Optional<SomeEnum1>#})[#(into: inout Hasher) -> Void#];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
293
test/Prototypes/Hypoarray.swift
Normal file
293
test/Prototypes/Hypoarray.swift
Normal file
@@ -0,0 +1,293 @@
|
|||||||
|
//===--- Algorithms.swift -------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// This source file is part of the Swift.org open source project
|
||||||
|
//
|
||||||
|
// Copyright (c) 2024 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: %empty-directory(%t)
|
||||||
|
// RUN: %target-build-swift -swift-version 6 -o %t/a.out %s
|
||||||
|
// RUN: %target-codesign %t/a.out
|
||||||
|
// RUN: %target-run %t/a.out
|
||||||
|
// REQUIRES: executable_test
|
||||||
|
|
||||||
|
import StdlibUnittest
|
||||||
|
import Synchronization
|
||||||
|
|
||||||
|
struct Hypoarray<Element: ~Copyable>: ~Copyable {
|
||||||
|
private var _storage: UnsafeMutableBufferPointer<Element>
|
||||||
|
private var _count: Int
|
||||||
|
|
||||||
|
var capacity: Int { _storage.count }
|
||||||
|
|
||||||
|
init() {
|
||||||
|
_storage = .init(start: nil, count: 0)
|
||||||
|
_count = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
init(_ element: consuming Element) {
|
||||||
|
_storage = .allocate(capacity: 1)
|
||||||
|
_storage.initializeElement(at: 0, to: element)
|
||||||
|
_count = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
init(count: Int, initializedBy generator: (Int) -> Element) {
|
||||||
|
_storage = .allocate(capacity: count)
|
||||||
|
for i in 0 ..< count {
|
||||||
|
_storage.initializeElement(at: i, to: generator(i))
|
||||||
|
}
|
||||||
|
_count = count
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
_storage.extracting(0 ..< count).deinitialize()
|
||||||
|
_storage.deallocate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Hypoarray: @unchecked Sendable where Element: Sendable & ~Copyable {}
|
||||||
|
|
||||||
|
extension Hypoarray where Element: ~Copyable {
|
||||||
|
typealias Index = Int
|
||||||
|
|
||||||
|
var isEmpty: Bool { _count == 0 }
|
||||||
|
var count: Int { _count }
|
||||||
|
|
||||||
|
var startIndex: Int { 0 }
|
||||||
|
var endIndex: Int { _count }
|
||||||
|
func index(after i: Int) -> Int { i + 1 }
|
||||||
|
func index(before i: Int) -> Int { i - 1 }
|
||||||
|
func distance(from start: Int, to end: Int) -> Int { end - start }
|
||||||
|
// etc.
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Hypoarray where Element: ~Copyable {
|
||||||
|
func borrowElement<E: Error, R: ~Copyable> (
|
||||||
|
at index: Int,
|
||||||
|
by body: (borrowing Element) throws(E) -> R
|
||||||
|
) throws(E) -> R {
|
||||||
|
precondition(index >= 0 && index < _count)
|
||||||
|
return try body(_storage[index])
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func updateElement<E: Error, R: ~Copyable> (
|
||||||
|
at index: Int,
|
||||||
|
by body: (inout Element) throws(E) -> R
|
||||||
|
) throws(E) -> R {
|
||||||
|
precondition(index >= 0 && index < _count)
|
||||||
|
return try body(&_storage[index])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Hypoarray where Element: ~Copyable {
|
||||||
|
subscript(position: Int) -> Element {
|
||||||
|
_read {
|
||||||
|
precondition(position >= 0 && position < _count)
|
||||||
|
yield _storage[position]
|
||||||
|
}
|
||||||
|
_modify {
|
||||||
|
precondition(position >= 0 && position < _count)
|
||||||
|
yield &_storage[position]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Hypoarray where Element: ~Copyable {
|
||||||
|
@discardableResult
|
||||||
|
mutating func remove(at index: Int) -> Element {
|
||||||
|
precondition(index >= 0 && index < count)
|
||||||
|
let old = _storage.moveElement(from: index)
|
||||||
|
let source = _storage.extracting(index + 1 ..< count)
|
||||||
|
let target = _storage.extracting(index ..< count - 1)
|
||||||
|
let i = target.moveInitialize(fromContentsOf: source)
|
||||||
|
assert(i == target.endIndex)
|
||||||
|
_count -= 1
|
||||||
|
return old
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Hypoarray where Element: ~Copyable {
|
||||||
|
mutating func reserveCapacity(_ n: Int) {
|
||||||
|
guard capacity < n else { return }
|
||||||
|
let newStorage: UnsafeMutableBufferPointer<Element> = .allocate(capacity: n)
|
||||||
|
let source = _storage.extracting(0 ..< count)
|
||||||
|
let i = newStorage.moveInitialize(fromContentsOf: source)
|
||||||
|
assert(i == count)
|
||||||
|
_storage.deallocate()
|
||||||
|
_storage = newStorage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Hypoarray where Element: ~Copyable {
|
||||||
|
mutating func _ensureFreeCapacity(_ minimumCapacity: Int) {
|
||||||
|
guard capacity < _count + minimumCapacity else { return }
|
||||||
|
reserveCapacity(max(_count + minimumCapacity, 2 * capacity))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Hypoarray where Element: ~Copyable {
|
||||||
|
mutating func append(_ item: consuming Element) {
|
||||||
|
_ensureFreeCapacity(1)
|
||||||
|
_storage.initializeElement(at: _count, to: item)
|
||||||
|
_count += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Hypoarray where Element: ~Copyable {
|
||||||
|
mutating func insert(_ item: consuming Element, at index: Int) {
|
||||||
|
precondition(index >= 0 && index <= count)
|
||||||
|
_ensureFreeCapacity(1)
|
||||||
|
if index < count {
|
||||||
|
let source = _storage.extracting(index ..< count)
|
||||||
|
let target = _storage.extracting(index + 1 ..< count + 1)
|
||||||
|
let last = target.moveInitialize(fromContentsOf: source)
|
||||||
|
assert(last == target.endIndex)
|
||||||
|
}
|
||||||
|
_storage.initializeElement(at: index, to: item)
|
||||||
|
_count += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Hypoarray {
|
||||||
|
mutating func append(contentsOf items: some Sequence<Element>) {
|
||||||
|
for item in items {
|
||||||
|
append(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Counted: ~Copyable {
|
||||||
|
var value: Int
|
||||||
|
nonisolated(unsafe) static var instances: Int = 0
|
||||||
|
|
||||||
|
init(_ value: Int) {
|
||||||
|
self.value = value
|
||||||
|
Counted.instances += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
Counted.instances -= 1
|
||||||
|
expectGE(Counted.instances, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var suite = TestSuite("Hypoarray")
|
||||||
|
defer { runAllTests() }
|
||||||
|
|
||||||
|
suite.test("basics") {
|
||||||
|
var array = Hypoarray(Counted(42))
|
||||||
|
expectFalse(array.isEmpty)
|
||||||
|
expectEqual(array.count, 1)
|
||||||
|
expectEqual(array[0].value, 42)
|
||||||
|
expectEqual(Counted.instances, 1)
|
||||||
|
|
||||||
|
array.append(Counted(23))
|
||||||
|
expectFalse(array.isEmpty)
|
||||||
|
expectEqual(array.count, 2)
|
||||||
|
expectEqual(array[0].value, 42)
|
||||||
|
expectEqual(array[1].value, 23)
|
||||||
|
expectEqual(Counted.instances, 2)
|
||||||
|
|
||||||
|
let old = array.remove(at: 0)
|
||||||
|
expectEqual(old.value, 42)
|
||||||
|
expectFalse(array.isEmpty)
|
||||||
|
expectEqual(array.count, 1)
|
||||||
|
expectEqual(array[0].value, 23)
|
||||||
|
expectEqual(Counted.instances, 2)
|
||||||
|
_ = consume old
|
||||||
|
expectEqual(Counted.instances, 1)
|
||||||
|
|
||||||
|
let old2 = array.remove(at: 0)
|
||||||
|
expectEqual(old2.value, 23)
|
||||||
|
expectEqual(array.count, 0)
|
||||||
|
expectTrue(array.isEmpty)
|
||||||
|
expectEqual(Counted.instances, 1)
|
||||||
|
_ = consume old2
|
||||||
|
expectEqual(Counted.instances, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
suite.test("read access") {
|
||||||
|
let c = 100
|
||||||
|
let array = Hypoarray<Counted>(count: c) { Counted($0) }
|
||||||
|
|
||||||
|
for i in 0 ..< c {
|
||||||
|
expectEqual(array.borrowElement(at: i) { $0.value }, i)
|
||||||
|
expectEqual(array[i].value, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suite.test("update access") {
|
||||||
|
let c = 100
|
||||||
|
var array = Hypoarray<Counted>(count: c) { Counted($0) }
|
||||||
|
|
||||||
|
for i in 0 ..< c {
|
||||||
|
array.updateElement(at: i) { $0.value += 100 }
|
||||||
|
array[i].value += 100
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0 ..< c {
|
||||||
|
expectEqual(array[i].value, 200 + i)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectEqual(Counted.instances, c)
|
||||||
|
_ = consume array
|
||||||
|
expectEqual(Counted.instances, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
suite.test("append") {
|
||||||
|
var array = Hypoarray<Counted>()
|
||||||
|
let c = 100
|
||||||
|
for i in 0 ..< c {
|
||||||
|
array.append(Counted(100 + i))
|
||||||
|
}
|
||||||
|
expectEqual(Counted.instances, c)
|
||||||
|
expectEqual(array.count, c)
|
||||||
|
|
||||||
|
for i in 0 ..< c {
|
||||||
|
// FIXME: unexpected exclusivity violation (rdar://128441125)
|
||||||
|
//expectEqual(array.borrowElement(at: i) { $0.value }, 100 + i)
|
||||||
|
expectEqual(array[i].value, 100 + i)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = consume array
|
||||||
|
expectEqual(Counted.instances, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
suite.test("insert") {
|
||||||
|
var array = Hypoarray<Counted>()
|
||||||
|
let c = 100
|
||||||
|
for i in 0 ..< c {
|
||||||
|
array.insert(Counted(100 + i), at: 0)
|
||||||
|
}
|
||||||
|
expectEqual(Counted.instances, c)
|
||||||
|
expectEqual(array.count, c)
|
||||||
|
|
||||||
|
for i in 0 ..< c {
|
||||||
|
// FIXME: unexpected exclusivity violation (rdar://128441125)
|
||||||
|
//expectEqual(array.borrowElement(at: i) { $0.value }, c + 99 - i)
|
||||||
|
expectEqual(array[i].value, c + 99 - i)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = consume array
|
||||||
|
expectEqual(Counted.instances, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
suite.test("remove") {
|
||||||
|
let c = 100
|
||||||
|
var array = Hypoarray<Counted>(count: c) { Counted(100 + $0) }
|
||||||
|
expectEqual(Counted.instances, c)
|
||||||
|
expectEqual(array.count, c)
|
||||||
|
|
||||||
|
for i in 0 ..< c {
|
||||||
|
array.remove(at: 0)
|
||||||
|
expectEqual(array.count, c - 1 - i)
|
||||||
|
expectEqual(Counted.instances, c - 1 - i)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectTrue(array.isEmpty)
|
||||||
|
expectEqual(Counted.instances, 0)
|
||||||
|
}
|
||||||
78
test/stdlib/swap.swift
Normal file
78
test/stdlib/swap.swift
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
// RUN: %target-run-simple-swift
|
||||||
|
// REQUIRES: executable_test
|
||||||
|
|
||||||
|
import StdlibUnittest
|
||||||
|
|
||||||
|
var suite = TestSuite("Unmanaged")
|
||||||
|
defer { runAllTests() }
|
||||||
|
|
||||||
|
struct Counted: ~Copyable {
|
||||||
|
let value: Int
|
||||||
|
static var instances: Int = 0
|
||||||
|
|
||||||
|
init(_ value: Int) {
|
||||||
|
self.value = value
|
||||||
|
Counted.instances += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
Counted.instances -= 1
|
||||||
|
expectGE(Counted.instances, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suite.test("swap.Int") {
|
||||||
|
var a = 1
|
||||||
|
var b = 2
|
||||||
|
swap(&a, &b)
|
||||||
|
expectEqual(a, 2)
|
||||||
|
expectEqual(b, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
suite.test("exchange.Int") {
|
||||||
|
var a = 1
|
||||||
|
let old = exchange(&a, with: 2)
|
||||||
|
expectEqual(old, 1)
|
||||||
|
expectEqual(a, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
suite.test("swap.class") {
|
||||||
|
var a = LifetimeTracked(10)
|
||||||
|
var b = LifetimeTracked(20)
|
||||||
|
swap(&a, &b)
|
||||||
|
expectEqual(LifetimeTracked.instances, 2)
|
||||||
|
expectEqual(a.value, 20)
|
||||||
|
expectEqual(b.value, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
suite.test("exchange.class") {
|
||||||
|
var a = LifetimeTracked(10)
|
||||||
|
let old = exchange(&a, with: LifetimeTracked(20))
|
||||||
|
expectEqual(LifetimeTracked.instances, 2)
|
||||||
|
expectEqual(old.value, 10)
|
||||||
|
expectEqual(a.value, 20)
|
||||||
|
}
|
||||||
|
|
||||||
|
suite.test("swap.noncopyable") {
|
||||||
|
var a = Counted(10)
|
||||||
|
var b = Counted(20)
|
||||||
|
expectEqual(Counted.instances, 2)
|
||||||
|
swap(&a, &b)
|
||||||
|
expectEqual(Counted.instances, 2)
|
||||||
|
expectEqual(a.value, 20)
|
||||||
|
expectEqual(b.value, 10)
|
||||||
|
_ = consume a
|
||||||
|
_ = consume b
|
||||||
|
expectEqual(Counted.instances, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
suite.test("exchange.noncopyable") {
|
||||||
|
var a = Counted(10)
|
||||||
|
let old = exchange(&a, with: Counted(20))
|
||||||
|
expectEqual(Counted.instances, 2)
|
||||||
|
expectEqual(a.value, 20)
|
||||||
|
expectEqual(old.value, 10)
|
||||||
|
_ = consume old
|
||||||
|
_ = consume a
|
||||||
|
expectEqual(Counted.instances, 0)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user