Hi,
Over the past few days, I have been looking into getting my apps work in Swift 6 language mode. This went mostly great! However, one of my apps was crashing in Swift 6 lanaguage mode, while it runs fine in Swift 5 language mode. After doing some digging, I found the culprit. In the AppKit SDK shipping with Xcode 16, NSDocument
is marked as @MainActor
. However, some of its functions can be called from another thread, such as the autosavesInPlace
, which was causing problems in my case. This function is not marked as nonisolated, so it will incorrectly inherit the MainActor from NSDocument (FB13874012). In Swift 6 language mode, the code will correctly crash. In Swift 5 language mode, it won't, even though it is called from another thread. My question is, why is that? I would expect the code to crash in both cases.
@MainActor
class FileDocument: NSDocument {
...
// Incorrect
override class var autosavesInPlace: Bool {
true
}
// |
// |
// v
// Correct
override nonisolated class var autosavesInPlace: Bool {
true
}
}
Stack trace:
* thread #5, queue = 'com.apple.root.default-qos', stop reason = EXC_BREAKPOINT (code=1, subcode=0x1004f8a5c)
* frame #0: 0x00000001004f8a5c libdispatch.dylib`_dispatch_assert_queue_fail + 120
frame #1: 0x00000001004f89e4 libdispatch.dylib`dispatch_assert_queue + 196
frame #2: 0x0000000275bd293c libswift_Concurrency.dylib`swift_task_isCurrentExecutorImpl(swift::SerialExecutorRef) + 280
frame #3: 0x0000000275b78588 libswift_Concurrency.dylib`Swift._checkExpectedExecutor(_filenameStart: Builtin.RawPointer, _filenameLength: Builtin.Word, _filenameIsASCII: Builtin.Int1, _line: Builtin.Word, _executor: Builtin.Executor) -> () + 60
frame #4: 0x0000000108922788 MyApp.debug.dylib`@objc static FileDocument.autosavesInPlace.getter at <compiler-generated>:0
frame #5: 0x00000001a2f7d780 AppKit`-[NSDocument(NSDocument_Versioning) _preserveContentsIfNecessaryAfterWriting:toURL:forSaveOperation:version:error:] + 92
frame #6: 0x00000001a2fe6d1c AppKit`__85-[NSDocument(NSDocumentSaving) _saveToURL:ofType:forSaveOperation:completionHandler:]_block_invoke_2.398 + 116
frame #7: 0x00000001004f4bd4 libdispatch.dylib`_dispatch_call_block_and_release + 32
frame #8: 0x00000001004f6914 libdispatch.dylib`_dispatch_client_callout + 20
frame #9: 0x00000001004f9878 libdispatch.dylib`_dispatch_queue_override_invoke + 1076
frame #10: 0x000000010050e9f8 libdispatch.dylib`_dispatch_root_queue_drain + 404
frame #11: 0x000000010050f604 libdispatch.dylib`_dispatch_worker_thread2 + 188
frame #12: 0x00000001005910c4 libsystem_pthread.dylib`_pthread_wqthread + 228