# How the Swift compiler knows that DispatchQueue.main implies @MainActor

DevFeed: [How the Swift compiler knows that DispatchQueue.main implies @MainActor](<https://devfeed.tech/articles/how-the-swift-compiler-knows-that-dispatchqueue-main-implies-mainactor-21724.md>)

Original publisher: [Read original article](<https://oleb.net/2024/dispatchqueue-mainactor/>)

Author: Ole Begemann

Published: 2024-02-29T18:54:47Z

Content type: article

Language: en

Sources: [Ole Begemann](<https://devfeed.tech/sources/ole-begemann.md>)

Topics: [Swift](<https://devfeed.tech/topics/swift.md>), [Compiler](<https://devfeed.tech/topics/compiler.md>), [Concurrency](<https://devfeed.tech/topics/concurrency.md>), [Xcode](<https://devfeed.tech/topics/xcode.md>)

Tags: [actor](<https://devfeed.tech/tags/actor.md>), [compiler](<https://devfeed.tech/tags/compiler.md>), [concurrency](<https://devfeed.tech/tags/concurrency.md>), [dispatchqueue](<https://devfeed.tech/tags/dispatchqueue.md>), [swift](<https://devfeed.tech/tags/swift.md>), [xcode](<https://devfeed.tech/tags/xcode.md>)

## AI overview

The article explains that Swift treats closures passed to exactly spelled DispatchQueue.main.async calls as @MainActor-isolated. It reports that this behavior comes from a coarse syntax-based check in the compiler's semantic analysis rather than an annotation on the method, and notes that equivalent expressions using aliases or variables do not receive the same inference.

## Source excerpt

You may have noticed that the Swift compiler automatically treats the closure of a DispatchQueue.main.async call as @MainActor. In other words, we can call a main-actor-isolated function in the closure: import Dispatch @MainActor func mainActorFunc() { } DispatchQueue.main.async { // The compiler lets us call this because // it knows we're on the main actor. mainActorFunc() } This behavior is welcome and very convenient, but it bugs me that it's so hidden. As far as I know it isn't documented, and neither Xcode nor any other editor/IDE I've used do a good job of showing me the actor context a function or closure will run in, even though the compiler has this information. I've written about a similar case before in Where View.task gets its main-actor isolation from, where Swift/Xcode hide essential information from the programmer by not showing certain attributes in declarations or the documentation. It's a syntax check So how is the magic behavior for DispatchQueue.main.async implemented? It can't be an attribute or other annotation on the closure parameter of the DispatchQueue.async method because the actual queue instance isn't known at that point. A bit of experimentation reveals that it is in fact a relatively coarse source-code-based check that singles out invocations on DispatchQueue.main, in exactly that spelling. For example, the following variations do produce warnings/errors (in Swift 5.10/6.0, respectively), even though they are just as safe as the previous code snippet. This is because we aren't using the "correct" DispatchQueue.main.async spelling: let queue = DispatchQueue.main queue.async { // Error: Call to main actor-isolated global function // 'mainActorFunc()' in a synchronous nonisolated context mainActorFunc() // ❌ } typealias DP = DispatchQueue DP.main.async { // Error: Call to main actor-isolated global function // 'mainActorFunc()' in a synchronous nonisolated context mainActorFunc() // ❌ } I found the place in the Swift compiler source code