Skip to content

Weaken some type checks for @preconcurrency decls #41386

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 3 commits into from
Mar 2, 2022

Conversation

beccadax
Copy link
Contributor

In an async context, trying to pass a non-@Sendable function to an @Sendable parameter or trying to assign a @MainActor method to a non-@MainActor-typed variable were hard errors. We now think that this a mistake for @preconcurrency APIs in Swift 5 mode, as it hinders retroactive adoption of @Sendable and @MainActor by libraries.

This PR weakens these errors to warnings only when the decl which contains the attribute in its type signature is @preconcurrency and only when in Swift 5 mode (with or without -warn-concurrency). For non-@preconcurrency decls, it is still an error.

Fixes rdar://88703266.

In an async context, trying to pass a non-`@Sendable` function to an `@Sendable` parameter or trying to assign a `@MainActor` method to a non-`@MainActor`-typed variable were hard errors. We now think that this a mistake for `@preconcurrency` APIs in Swift 5 mode, as it hinders retroactive adoption of `@Sendable` and `@MainActor` by libraries.

This PR weakens these errors to warnings *only* when the decl which contains the attribute in its type signature is `@preconcurrency` and *only* when in Swift 5 mode (with or without -warn-concurrency). For non-`@preconcurrency` decls, it is still an error.

Fixes <rdar://88703266>.
@beccadax
Copy link
Contributor Author

@swift-ci smoke test

@beccadax beccadax requested a review from DougGregor February 15, 2022 21:44
Copy link
Member

@DougGregor DougGregor left a comment

Choose a reason for hiding this comment

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

This looks great, and is very much how @preconcurrency should work. There another place in isolation checking where we likely want to downgrade from an error to a warning when calling a @preconcurrency function that has a @Sendable parameter, which is actor-isolation checking. For example, I've seen this issue crop up:

@MainActor class C {
  func f() { }

  func g() { 
    someCompletionHandlerAPI {
      f() // when completion handler was non-Sendable, this is okay
          // now that completion handler is Sendable, it's an error. can we downgrade to a warning?
    }
  }
}

If, for instance, an error is emitted as a warning instead, the verifier now detects this and emits a single diagnostic saying that the warning was found but had the wrong kind, instead of emitting one diagnostic saying the error was missing and another saying the warning was unexpected.

In theory there are some edge cases we could handle better by doing two separate passes—one to detect exact expectation matches and remove them, another to detect near-misses and diagnose them—but in practice, I think the text + diagnostic location is likely to be unique enough to keep this from being a problem. (I would hesitate to do wrong-line diagnostics in the same pass like this, though.)
When a closure is not properly actor-isolated, but we know that we inferred its isolation from a `@preconcurrency` declaration, we now emit the errors as warnings in Swift 5 mode to avoid breaking source compatibility if the isolation was added retroactively.
@beccadax beccadax requested a review from DougGregor February 18, 2022 22:55
@beccadax
Copy link
Contributor Author

@swift-ci smoke test

@beccadax
Copy link
Contributor Author

macOS failure looks unrelated.

@swift-ci smoke test macOS platform

@beccadax beccadax requested a review from kavon February 23, 2022 22:41
@@ -86,7 +90,7 @@ func testCallsWithAsync() async {
onMainActorAlways() // expected-error{{expression is 'async' but is not marked with 'await'}}
// expected-note@-1{{calls to global function 'onMainActorAlways()' from outside of its actor context are implicitly asynchronous}}

let _: () -> Void = onMainActorAlways // expected-error{{converting function value of type '@MainActor () -> ()' to '() -> Void' loses global actor 'MainActor'}}
let _: () -> Void = onMainActorAlways // expected-warning{{converting function value of type '@MainActor () -> ()' to '() -> Void' loses global actor 'MainActor'}}
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if we should also allow the assignment of a non-Sendable closure to a property whose type requires that it be sendable, if the property is a member of a pre-concurrency type / module?

Copy link
Member

Choose a reason for hiding this comment

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

This already seems to work on main for Swift 5 without preconcurrency:

struct hello {
  var clos: (@Sendable () -> Void)? = nil
}

func asdf(f: @escaping () -> Void) -> hello {
  var h = hello()
  h.clos = f // warning: assigning non-sendable parameter 'f' to a @Sendable closure
}

Copy link
Member

@kavon kavon left a comment

Choose a reason for hiding this comment

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

LGTM, just one thought above ^

@beccadax
Copy link
Contributor Author

beccadax commented Mar 1, 2022

@swift-ci smoke test and merge

@swift-ci swift-ci merged commit ad90309 into swiftlang:main Mar 2, 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.

4 participants