Skip to content

Commit 470a04c

Browse files
authored
Merge pull request #73807 from lorentey/stdlib-noncopyable-additions
[stdlib] API additions for basic noncopyable primitives
2 parents 21302e8 + 9d947e4 commit 470a04c

12 files changed

+510
-44
lines changed

stdlib/public/core/LifetimeManager.swift

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@
2020
/// return value for the `withExtendedLifetime(_:_:)` method.
2121
/// - Returns: The return value, if any, of the `body` closure parameter.
2222
@_alwaysEmitIntoClient
23-
public func withExtendedLifetime<T: ~Copyable, Result: ~Copyable>(
23+
public func withExtendedLifetime<T: ~Copyable, E: Error, Result: ~Copyable>(
2424
_ x: borrowing T,
25-
_ body: () throws -> Result // FIXME: Typed throws rdar://126576356
26-
) rethrows -> Result {
25+
_ body: () throws(E) -> Result
26+
) throws(E) -> Result {
2727
defer { _fixLifetime(x) }
2828
return try body()
2929
}
@@ -32,8 +32,7 @@ public func withExtendedLifetime<T: ~Copyable, Result: ~Copyable>(
3232
@_silgen_name("$ss20withExtendedLifetimeyq_x_q_yKXEtKr0_lF")
3333
@usableFromInline
3434
internal func __abi_withExtendedLifetime<T, Result>(
35-
_ x: T,
36-
_ body: () throws -> Result // FIXME: Typed throws rdar://126576356
35+
_ x: T, _ body: () throws -> Result
3736
) rethrows -> Result {
3837
defer { _fixLifetime(x) }
3938
return try body()
@@ -49,9 +48,10 @@ internal func __abi_withExtendedLifetime<T, Result>(
4948
/// return value for the `withExtendedLifetime(_:_:)` method.
5049
/// - Returns: The return value, if any, of the `body` closure parameter.
5150
@_alwaysEmitIntoClient
52-
public func withExtendedLifetime<T, Result: ~Copyable>(
53-
_ x: T, _ body: (T) throws -> Result // FIXME: Typed throws rdar://126576356
54-
) rethrows -> Result {
51+
public func withExtendedLifetime<T: ~Copyable, E: Error, Result: ~Copyable>(
52+
_ x: borrowing T,
53+
_ body: (borrowing T) throws(E) -> Result
54+
) throws(E) -> Result {
5555
defer { _fixLifetime(x) }
5656
return try body(x)
5757
}
@@ -60,7 +60,7 @@ public func withExtendedLifetime<T, Result: ~Copyable>(
6060
@_silgen_name("$ss20withExtendedLifetimeyq_x_q_xKXEtKr0_lF")
6161
@usableFromInline
6262
internal func __abi_withExtendedLifetime<T, Result>(
63-
_ x: T, _ body: (T) throws -> Result // FIXME: Typed throws rdar://126576356
63+
_ x: T, _ body: (T) throws -> Result
6464
) rethrows -> Result {
6565
defer { _fixLifetime(x) }
6666
return try body(x)

stdlib/public/core/MutableCollection.swift

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -529,24 +529,24 @@ extension MutableCollection {
529529
@inlinable
530530
@_preInverseGenerics
531531
public func swap<T: ~Copyable>(_ a: inout T, _ b: inout T) {
532-
// Semantically equivalent to (a, b) = (b, a).
533-
// Microoptimized to avoid retain/release traffic.
534-
#if $BuiltinUnprotectedAddressOf
535-
let p1 = Builtin.unprotectedAddressOf(&a)
536-
let p2 = Builtin.unprotectedAddressOf(&b)
537-
#else
538-
let p1 = Builtin.addressof(&a)
539-
let p2 = Builtin.addressof(&b)
540-
#endif
541-
_debugPrecondition(
542-
p1 != p2,
543-
"swapping a location with itself is not supported")
544-
545-
// Take from P1.
546-
let tmp: T = Builtin.take(p1)
547-
// Transfer P2 into P1.
548-
Builtin.initialize(Builtin.take(p2) as T, p1)
549-
// Initialize P2.
550-
Builtin.initialize(tmp, p2)
532+
let temp = consume a
533+
a = consume b
534+
b = consume temp
551535
}
552536

537+
/// Replaces the value of a mutable value with the supplied new value,
538+
/// returning the original.
539+
///
540+
/// - Parameters:
541+
/// - item: A mutable binding.
542+
/// - newValue: The new value of `item`.
543+
/// - Returns: The original value of `item`.
544+
@_alwaysEmitIntoClient
545+
public func exchange<T: ~Copyable>(
546+
_ item: inout T,
547+
with newValue: consuming T
548+
) -> T {
549+
let oldValue = consume item
550+
item = consume newValue
551+
return oldValue
552+
}

stdlib/public/core/NFC.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ extension Unicode {
236236

237237
// If we have a leftover composee, make sure to return it.
238238
// We may still have things in the buffer which are not complete segments.
239-
return composee._take() ?? buffer.next()?.scalar
239+
return composee.take() ?? buffer.next()?.scalar
240240
}
241241
}
242242
}

stdlib/public/core/NFD.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,15 +279,15 @@ extension Unicode._NFDNormalizer {
279279
// The buffer contains the decomposed segment *prior to*
280280
// any pending starter we might have.
281281

282-
return buffer.next() ?? pendingStarter._take()
282+
return buffer.next() ?? pendingStarter.take()
283283
}
284284

285285
@inline(__always)
286286
private mutating func takePendingOrConsume(
287287
_ nextFromSource: () -> Unicode.Scalar?
288288
) -> ScalarAndNormData? {
289289

290-
if let pendingStarter = pendingStarter._take() {
290+
if let pendingStarter = pendingStarter.take() {
291291
return pendingStarter
292292
} else if let nextScalar = nextFromSource() {
293293
return (nextScalar, Unicode._NormData(nextScalar))

stdlib/public/core/Optional.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ extension Optional where Wrapped: ~Copyable {
232232
) throws(E) -> U? {
233233
#if compiler(>=6.0) && $NoncopyableGenerics
234234
switch self {
235-
case .some(_borrowing y):
235+
case .some(let y):
236236
return .some(try transform(y))
237237
case .none:
238238
return .none
@@ -310,7 +310,7 @@ extension Optional where Wrapped: ~Copyable {
310310
) throws(E) -> U? {
311311
#if compiler(>=6.0) && $NoncopyableGenerics
312312
switch self {
313-
case .some(_borrowing y):
313+
case .some(let y):
314314
return try transform(y)
315315
case .none:
316316
return .none
@@ -418,8 +418,8 @@ extension Optional where Wrapped: ~Copyable {
418418
///
419419
/// - Returns: The wrapped value being stored in this instance. If this
420420
/// instance is `nil`, returns `nil`.
421-
@_alwaysEmitIntoClient // FIXME(NCG): Make this public.
422-
public mutating func _take() -> Self {
421+
@_alwaysEmitIntoClient
422+
public mutating func take() -> Self {
423423
let result = consume self
424424
self = nil
425425
return result

stdlib/public/core/Result.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ extension Result where Success: ~Copyable {
9999
_ transform: (borrowing Success) -> NewSuccess
100100
) -> Result<NewSuccess, Failure> {
101101
switch self {
102-
case .success(borrowing success):
102+
case .success(let success):
103103
return .success(transform(success))
104104
case let .failure(failure):
105105
return .failure(failure)
@@ -241,7 +241,7 @@ extension Result where Success: ~Copyable {
241241
_ transform: (borrowing Success) -> Result<NewSuccess, Failure>
242242
) -> Result<NewSuccess, Failure> {
243243
switch self {
244-
case .success(borrowing success):
244+
case .success(let success):
245245
return transform(success)
246246
case let .failure(failure):
247247
return .failure(failure)

stdlib/public/core/UnsafeBufferPointer.swift.gyb

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,98 @@ extension Unsafe${Mutable}BufferPointer where Element: ~Copyable {
456456

457457
}
458458

459+
extension Unsafe${Mutable}BufferPointer where Element: ~Copyable {
460+
/// Constructs a standalone buffer pointer over the items within the supplied
461+
/// range of positions in the memory region addressed by this buffer pointer.
462+
///
463+
/// The returned buffer's first item is always at index 0; unlike buffer
464+
/// slices, extracted buffers do not generally share their indices with the
465+
/// original buffer pointer.
466+
///
467+
/// withUnsafeTemporaryAllocation(of: Int.self, capacity: 5) { buffer in
468+
/// buffer.initialize(repeating: 0)
469+
/// // buffer contains [0, 0, 0, 0, 0]
470+
/// let part = buffer.extracting(2 ..< 4)
471+
/// part[0] = 1
472+
/// part[1] = 2
473+
/// // buffer now contains [0, 0, 1, 2, 0]
474+
/// }
475+
///
476+
/// When `Element` is copyable, the `extracting` operation is equivalent to
477+
/// slicing the buffer then rebasing the resulting buffer slice:
478+
///
479+
/// let a = buffer.extracting(i ..< j)
480+
/// let b = UnsafeBufferPointer(rebasing: buffer[i ..< j])
481+
/// // `a` and `b` are now holding the same buffer
482+
///
483+
/// However, unlike slicing, the `extracting` operation remains available even
484+
/// if `Element` happens to be noncopyable.
485+
///
486+
/// - Parameter bounds: A valid range of indices within this buffer.
487+
/// - Returns: A new buffer pointer over the items at `bounds`.
488+
@_alwaysEmitIntoClient
489+
public func extracting(_ bounds: Range<Int>) -> Self {
490+
_precondition(bounds.lowerBound >= 0 && bounds.upperBound <= count,
491+
"Index out of range")
492+
guard let start = self.baseAddress else {
493+
return Self(start: nil, count: 0)
494+
}
495+
return Self(start: start + bounds.lowerBound, count: bounds.count)
496+
}
497+
498+
/// Constructs a standalone buffer pointer over the items within the supplied
499+
/// range of positions in the memory region addressed by this buffer pointer.
500+
///
501+
/// The returned buffer's first item is always at index 0; unlike buffer
502+
/// slices, extracted buffers do not generally share their indices with the
503+
/// original buffer pointer.
504+
///
505+
/// withUnsafeTemporaryAllocation(of: Int.self, capacity: 5) { buffer in
506+
/// buffer.initialize(repeating: 0)
507+
/// // buffer contains [0, 0, 0, 0, 0]
508+
/// let part = buffer.extracting(2...)
509+
/// part[0] = 1
510+
/// part[1] = 2
511+
/// // buffer now contains [0, 0, 1, 2, 0]
512+
/// }
513+
///
514+
/// When `Element` is copyable, the `extracting` operation is equivalent to
515+
/// slicing the buffer then rebasing the resulting buffer slice:
516+
///
517+
/// let a = buffer.extracting(i ..< j)
518+
/// let b = UnsafeBufferPointer(rebasing: buffer[i ..< j])
519+
/// // `a` and `b` are now holding the same buffer
520+
///
521+
/// However, unlike slicing, the `extracting` operation remains available even
522+
/// if `Element` happens to be noncopyable.
523+
///
524+
/// - Parameter bounds: A valid range of indices within this buffer.
525+
/// - Returns: A new buffer pointer over the items at `bounds`.
526+
@_alwaysEmitIntoClient
527+
public func extracting(_ bounds: some RangeExpression<Int>) -> Self {
528+
extracting(bounds.relative(to: Range(uncheckedBounds: (0, count))))
529+
}
530+
531+
/// Extracts and returns a copy of the entire buffer.
532+
///
533+
/// When `Element` is copyable, the `extracting` operation is equivalent to
534+
/// slicing the buffer then rebasing the resulting buffer slice:
535+
///
536+
/// let a = buffer
537+
/// let b = buffer.extracting(...)
538+
/// let c = UnsafeBufferPointer(rebasing: buffer[...])
539+
/// // `a`, `b` and `c` are now all referring to the same buffer
540+
///
541+
/// Note that unlike slicing, the `extracting` operation remains available
542+
/// even if `Element` happens to be noncopyable.
543+
//
544+
/// - Returns: The same buffer as `self`.
545+
@_alwaysEmitIntoClient
546+
public func extracting(_ bounds: UnboundedRange) -> Self {
547+
self
548+
}
549+
}
550+
459551
@_disallowFeatureSuppression(NoncopyableGenerics)
460552
extension Unsafe${Mutable}BufferPointer {
461553
/// Accesses the element at the specified position.

test/IDE/complete_generic_optional.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ struct Foo<T> {
1313
// in an optional
1414
let x: Foo<Bar>? = Foo<Bar>()
1515
x.#^FOO_OPTIONAL_1^#
16-
// FOO_OPTIONAL_1: Begin completions, 7 items
16+
// FOO_OPTIONAL_1: Begin completions, 8 items
1717
// FOO_OPTIONAL_1-DAG: Decl[InstanceMethod]/CurrNominal/Erase[1]: ?.myFunction({#(foobar): Bar#})[#Void#]; name=myFunction(:)
1818
// FOO_OPTIONAL_1-DAG: Keyword[self]/CurrNominal: self[#Foo<Bar>?#]; name=self

test/IDE/complete_unresolved_members.swift

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ class C4 {
147147
// UNRESOLVED_3-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#]; name=hash(:)
148148

149149
// Exhaustive to make sure we don't include `init({#(some):` or `init({#nilLiteral:` entries
150-
// UNRESOLVED_3_OPT: Begin completions, 9 items
150+
// UNRESOLVED_3_OPT: Begin completions, 10 items
151151
// UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: North[#SomeEnum1#];
152152
// UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: South[#SomeEnum1#];
153153
// UNRESOLVED_3_OPT-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#];
@@ -157,9 +157,10 @@ class C4 {
157157
// UNRESOLVED_3_OPT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: map({#(self): Optional<SomeEnum1>#})[#((SomeEnum1) throws(Error) -> ~Copyable) -> ~Copyable?#]; name=map(:)
158158
// UNRESOLVED_3_OPT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: flatMap({#(self): Optional<SomeEnum1>#})[#((SomeEnum1) throws(Error) -> ~Copyable?) -> ~Copyable?#];
159159
// UNRESOLVED_3_OPT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem/TypeRelation[Invalid]: hash({#(self): Optional<SomeEnum1>#})[#(into: inout Hasher) -> Void#];
160+
// UNRESOLVED_3_OPT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: take({#(self): &Optional<SomeEnum1>#})[#() -> Optional<SomeEnum1>#];
160161

161162
// Exhaustive to make sure we don't include `init({#(some):` or `init({#nilLiteral:` entries
162-
// UNRESOLVED_3_OPTOPTOPT: Begin completions, 9 items
163+
// UNRESOLVED_3_OPTOPTOPT: Begin completions, 10 items
163164
// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: North[#SomeEnum1#];
164165
// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: South[#SomeEnum1#];
165166
// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#];
@@ -168,6 +169,7 @@ class C4 {
168169
// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/CurrNominal/IsSystem/TypeRelation[Convertible]: some({#SomeEnum1??#})[#Optional<SomeEnum1??>#];
169170
// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: map({#(self): Optional<SomeEnum1??>#})[#((SomeEnum1??) throws(Error) -> ~Copyable) -> ~Copyable?#];
170171
// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: flatMap({#(self): Optional<SomeEnum1??>#})[#((SomeEnum1??) throws(Error) -> ~Copyable?) -> ~Copyable?#];
172+
// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: take({#(self): &Optional<SomeEnum1??>#})[#() -> Optional<SomeEnum1??>#];
171173
// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem/TypeRelation[Invalid]: hash({#(self): Optional<SomeEnum1??>#})[#(into: inout Hasher) -> Void#];
172174

173175
enum Somewhere {
@@ -179,7 +181,7 @@ extension Optional where Wrapped == Somewhere {
179181
}
180182
func testOptionalWithCustomExtension() {
181183
var _: Somewhere? = .#^UNRESOLVED_OPT_4^#
182-
// UNRESOLVED_OPT_4: Begin completions, 11 items
184+
// UNRESOLVED_OPT_4: Begin completions, 12 items
183185
// UNRESOLVED_OPT_4-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: earth[#Somewhere#];
184186
// UNRESOLVED_OPT_4-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: mars[#Somewhere#];
185187
// UNRESOLVED_OPT_4-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): Somewhere#})[#(into: inout Hasher) -> Void#];
@@ -190,6 +192,7 @@ func testOptionalWithCustomExtension() {
190192
// UNRESOLVED_OPT_4-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Convertible]: nowhere[#Optional<Somewhere>#]; name=nowhere
191193
// UNRESOLVED_OPT_4-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: map({#(self): Optional<Somewhere>#})[#((Somewhere) throws(Error) -> ~Copyable) -> ~Copyable?#];
192194
// UNRESOLVED_OPT_4-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: flatMap({#(self): Optional<Somewhere>#})[#((Somewhere) throws(Error) -> ~Copyable?) -> ~Copyable?#];
195+
// UNRESOLVED_OPT_4-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: take({#(self): &Optional<Somewhere>#})[#() -> Optional<Somewhere>#];
193196
// UNRESOLVED_OPT_4-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem/TypeRelation[Invalid]: hash({#(self): Optional<Somewhere>#})[#(into: inout Hasher) -> Void#];
194197
// UNRESOLVED_OPT_4-NOT: init({#(some):
195198
// UNRESOLVED_OPT_4-NOT: init({#nilLiteral:
@@ -687,7 +690,7 @@ func testSameType() {
687690

688691
testSugarType(.#^SUGAR_TYPE^#
689692
// Ensure results aren't duplicated.
690-
// SUGAR_TYPE: Begin completions, 9 items
693+
// SUGAR_TYPE: Begin completions, 10 items
691694
// SUGAR_TYPE-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: South[#SomeEnum1#];
692695
// SUGAR_TYPE-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: North[#SomeEnum1#];
693696
// SUGAR_TYPE-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#];
@@ -696,6 +699,7 @@ func testSameType() {
696699
// SUGAR_TYPE-DAG: Decl[EnumElement]/CurrNominal/IsSystem/TypeRelation[Convertible]: some({#SomeEnum1#})[#Optional<SomeEnum1>#];
697700
// SUGAR_TYPE-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: map({#(self): Optional<SomeEnum1>#})[#((SomeEnum1) throws(Error) -> ~Copyable) -> ~Copyable?#];
698701
// SUGAR_TYPE-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: flatMap({#(self): Optional<SomeEnum1>#})[#((SomeEnum1) throws(Error) -> ~Copyable?) -> ~Copyable?#];
702+
// SUGAR_TYPE-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: take({#(self): &Optional<SomeEnum1>#})[#() -> Optional<SomeEnum1>#];
699703
// SUGAR_TYPE-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem/TypeRelation[Invalid]: hash({#(self): Optional<SomeEnum1>#})[#(into: inout Hasher) -> Void#];
700704
}
701705

0 commit comments

Comments
 (0)