-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[Sema]: Fix data race safety hole with mutable statics within actors #78652
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
Conversation
auto *classDecl = var->getDeclContext()->getSelfClassDecl(); | ||
const bool isActorType = classDecl && classDecl->isAnyActor(); | ||
if (var->isGlobalStorage() && !isActorType) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the primary logical change is the removal of this isActorType
condition. i'm curious if this seems like the appropriate solution to this issue to those more familiar with this code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that there is no reason to check whether the enclosing type is an actor type in this code. If a variable is static, it doesn't matter whether or not it's inside an actor - as you've noted, actor instance isolation can't apply here.
if (auto *originalVar = var->getOriginalWrappedProperty()) { | ||
diagVar = originalVar; | ||
} | ||
if (isolation.isGlobalActor() || isolation.isNonisolatedUnsafe()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor change here to use the isNonisolatedUnsafe()
accessor vs what was previously being done.
tangent: if someone cares to enlighten me as to whether these two formulations are actually the same i'd be curious to learn more about exactly why/how that is. the first case seems like a class is being compared to a 'Kind' which i don't entirely understand.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the reason the comparison between ActorIsolation
and ActorIsolation::Kind
works is because there's an operator ()
on ActorIsolation
that returns its kind:
operator Kind() const { return getKind(); }
This effectively enables implicit conversions from ActorIsolation
to ActorIsoaltion::Kind
; I believe operator ()
was implicitly called on ActorIsolation
in the call to ==
/ !=
, so it was really just a comparison between two ActorIsolation::Kind
s.
@@ -8,7 +8,7 @@ | |||
|
|||
@globalActor | |||
actor TestGlobalActor { | |||
static var shared = TestGlobalActor() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this code is now diagnosed as invalid since shared
is global, mutable, non-isolated state. presumably this means this change could be a source-break – what is the stance on that when it comes to addressing bugs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's okay to fix this as an error in Swift 6 mode - we can adjust if we find this causes wide spread source incompatibility amongst Swift 6 mode adopters through testing. These diagnostics are already warnings in strict concurrency checking / using the GlobalConcurrency
upcoming feature prior to migrating to the Swift 6 language mode.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you!
@swift-ci please smoke test |
hmm... seems my refactoring of some of the early exit conditions to occur before the |
Update the concurrency typechecking logic to remove a check that allowed mutable static variable declarations nested within an Actor type to be ignored when diagnosing mutable non-Sendable state.
fa36959
to
f1a1998
Compare
@hborla i pushed a fix (and rebased) for the failed CI test. would you mind kicking off another build when you have a moment please? |
@swift-ci please smoke test |
Huh, I'd love to investigate this to figure out why (separately from your fix here). I thought your refactoring to add the early exits was much easier to follow! |
i'll leave some notes based on the brief time i spent looking into it today. the refactoring commit that adjusted the early exit conditions caused the
this seemed to be due to my original change which moved the call to compute actor isolation to only conditionally occur if there was a valid source loc & the global concurrency feature was enabled (it originally occurred unconditionally like this). it seems there may be something load-bearing about the downstream |
CI failures seemed unrelated to this change and might be resolved now that this merged? #79415 |
@swift-ci please smoke test |
To fix a concurrency error that was surfaced by swiftlang/swift#78652.
@swift-ci please smoke test |
@swift-ci please test source compatibility |
@swift-ci please smoke test Linux |
1 similar comment
@swift-ci please smoke test Linux |
@jamieQ Thank you for the contribution! Please feel free to ping me if you decide to pick up more in the concurrency area and have questions or need guidance 😄 |
@hborla thank you for helping get this through CI – much appreciated! |
Issue
the existing logic in
checkGlobalIsolation
ignored global variables nested within actor types when diagnosing global isolation during typechecking. this left open a data race safety hole, as described here.Description
checkGlobalIsolation
checkGlobalIsolation
implementation (separate commit)i'd recommend using the 'hide whitespace' option if reviewing the full diff via GitHub as it makes it somewhat easier to parse.
resolves #78435