Skip to content

Commit

Permalink
Merge pull request #58 from lorentey/fix-non-final-class-warnings
Browse files Browse the repository at this point in the history
Fix support for non-final classes in AtomicReference
  • Loading branch information
lorentey authored Mar 20, 2023
2 parents 81f9344 + 907ce2a commit ca8a8fc
Show file tree
Hide file tree
Showing 8 changed files with 269 additions and 90 deletions.
7 changes: 5 additions & 2 deletions Sources/Atomics/AtomicInteger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift Atomics open source project
//
// Copyright (c) 2020 Apple Inc. and the Swift project authors
// Copyright (c) 2020-2023 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
Expand All @@ -21,7 +21,10 @@
/// exchange operation; however, this depends on the capabilities of
/// the compiler and the underlying hardware.
public protocol AtomicInteger: AtomicValue, FixedWidthInteger
where AtomicRepresentation: AtomicIntegerStorage {}
where
AtomicRepresentation: AtomicIntegerStorage,
AtomicRepresentation.Value == Self
{}

/// The storage representation for an atomic integer value, providing
/// pointer-based atomic operations.
Expand Down
10 changes: 7 additions & 3 deletions Sources/Atomics/AtomicOptional.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift Atomics open source project
//
// Copyright (c) 2020 Apple Inc. and the Swift project authors
// Copyright (c) 2020-2023 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
Expand All @@ -17,9 +17,13 @@
public protocol AtomicOptionalWrappable: AtomicValue {
/// The atomic storage representation for `Optional<Self>`.
associatedtype AtomicOptionalRepresentation: AtomicStorage
where AtomicOptionalRepresentation.Value == Self?
where AtomicOptionalRepresentation.Value == AtomicRepresentation.Value?
}

