diff --git a/stdlib/public/Concurrency/Executor.swift b/stdlib/public/Concurrency/Executor.swift index 91236cfb9165d..939100d17d6e2 100644 --- a/stdlib/public/Concurrency/Executor.swift +++ b/stdlib/public/Concurrency/Executor.swift @@ -808,7 +808,13 @@ public struct UnownedSerialExecutor: Sendable { @available(StdlibDeploymentTarget 6.2, *) public func asSerialExecutor() -> (any SerialExecutor)? { - return unsafe unsafeBitCast(executor, to: (any SerialExecutor)?.self) + // The low bits of the witness table are used to encode the executor kind. + // any SerialExecutor needs a raw witness table pointer, so mask off the low + // bits, knowing the witness table pointer is aligned to the pointer size. + let rawExecutorData = unsafe unsafeBitCast(executor, to: (UInt, UInt).self) + let mask = ~(UInt(MemoryLayout.alignment) - 1) + let alignedExecutor = unsafe (rawExecutorData.0, rawExecutorData.1 & mask) + return unsafe unsafeBitCast(alignedExecutor, to: (any SerialExecutor)?.self) } } diff --git a/test/Concurrency/Runtime/custom_executors_complex_equality.swift b/test/Concurrency/Runtime/custom_executors_complex_equality.swift index b07ab1cab8fe1..6bf38184ec9e4 100644 --- a/test/Concurrency/Runtime/custom_executors_complex_equality.swift +++ b/test/Concurrency/Runtime/custom_executors_complex_equality.swift @@ -66,6 +66,11 @@ actor MyActor { func test(expectedExecutor: NaiveQueueExecutor, expectedQueue: DispatchQueue) { expectedExecutor.preconditionIsolated("Expected deep equality to trigger for \(expectedExecutor) and our \(self.executor)") + + // Ensure we get a usable value from asSerialExecutor() when the executor + // has complex equality. + _ = expectedExecutor.asUnownedSerialExecutor().asSerialExecutor()!.asUnownedSerialExecutor() + print("\(Self.self): [\(self.executor.name)] on same context as [\(expectedExecutor.name)]") } }