Skip to content

Conversation

rjmccall
Copy link
Contributor

Follow-up to #84181.

@rjmccall
Copy link
Contributor Author

@swift-ci Please test

Copy link
Contributor

@ktoso ktoso left a comment

Choose a reason for hiding this comment

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

LGTM, thanks for following up on all those John!

take(iso: #isolation)
}
do {}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

thanks for following up on this! i tested one more flavor that still seems to trip an assertion:

func implicitIsolationInheriting(iso: isolated (any Actor)? = #isolation) {}

func isolated_defer(_ iso: isolated (any Actor)) {
    defer {
        implicitIsolationInheriting()
    }
    do {}
}

Copy link
Contributor

Choose a reason for hiding this comment

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

i'm not sure if it's just me, but i also tried running the following example inspired from this bug report and now am a bit confused:

actor MyActor {
    func test(_ closure: nonisolated(nonsending) @escaping () async -> Void) async {
        await closure()
    }
}

@concurrent
nonisolated func test() async {
    let a = MyActor()

    nonisolated(nonsending)
    func localF() async -> Void {
        print("local func")
        let iso = #isolation
        print(iso as Any)       // Optional(main.MyActor)
        a.assertIsolated()      // 💥
    }

    await a.test(localF)

    let fn: nonisolated(nonsending) () async -> Void = {
        print("local var closure literal")
        let iso = #isolation
        print(iso as Any)       // Optional(main.MyActor)
        a.assertIsolated()      // 💥
    }

    await a.test(fn)

    await a.test {
        print("contextual closure literal")
        let iso = #isolation
        print(iso as Any)       // Optional(main.MyActor)
        a.assertIsolated()      // 💥
    }
}

@main
enum App {
    static func main() async {
        await test()
    }
}

prior to the changes (here & in #84181), the #isolation expansion would return nil, but the isolation assertions would pass. now it seems it's the other way around. i find both of these states confusing.

it's possible this is some artifact of my local build configuration, in which case i apologize for the noise. however, i was wondering if anyone else may be able to reproduce this behavior, have an explanation for it, or know if there are other tests covering it.

@rjmccall rjmccall force-pushed the capture-defer-isolation branch from 8c9f0fd to d780b62 Compare September 11, 2025 13:41
@rjmccall
Copy link
Contributor Author

rjmccall commented Sep 11, 2025

@jamieQ I've added a fix for your test case with default arguments. Thanks for being on the ball about this! I should've anticipated that possibly being a problem for capture analysis.

I can verify your crasher. What seems to be happening is that the SIL optimizer is removing the actor hop in MyActor.test prior to the call to its parameter function, and then the functions themselves are not doing the hop either. I believe this might be related to work @gottesmm has been doing to optimize the calling convention around nonisolated(nonsending) functions. It's possible that this was exposed by my patch somehow, though. Do you have a build immediately prior to my patches that observe that test case passing, or are you comparing with one of the beta releases?

@rjmccall
Copy link
Contributor Author

@swift-ci Please test

@jamieQ
Copy link
Contributor

jamieQ commented Sep 11, 2025

Do you have a build immediately prior to my patches that observe that test case passing, or are you comparing with one of the beta releases?

@rjmccall i double checked, and i don't think your patch affected the behavior of the executor assertions – but, as hoped, the patches do make the isolation macro work as expected (yay!).

the behavior where an isolation assertion will pass but the #isolation macro (sometimes) resolves to nil appear to be limited to the 6.2 branch (and various Xcode 26 beta toolchains) AFAICT (e.g. here is a compiler explorer example). so perhaps that's related to a different change on the main branch from the relatively recent past.

@rjmccall
Copy link
Contributor Author

rjmccall commented Sep 11, 2025

Do you have a build immediately prior to my patches that observe that test case passing, or are you comparing with one of the beta releases?

@rjmccall i double checked, and i don't think your patch affected the behavior of the executor assertions – but, as hoped, the patches do make the isolation macro work as expected (yay!).

the behavior where an isolation assertion will pass but the #isolation macro (sometimes) resolves to nil appear to be limited to the 6.2 branch (and various Xcode 26 beta toolchains) AFAICT (e.g. here is a compiler explorer example). so perhaps that's related to a different change on the main branch from the relatively recent past.

Okay, thanks. So it sounds like:

  • The 6.2 branch toolchains appear to isolate the function correctly but sometimes compute #isolation wrong.
  • The main branch toolchains prior to my patches appear to not isolate the function correctly and also compute #isolation wrong.
  • The main branch toolchains with my patches appear to not isolate the function correctly but now compute #isolation right.

This is consistent with the #isolation bug not being fixed until my patches on main and a separate bug that incorrectly allows an actor hop to be removed. I think we have a good idea what that bug is, but @gottesmm is on vacation this week. I'll make sure he's aware of your test case. FWIW, doing an assertIsolated() in the actor function prior to the call appears to prevent the actor hop from being removed.

@rjmccall rjmccall merged commit a71336c into swiftlang:main Sep 11, 2025
5 checks passed
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