extension Optional: AtomicValue where Wrapped: AtomicOptionalWrappable {
extension Optional: AtomicValue
where
Wrapped: AtomicOptionalWrappable,
Wrapped.AtomicRepresentation.Value == Wrapped
{
public typealias AtomicRepresentation = Wrapped.AtomicOptionalRepresentation
}
41 changes: 27 additions & 14 deletions Sources/Atomics/AtomicRawRepresentable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,41 @@
//
// This source file is part of the Swift Atomics open source project
//
// Copyright (c) 2020 Apple Inc. and the Swift project authors
// Copyright (c) 2020-2023 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
//
//===----------------------------------------------------------------------===//

extension RawRepresentable where Self: AtomicValue, RawValue: AtomicValue {
extension RawRepresentable
where
Self: AtomicValue,
RawValue: AtomicValue,
RawValue.AtomicRepresentation.Value == RawValue
{
public typealias AtomicRepresentation = AtomicRawRepresentableStorage<Self>
}

/// The default atomic storage representation for an atomic `RawRepresentable`
/// type whose `RawValue` conforms to `AtomicValue`.
@frozen
public struct AtomicRawRepresentableStorage<Value>: AtomicStorage
where Value: RawRepresentable, Value.RawValue: AtomicValue {
@usableFromInline internal typealias Storage = Value.RawValue.AtomicRepresentation
where
Value: RawRepresentable,
Value.RawValue: AtomicValue,
Value.RawValue.AtomicRepresentation.Value == Value.RawValue
{
@usableFromInline
internal var _storage: Storage
internal typealias _Storage = Value.RawValue.AtomicRepresentation

@usableFromInline
internal var _storage: _Storage

@_transparent @_alwaysEmitIntoClient
public init(_ value: __owned Value) {
_storage = Storage(value.rawValue)
_storage = _Storage(value.rawValue)
}

@_transparent @_alwaysEmitIntoClient
Expand All @@ -36,9 +47,9 @@ where Value: RawRepresentable, Value.RawValue: AtomicValue {
@_transparent @_alwaysEmitIntoClient
internal static func _extract(
_ ptr: UnsafeMutablePointer<Self>
) -> UnsafeMutablePointer<Storage> {
) -> UnsafeMutablePointer<_Storage> {
// `Self` is layout-compatible with its only stored property.
UnsafeMutableRawPointer(ptr).assumingMemoryBound(to: Storage.self)
UnsafeMutableRawPointer(ptr).assumingMemoryBound(to: _Storage.self)
}

@_semantics("atomics.requires_constant_orderings")
Expand All @@ -47,7 +58,7 @@ where Value: RawRepresentable, Value.RawValue: AtomicValue {
at pointer: UnsafeMutablePointer<Self>,
ordering: AtomicLoadOrdering
) -> Value {
let raw = Storage.atomicLoad(at: _extract(pointer), ordering: ordering)
let raw = _Storage.atomicLoad(at: _extract(pointer), ordering: ordering)
return Value(rawValue: raw)!
}

Expand All @@ -58,7 +69,8 @@ where Value: RawRepresentable, Value.RawValue: AtomicValue {
at pointer: UnsafeMutablePointer<Self>,
ordering: AtomicStoreOrdering
) {
Storage.atomicStore(desired.rawValue, at: _extract(pointer), ordering: ordering)
_Storage.atomicStore(
desired.rawValue, at: _extract(pointer), ordering: ordering)
}

@_semantics("atomics.requires_constant_orderings")
Expand All @@ -68,7 +80,8 @@ where Value: RawRepresentable, Value.RawValue: AtomicValue {
at pointer: UnsafeMutablePointer<Self>,
ordering: AtomicUpdateOrdering
) -> Value {
let raw = Storage.atomicExchange(desired.rawValue, at: _extract(pointer), ordering: ordering)
let raw = _Storage.atomicExchange(
desired.rawValue, at: _extract(pointer), ordering: ordering)
return Value(rawValue: raw)!
}

Expand All @@ -80,7 +93,7 @@ where Value: RawRepresentable, Value.RawValue: AtomicValue {
at pointer: UnsafeMutablePointer<Self>,
ordering: AtomicUpdateOrdering
) -> (exchanged: Bool, original: Value) {
let raw = Storage.atomicCompareExchange(
let raw = _Storage.atomicCompareExchange(
expected: expected.rawValue,
desired: desired.rawValue,
at: _extract(pointer),
Expand All @@ -97,7 +110,7 @@ where Value: RawRepresentable, Value.RawValue: AtomicValue {
successOrdering: AtomicUpdateOrdering,
failureOrdering: AtomicLoadOrdering
) -> (exchanged: Bool, original: Value) {
let raw = Storage.atomicCompareExchange(
let raw = _Storage.atomicCompareExchange(
expected: expected.rawValue,
desired: desired.rawValue,
at: _extract(pointer),
Expand All @@ -115,7 +128,7 @@ where Value: RawRepresentable, Value.RawValue: AtomicValue {
successOrdering: AtomicUpdateOrdering,
failureOrdering: AtomicLoadOrdering
) -> (exchanged: Bool, original: Value) {
let raw = Storage.atomicWeakCompareExchange(
let raw = _Storage.atomicWeakCompareExchange(
expected: expected.rawValue,
desired: desired.rawValue,
at: _extract(pointer),
Expand Down
63 changes: 52 additions & 11 deletions Sources/Atomics/AtomicStrongReference.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift Atomics open source project
//
// Copyright (c) 2020-2021 Apple Inc. and the Swift project authors
// Copyright (c) 2020-2023 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
Expand All @@ -13,17 +13,59 @@
import _AtomicsShims

/// A class type that supports atomic strong references.
///
/// class MyObject: AtomicReference {}
///
/// let object = MyObject()
/// let ref = ManagedAtomic<MyObject>(object)
///
/// ref.load(ordering: .relaxed) // Returns `object`.
///
/// The conforming class is allowed to be non-final, but `ManagedAtomic` and
/// `UnsafeAtomic` do not support using a subclass as their generic argument --
/// the type of an atomic reference must be precisely the same class that
/// originally conformed to the protocol.
///
///
/// class Derived: MyObject {}
///
/// let ref2: ManagedAtomic<Derived>
/// // error: 'ManagedAtomic' requires the types 'Derived' and 'Base' be equivalent
///
/// Note that this limitation only affects the static type of the
/// `ManagedAtomic`/`UnsafeAtomic` variables. Such references still fully
/// support holding instances of subclasses of the conforming class. (Returned
/// may be downcasted from the base type after an `is` check.)
///
/// let child = Derived()
/// ref.store(child, ordering: .relaxed) // OK!
/// let value = ref.load(ordering: .relaxed)
/// // `value` is a variable of type `MyObject`, holding a `Derived` instance.
/// print(value is Derived) // Prints "true"
///
public protocol AtomicReference: AnyObject, AtomicOptionalWrappable
where
AtomicRepresentation == AtomicReferenceStorage<Self>,
AtomicOptionalRepresentation == AtomicOptionalReferenceStorage<Self>
AtomicRepresentation == AtomicReferenceStorage<_AtomicBase>,
AtomicOptionalRepresentation == AtomicOptionalReferenceStorage<_AtomicBase>
{
// These were added as a workaround for https://bugs.swift.org/browse/SR-10251
// FIXME: We should remove these once the package requires a
// compiler version that contains that fix.
override associatedtype AtomicRepresentation = AtomicReferenceStorage<Self>
override associatedtype AtomicOptionalRepresentation =
AtomicOptionalReferenceStorage<Self>
/// This is a utility type that enables non-final classes to conform to
/// `AtomicReference`.
///
/// This associated type must be left at its default value, `Self`.
/// `ManagedAtomic` et al. currently require that `Self == _AtomicBase`,
/// so conformances that set this to anything else will technically build,
/// but they will not be very practical.
///
/// Ideally we could just require that `Self` should be a subtype of
/// `AtomicRepresentation.Value`; however we have no good way to
/// express that requirement.
///
/// protocol AtomicValue where Self: AtomicRepresentation.Value {
/// associatedtype AtomicRepresentation: AtomicStorage
/// }
///
/// See https://github.com/apple/swift-atomics/issues/53 for more details.
associatedtype _AtomicBase: AnyObject = Self
}

/// The maximum number of other threads that can start accessing a
Expand All @@ -45,8 +87,7 @@ extension Unmanaged {
extension Unmanaged {
fileprivate static func passRetained(_ instance: __owned Instance?) -> Self? {
guard let instance = instance else { return nil }
// Note: Swift 5.2 doesn't like this optional promotion without the explicit cast
return .passRetained(instance) as Self
return .passRetained(instance)
}
}

Expand Down
4 changes: 2 additions & 2 deletions Sources/Atomics/AtomicValue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift Atomics open source project
//
// Copyright (c) 2020 Apple Inc. and the Swift project authors
// Copyright (c) 2020-2023 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
Expand All @@ -15,7 +15,7 @@
public protocol AtomicValue {
/// The atomic storage representation for this value.
associatedtype AtomicRepresentation: AtomicStorage
where AtomicRepresentation.Value == Self
/* where Self is a subtype of AtomicRepresentation.Value */
}

/// The storage representation for an atomic value, providing pointer-based
Expand Down
Loading

0 comments on commit ca8a8fc

Please sign in to comment.