Skip to content

[REQ][swift6] Option to mark generated declarations nonisolated for projects using default MainActor isolation #24903

Description

@danieldickison

Is your feature request related to a problem? Please describe.

Swift 6.2 lets a module opt into main-actor isolation by default (SE-0466, -default-isolation MainActor; in Xcode 26 the SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor build setting, which the new-project template enables). Under that setting, every top-level declaration without an explicit isolation is inferred @MainActor, including the public struct / public enum models the swift6 generator emits, their Codable conformances, and the extension blocks it writes for them. The generated models then cannot be used from any nonisolated context, which is where decoding normally happens (a URLSession completion, an actor-based API client, a background Task). The diagnostics take the form (Xcode 26, Swift 6.2):

conformance of 'Foo' to protocol 'Decodable' crosses into main actor-isolated code and can cause data races
main actor-isolated initializer 'init(from:)' cannot be used to satisfy nonisolated requirement from protocol 'Decodable'

Describe the solution you'd like

A swift6 generator option, for example nonisolatedModels (boolean), that when enabled emits:

  • public nonisolated struct Foo: Codable, … / public nonisolated enum Foo: String, … for every top-level model
  • nonisolated extension Foo: … for every conformance extension the generator writes (e.g. the CaseIterableDefaultsLast / UnknownCaseCheckable extensions produced under enumUnknownDefaultCase, and the oneOf enum wrappers)
  • the same on the Infrastructure/*.swift protocols and their default-implementation extensions, since a nonisolated type cannot satisfy a main-actor-isolated protocol requirement, nor take a main-actor default implementation as its witness

nonisolated extension requires Swift 6.2, so the option should probably be documented as requiring a Swift 6.2 toolchain, or emit per-member nonisolated when it is off.

Always emitting nonisolated (without the config option) would also be correct for these types, since it changes nothing for consumers on the default isolation.

Describe alternatives you've considered

  • additionalModelObjectAttributes: this hook inserts text on the model struct/enum line, but it does not reach the oneOf wrapper enums, inline property enums, or the conformance extension blocks, and it cannot touch the Infrastructure protocols. So a project cannot get a compiling result from it alone.
  • A textual post-pass over the generated files (what we do today): a regex rewriting ^public (struct|enum) and ^extension , plus a hand-written copy of the two Infrastructure protocols with nonisolated added. It works, but it is fragile against template changes.
  • Wrapping every use of a model in MainActor.run or making the API client @MainActor: pushes decoding onto the main thread, which incurs unnecessary runtime costs.

Additional context

This is the next step after #20057: that issue made the models Sendable so they can cross an actor boundary; default MainActor isolation additionally pins them to one unless they are declared nonisolated. The generated code is plain value types with no shared mutable state, so there is no reason for it to carry actor isolation. Marking the declarations nonisolated is the correct annotation regardless of the consumer's default-isolation setting, and it is a no-op for modules that keep the Swift 6.0/6.1 default (nonisolated).

Reproduction

Generate any spec with -g swift6 --global-property models, add the output to an app target with SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor and SWIFT_VERSION = 6, and decode a model from a nonisolated function:

nonisolated func decode(_ data: Data) throws -> Pet {
    try JSONDecoder().decode(Pet.self, from: data)   // error: main actor-isolated initializer 'init(from:)' …
}

Generator version: 7.25.0.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions