Skip to content

[SR-15694] make isolation inference + override more consistent #41662

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 8, 2022

Conversation

kavon
Copy link
Member

@kavon kavon commented Mar 4, 2022

During actor isolation inference, we would unconditionally choose the
isolation of the overridden decl (when, say, there is no attribute on the decl).
The overridden decl is identified with getOverriddenDecl.

This works mostly fine, but initializers have some unusual overridden decls
returned by that method. For example, in the following code:

@objc class PictureFrame: NSObject {
  init(size: Int) { }
}

@MainActor
class FooFrame: PictureFrame {
  init() {
    super.init(size: 0)
  }
}

that method claims that FooFrame.init() overrides PictureFrame.init(), when
it really does not! So, if we were to unconditionally take the isolation from
PictureFrame.init() (and thus NSObject.init()), then we'd infer that
FooFrame.init() has unspecified isolation, despite the nominal it resides in
being marked as MainActor. This is in essence the problem in SR-15694, where
placing the isolation directly on the initializer fixes this issue.

If FooFrame.init() really does override, then why can it be MainActor? Well,
we have a rule in one part of the type-checker saying that if an ObjC-imported
decl has unspecified isolation, then overriding it with isolation is permitted.
But the other part of the type-checker dealing with the isolation inference was
not permitting that.

So, this patch unifies how actor-isolation inference is conducted to reuse that
same logic. In addition, the inference now effectively says that, if the decl
has no inferred isolation, or the inferred isolation is invalid according to the
overriding rules, then we use the isolation of the overridden decl. This
preserves the old behavior of the inference, while also fixing this issue with
ObjC declarations by expanding where isolation is allowed.

For example, as a consequence of this change, in the following code:

@MainActor
class FooFrame: NotIsolatedPictureFrame {
  override func rotate() {
    mainActorFn()
  }
}

if NotIsolatedPictureFrame is a plain-old not-isolated class imported from
Objective-C, then rotate will now have MainActor isolation, instead of
having unspecified isolation like it was inferred to have previously. This
helps make things consistent, because rotate is allowed to be @MainActor if
it were explicitly marked as such.

resolves rdar://87217618 / SR-15694

@kavon
Copy link
Member Author

kavon commented Mar 4, 2022

@swift-ci please test


// If the overridden declaration is from Objective-C with no actor annotation,
// allow it.
if (overriddenDecl->hasClangNode() && !overriddenIso)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like the kind of thing we should diagnose with -warn-concurrency and in Swift 6, because it is a hole in the safety model. That can be a follow-on PR, of course.

@kavon kavon force-pushed the cascade-mainactor branch from ea9924e to a0445e4 Compare March 7, 2022 19:38
@kavon
Copy link
Member Author

kavon commented Mar 7, 2022

@swift-ci please test and merge

During actor isolation inference, we would unconditionally choose the
isolation of the overridden decl (when, say, there is no attribute on the decl).
The overridden decl is identified with `getOverriddenDecl`.

This works mostly fine, but initializers have some unusual overridden decls
returned by that method. For example, in the following code:

```swift
@objc class PictureFrame: NSObject {
  init(size: Int) { }
}

@mainactor
class FooFrame: PictureFrame {
  init() {
    super.init(size: 0)
  }
}
```

that method claims that `FooFrame.init()` overrides `PictureFrame.init()`, when
it really does not! So, if we were to unconditionally take the isolation from
`PictureFrame.init()` (and thus `NSObject.init()`), then we'd infer that
`FooFrame.init()` has unspecified isolation, despite the nominal it resides in
being marked as `MainActor`. This is in essence the problem in SR-15694, where
placing the isolation directly on the initializer fixes this issue.

If `FooFrame.init()` really does override, then why can it be `MainActor`? Well,
we have a rule in one part of the type-checker saying that if an ObjC-imported
decl has unspecified isolation, then overriding it with isolation is permitted.
But the other part of the type-checker dealing with the isolation inference was
not permitting that.

So, this patch unifies how actor-isolation inference is conducted to reuse that
same logic. In addition, the inference now effectively says that, if the decl
has no inferred isolation, or the inferred isolation is invalid according to the
overriding rules, then we use the isolation of the overridden decl. This
preserves the old behavior of the inference, while also fixing this issue with
ObjC declarations by expanding where isolation is allowed.

For example, as a consequence of this change, in the following code:

```swift
@mainactor
class FooFrame: NotIsolatedPictureFrame {
  override func rotate() {
    mainActorFn()
  }
}
```

if `NotIsolatedPictureFrame` is a plain-old not-isolated class imported from
Objective-C, then `rotate` will now have `MainActor` isolation, instead of
having unspecified isolation like it was inferred to have previously. This
helps make things consistent, because `rotate` is allowed to be `@MainActor` if
it were explicitly marked as such.

resolves rdar://87217618 / SR-15694
@kavon kavon force-pushed the cascade-mainactor branch from a0445e4 to 3c04f0a Compare March 7, 2022 23:42
@kavon
Copy link
Member Author

kavon commented Mar 7, 2022

@swift-ci please test and merge

@swift-ci swift-ci merged commit 7ca4f88 into swiftlang:main Mar 8, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants