# Effects of explicit nonisolated on isolation inference

**URL:** <https://forums.swift.org/t/effects-of-explicit-nonisolated-on-isolation-inference/82961>\
**Category:** Using Swift\
**Tags:** concurrency\
**Created:** [October 31, 2025, 10:55am UTC](https://forums.swift.org/t/effects-of-explicit-nonisolated-on-isolation-inference/82961 "2025-10-31T10:55:33Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![mattie](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mattie/32/13267_2.png) [@mattie](https://forums.swift.org/u/mattie)\
**Post date:** [October 31, 2025, 10:55am UTC](https://forums.swift.org/t/effects-of-explicit-nonisolated-on-isolation-inference/82961/1 "2025-10-31T10:55:33Z")

</div>

I’m having difficulty explaining some behavior I’ve noticed. The following code compiles. But if you mark **either** protocol as `nonisolated`, it appears to cut off the `MainActor` isolation inference for `MyClass.requirement`.

I find this a little surprising when applied to `A`, and very surprising when applied to `B`.

```swift
// default: nonisolated

protocol A {
	func requirement()
}

protocol B: A {
}

@MainActor
final class MyClass: @MainActor B {
	var state = 1

	init() {
	}

	func requirement() {
		// if either protocol is explicitly nonisolated:
		// Error: Main actor-isolated property 'state' can not be referenced from a nonisolated context
		print(state)
	}
}

```

This came up in the context of using default `MainActor`, which can motivate adding explicit `nonisolated` where you otherwise wouldn’t need it.

I’ve re-read [SE-0449](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0449-nonisolated-for-global-actor-cutoff.md), but I wasn’t able to find a clear justification for this behavior. Did I miss something?

---

<div class="post-metadata">

**Author:** ![jamieQ](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jamieq/32/5117_2.png) [@jamieQ](https://forums.swift.org/u/jamieQ)\
**Post date:** [October 31, 2025, 11:57am UTC](https://forums.swift.org/t/effects-of-explicit-nonisolated-on-isolation-inference/82961/2 "2025-10-31T11:57:51Z")

</div>

not an answer, but to further add to the confusion... indirecting through another protocol refinement produces different behavior (which also makes me think there is a bug involved somewhere):

```swift
protocol A {
	func requirement()
}
protocol B: A {}
nonisolated protocol C: B {}

@MainActor
final class MyClass: @MainActor C
{
	var state = 1
	init() {}

	func requirement() {
		print(state) // ✅ – still inferred to be @MainActor
	}
}

```

also, if you declare the conformance via an extension, it appears to behave differently:

```swift
protocol A {
	func requirement()
}
nonisolated protocol B: A {}

@MainActor
final class MyClass
{
	var state = 1
	init() {}

	func requirement() {
		print(state) // ✅
	}
}

extension MyClass: @MainActor B {}

```

i find myself currently struggling to formulate how i think these feature interactions even _should_ work... do you feel you have a sense for that? my intuition is that the explicit isolated conformance being written on the class declaration should 'get priority' over the protocols being marked `nonisolated`.

cc @hborla @Douglas_Gregor – curious if either of you could shed light on what's happening in these cases, or what the intended interaction between isolated conformances and 'global actor inference cutoff' should be.

---

<div class="post-metadata">

**Author:** ![hborla](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/hborla/32/27563_2.png) [@hborla](https://forums.swift.org/u/hborla)\
**Post date:** [November 7, 2025, 9:22pm UTC](https://forums.swift.org/t/effects-of-explicit-nonisolated-on-isolation-inference/82961/3 "2025-11-07T21:22:28Z")

</div>

I'm glad you brought this up! I've been meaning to revisit some of the longstanding quirks of how explicit `nonisolated` modifiers behave. I believe this is due to a discrepancy with how explicit `nonisolated` works when written on protocol requirements that has always existed as far as I can tell. Here's an example, showing the Swift 6.0 behavior before we had isolated conformances:

```swift
// compiling with Swift 6.0 under '-language-mode 6'

protocol P {
  nonisolated func f()
}

protocol Q {
  func g()
}

@MainActor struct S1: P {
  // Okay, 'f' is inferred to be 'nonisolated'
  func f() {}
}

@MainActor struct S2: Q {
  // Error, 'g' is inferred to be '@MainActor' and can't satisfy a 'nonisolated' requirement
  func g() {}
}

```

With isolated conformances in Swift 6.2, both conformances compile, but `S1` has a nonisolated conformance to `P` and `S2` has a main actor isolated conformance to `Q`.

I find this behavior extremely surprising. Both protocol requirements _are_ `nonisolated`, and whether or not you write it explicitly shouldn't be load bearing for how inference works on the conforming type.

Isolation inference for class overrides is not different between explicit vs implicit `nonisolated`, but it's still surprising:

```swift
class C1 {
  nonisolated func f() {}
}

class C2 {
  func g() {}
}

@MainActor class D1: C1 {
  // Okay, 'f' is inferred to be 'nonisolated'
  override func f() {}
}

@MainActor class D2: C2 {
    // Okay, 'g' is inferred to be 'nonisolated'
  override func g() {}
}

```

For class overrides, an explicit `nonisolated` does not make a difference, and `nonisolated` is always inferred on the overridden method if the superclass method is `nonisolated`. If we want to have the notion of [isolated subclasses and overrides](https://github.com/swiftlang/swift-evolution/blob/main/visions/approachable-concurrency.md#isolated-subclasses-and-overrides), this is not the inference rule that we would want.

The representation of actor isolation in the compiler does distinguish between "default isolation" and "nonisolated", which is how the compiler treats these cases differently. I have no idea how protocol refinement is somehow messing with whether the original requirement is default isolated or nonisolated, but my guess is there's some funny behavior that tries to cut off isolation inference in a case like this:

```swift
@MainActor protocol A {
  func f()
}

nonisolated protocol B: A {}

struct S: B {
  // infer 'f' as 'nonisolated'
  func f()
}

```

My opinion is that we should make the semantics consistent regardless of whether `nonisolated` is explicit or inferred via the default isolation, and unify these cases in the implementation as well. In your example, I think `func requirement()` inside `@MainActor final class MyClass` should always have`@MainActor` inferred from the context, and never have `nonisolated` inferred from the protocol requirement it satisfies. This is actually implemented behind an experimental feature ([`NoExplicitNonisolated`](https://github.com/swiftlang/swift/blob/main/include/swift/Basic/Features.def#L351)) that "just" needs some source compatibility testing to determine whether we really need an upcoming feature to fix these bugs! I do plan to write up a proposal for this (and would welcome collaborators!) because as you both mention, the rules are not written down anywhere and they're pretty nuanced.

> [@jamieQ](#):
>
> also, if you declare the conformance via an extension, it appears to behave differently:

This is just because isolation inference from protocol requirements only happens if the implementation is written in the same scope as the conformance.

---

<div class="post-metadata">

**Author:** ![Panajev](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/panajev/32/1227_2.png) [@Panajev](https://forums.swift.org/u/Panajev)\
**Post date:** [November 8, 2025, 7:53am UTC](https://forums.swift.org/t/effects-of-explicit-nonisolated-on-isolation-inference/82961/4 "2025-11-08T07:53:42Z")

</div>

> [@hborla](#):
>
> This is just because isolation inference from protocol requirements only happens if the implementation is written in the same scope as the conformance.

Hello Holly, do you think this would change too then (for consistency)?
