Enable strict memory safety in the Distributed module

This commit is contained in:
Doug Gregor
2025-02-22 07:56:57 -10:00
parent 177d16ced8
commit d9b5a4621f
7 changed files with 50 additions and 48 deletions

View File

@@ -52,6 +52,7 @@ add_swift_target_library(swiftDistributed ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS
SWIFT_COMPILE_FLAGS
${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS}
-parse-stdlib
-strict-memory-safety
LINK_FLAGS
"${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}"

View File

@@ -406,10 +406,10 @@ extension DistributedActor {
@_implements(Actor, unownedExecutor)
public nonisolated var __actorUnownedExecutor: UnownedSerialExecutor {
if #available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *) {
return unownedExecutor
return unsafe unownedExecutor
} else {
// On older platforms, all distributed actors are default actors.
return UnownedSerialExecutor(Builtin.buildDefaultActorExecutorRef(self))
return unsafe UnownedSerialExecutor(Builtin.buildDefaultActorExecutorRef(self))
}
}

View File

@@ -438,8 +438,8 @@ extension DistributedActorSystem {
// Gen the generic environment (if any) associated with the target.
let genericEnv =
targetNameUTF8.withUnsafeBufferPointer { targetNameUTF8 in
_getGenericEnvironmentOfDistributedTarget(
unsafe targetNameUTF8.withUnsafeBufferPointer { targetNameUTF8 in
unsafe _getGenericEnvironmentOfDistributedTarget(
targetNameUTF8.baseAddress!,
UInt(targetNameUTF8.endIndex))
}
@@ -449,11 +449,11 @@ extension DistributedActorSystem {
var numWitnessTables: Int = 0
defer {
substitutionsBuffer?.deallocate()
witnessTablesBuffer?.deallocate()
unsafe substitutionsBuffer?.deallocate()
unsafe witnessTablesBuffer?.deallocate()
}
if let genericEnv = genericEnv {
if let genericEnv = unsafe genericEnv {
let subs = try invocationDecoder.decodeGenericSubstitutions()
if subs.isEmpty {
throw ExecuteDistributedTargetError(
@@ -461,14 +461,14 @@ extension DistributedActorSystem {
errorCode: .missingGenericSubstitutions)
}
substitutionsBuffer = .allocate(capacity: subs.count)
unsafe substitutionsBuffer = .allocate(capacity: subs.count)
for (offset, substitution) in subs.enumerated() {
let element = substitutionsBuffer?.advanced(by: offset)
element?.initialize(to: substitution)
let element = unsafe substitutionsBuffer?.advanced(by: offset)
unsafe element?.initialize(to: substitution)
}
(witnessTablesBuffer, numWitnessTables) = _getWitnessTablesFor(environment: genericEnv,
unsafe (witnessTablesBuffer, numWitnessTables) = unsafe _getWitnessTablesFor(environment: genericEnv,
genericArguments: substitutionsBuffer!)
if numWitnessTables < 0 {
throw ExecuteDistributedTargetError(
@@ -478,8 +478,8 @@ extension DistributedActorSystem {
}
let paramCount =
targetNameUTF8.withUnsafeBufferPointer { targetNameUTF8 in
__getParameterCount(
unsafe targetNameUTF8.withUnsafeBufferPointer { targetNameUTF8 in
unsafe __getParameterCount(
targetNameUTF8.baseAddress!,
UInt(targetNameUTF8.endIndex))
}
@@ -497,12 +497,12 @@ extension DistributedActorSystem {
// Prepare buffer for the parameter types to be decoded into:
let argumentTypesBuffer = UnsafeMutableBufferPointer<Any.Type>.allocate(capacity: Int(paramCount))
defer {
argumentTypesBuffer.deallocate()
unsafe argumentTypesBuffer.deallocate()
}
// Demangle and write all parameter types into the prepared buffer
let decodedNum = targetNameUTF8.withUnsafeBufferPointer { targetNameUTF8 in
__getParameterTypeInfo(
let decodedNum = unsafe targetNameUTF8.withUnsafeBufferPointer { targetNameUTF8 in
unsafe __getParameterTypeInfo(
targetNameUTF8.baseAddress!,
UInt(targetNameUTF8.endIndex),
genericEnv,
@@ -525,7 +525,7 @@ extension DistributedActorSystem {
var argumentTypes: [Any.Type] = []
do {
argumentTypes.reserveCapacity(Int(decodedNum))
for argumentType in argumentTypesBuffer {
for unsafe argumentType in unsafe argumentTypesBuffer {
argumentTypes.append(argumentType)
}
}
@@ -536,8 +536,8 @@ extension DistributedActorSystem {
}
let maybeReturnTypeFromTypeInfo =
targetNameUTF8.withUnsafeBufferPointer { targetNameUTF8 in
__getReturnTypeInfo(
unsafe targetNameUTF8.withUnsafeBufferPointer { targetNameUTF8 in
unsafe __getReturnTypeInfo(
/*targetName:*/targetNameUTF8.baseAddress!,
/*targetLength:*/UInt(targetNameUTF8.endIndex),
/*genericEnv:*/genericEnv,
@@ -549,7 +549,7 @@ extension DistributedActorSystem {
errorCode: .typeDeserializationFailure)
}
guard let resultBuffer = _openExistential(returnTypeFromTypeInfo, do: doAllocateReturnTypeBuffer) else {
guard let resultBuffer = _openExistential(returnTypeFromTypeInfo, do: unsafe doAllocateReturnTypeBuffer) else {
throw ExecuteDistributedTargetError(
message: "Failed to allocate buffer for distributed target return type",
errorCode: .typeDeserializationFailure)
@@ -560,16 +560,16 @@ extension DistributedActorSystem {
var executeDistributedTargetHasThrown = true
func doDestroyReturnTypeBuffer<R>(_: R.Type) {
let buf = resultBuffer.assumingMemoryBound(to: R.self)
let buf = unsafe resultBuffer.assumingMemoryBound(to: R.self)
if !executeDistributedTargetHasThrown {
// since the _execute function has NOT thrown,
// there must be a value in the result buffer that we must deinitialize
buf.deinitialize(count: 1)
unsafe buf.deinitialize(count: 1)
} // otherwise, the _execute has thrown and not populated the result buffer
// finally, deallocate the buffer
buf.deallocate()
unsafe buf.deallocate()
}
defer {
@@ -581,7 +581,7 @@ extension DistributedActorSystem {
// let errorType = try invocationDecoder.decodeErrorType() // TODO(distributed): decide how to use when typed throws are done
// Execute the target!
try await _executeDistributedTarget(
try unsafe await _executeDistributedTarget(
on: actor,
/*targetNameData:*/targetName,
/*targetNameLength:*/UInt(targetName.count),
@@ -599,7 +599,7 @@ extension DistributedActorSystem {
if returnType == Void.self {
try await handler.onReturnVoid()
} else {
try await self.invokeHandlerOnReturn(
try unsafe await self.invokeHandlerOnReturn(
handler: handler,
resultBuffer: resultBuffer,
metatype: returnType

View File

@@ -51,11 +51,11 @@ extension DistributedActor {
return
}
let unownedExecutor = self.unownedExecutor
let expectationCheck = _taskIsCurrentExecutor(unownedExecutor._executor)
let unownedExecutor = unsafe self.unownedExecutor
let expectationCheck = unsafe _taskIsCurrentExecutor(unownedExecutor._executor)
precondition(expectationCheck,
"Incorrect actor executor assumption; Expected '\(self.unownedExecutor)' executor. \(message())",
unsafe "Incorrect actor executor assumption; Expected '\(unsafe self.unownedExecutor)' executor. \(message())",
file: file, line: line)
}
}
@@ -99,9 +99,9 @@ extension DistributedActor {
return
}
let unownedExecutor = self.unownedExecutor
guard _taskIsCurrentExecutor(unownedExecutor._executor) else {
let msg = "Incorrect actor executor assumption; Expected '\(unownedExecutor)' executor. \(message())"
let unownedExecutor = unsafe self.unownedExecutor
guard unsafe _taskIsCurrentExecutor(unownedExecutor._executor) else {
let msg = unsafe "Incorrect actor executor assumption; Expected '\(unsafe unownedExecutor)' executor. \(message())"
/// TODO: implement the logic in-place perhaps rather than delegating to precondition()?
assertionFailure(msg, file: file, line: line) // short-cut so we get the exact same failure reporting semantics
return
@@ -165,8 +165,8 @@ extension DistributedActor {
fatalError("Cannot assume to be 'isolated \(Self.self)' since distributed actor '\(self)' is a remote actor reference.")
}
let unownedExecutor = self.unownedExecutor
guard _taskIsCurrentExecutor(unownedExecutor._executor) else {
let unownedExecutor = unsafe self.unownedExecutor
guard unsafe _taskIsCurrentExecutor(unownedExecutor._executor) else {
// TODO: offer information which executor we actually got when
fatalError("Incorrect actor executor assumption; Expected same executor as \(self).", file: file, line: line)
}
@@ -174,7 +174,7 @@ extension DistributedActor {
// To do the unsafe cast, we have to pretend it's @escaping.
return try withoutActuallyEscaping(operation) {
(_ fn: @escaping YesActor) throws -> T in
let rawFn = unsafeBitCast(fn, to: NoActor.self)
let rawFn = unsafe unsafeBitCast(fn, to: NoActor.self)
return try rawFn(self)
}
}

View File

@@ -18,7 +18,7 @@ import _Concurrency
internal final class DistributedRemoteActorReferenceExecutor: SerialExecutor {
static let _shared: DistributedRemoteActorReferenceExecutor = DistributedRemoteActorReferenceExecutor()
static var sharedUnownedExecutor: UnownedSerialExecutor {
UnownedSerialExecutor(ordinary: _shared)
unsafe UnownedSerialExecutor(ordinary: _shared)
}
internal init() {}
@@ -38,7 +38,7 @@ internal final class DistributedRemoteActorReferenceExecutor: SerialExecutor {
#endif // !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
public func asUnownedSerialExecutor() -> UnownedSerialExecutor {
UnownedSerialExecutor(ordinary: self)
unsafe UnownedSerialExecutor(ordinary: self)
}
}
@@ -57,5 +57,5 @@ internal final class DistributedRemoteActorReferenceExecutor: SerialExecutor {
public func buildDefaultDistributedRemoteActorExecutor<Act>(
_ actor: Act
) -> UnownedSerialExecutor where Act: DistributedActor {
return DistributedRemoteActorReferenceExecutor.sharedUnownedExecutor
return unsafe DistributedRemoteActorReferenceExecutor.sharedUnownedExecutor
}

View File

@@ -19,8 +19,8 @@ import Swift
public // SPI Distributed
func _getParameterCount(mangledMethodName name: String) -> Int32 {
let nameUTF8 = Array(name.utf8)
return nameUTF8.withUnsafeBufferPointer { nameUTF8 in
return __getParameterCount(
return unsafe nameUTF8.withUnsafeBufferPointer { nameUTF8 in
return unsafe __getParameterCount(
nameUTF8.baseAddress!, UInt(nameUTF8.endIndex))
}
}
@@ -47,8 +47,8 @@ func _getParameterTypeInfo(
into typesBuffer: Builtin.RawPointer, length typesLength: Int
) -> Int32 {
let nameUTF8 = Array(name.utf8)
return nameUTF8.withUnsafeBufferPointer { nameUTF8 in
return __getParameterTypeInfo(
return unsafe nameUTF8.withUnsafeBufferPointer { nameUTF8 in
return unsafe __getParameterTypeInfo(
nameUTF8.baseAddress!, UInt(nameUTF8.endIndex),
genericEnv, genericArguments, typesBuffer, typesLength)
}
@@ -75,8 +75,8 @@ func _getReturnTypeInfo(
genericArguments: UnsafeRawPointer?
) -> Any.Type? {
let nameUTF8 = Array(name.utf8)
return nameUTF8.withUnsafeBufferPointer { nameUTF8 in
return __getReturnTypeInfo(nameUTF8.baseAddress!, UInt(nameUTF8.endIndex),
return unsafe nameUTF8.withUnsafeBufferPointer { nameUTF8 in
return unsafe __getReturnTypeInfo(nameUTF8.baseAddress!, UInt(nameUTF8.endIndex),
genericEnv, genericArguments)
}
}

View File

@@ -236,6 +236,7 @@ public struct LocalTestingDistributedActorSystemError: DistributedActorSystemErr
// === lock ----------------------------------------------------------------
@available(SwiftStdlib 5.7, *)
@safe
fileprivate class _Lock {
#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS)
private let underlying: UnsafeMutablePointer<os_unfair_lock>
@@ -252,7 +253,7 @@ fileprivate class _Lock {
init() {
#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS)
self.underlying = UnsafeMutablePointer.allocate(capacity: 1)
self.underlying.initialize(to: os_unfair_lock())
unsafe self.underlying.initialize(to: os_unfair_lock())
#elseif os(Windows)
self.underlying = UnsafeMutablePointer.allocate(capacity: 1)
InitializeSRWLock(self.underlying)
@@ -280,8 +281,8 @@ fileprivate class _Lock {
#endif
#if !os(WASI)
self.underlying.deinitialize(count: 1)
self.underlying.deallocate()
unsafe self.underlying.deinitialize(count: 1)
unsafe self.underlying.deallocate()
#endif
}
@@ -289,7 +290,7 @@ fileprivate class _Lock {
@discardableResult
func withLock<T>(_ body: () -> T) -> T {
#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS)
os_unfair_lock_lock(self.underlying)
unsafe os_unfair_lock_lock(self.underlying)
#elseif os(Windows)
AcquireSRWLockExclusive(self.underlying)
#elseif os(WASI)
@@ -302,7 +303,7 @@ fileprivate class _Lock {
defer {
#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS)
os_unfair_lock_unlock(self.underlying)
unsafe os_unfair_lock_unlock(self.underlying)
#elseif os(Windows)
ReleaseSRWLockExclusive(self.underlying)
#elseif os(WASI)