Syntax issue with calling into an actor in a while loop condition

I cannot get this simple while loop with 2 conditions to compile:

        actor Actor1 {
            func isBusy() -> Bool { false }
        }
        
        actor Actor2 {
            func isBusy() -> Bool { false }
        }
        
        let a1 = Actor1()
        let a2 = Actor2()
        
        while await a1.isBusy() && await a2.isBusy() {
            // 'await' cannot appear to the right of a non-assignment operator
            // 'await' in an autoclosure that does not support concurrency
            // Actor-isolated instance method 'isBusy()' can not be referenced from a non-isolated context
        }
        
        while (await a1.isBusy()) && (await a2.isBusy()) {
            // 'await' in an autoclosure that does not support concurrency
            // Actor-isolated instance method 'isBusy()' can not be referenced from a non-isolated context
        }

        while await a1.isBusy() {
            // One condition compiles fine
        }

Does anybody have any recommendations?

I believe that should be:

    while await a1.isBusy() && a2.isBusy() {
        ...
    }

But that doesn't work (compiler bug?)

Perhaps this as a workaround:

    while true {
        if !(await a1.isBusy()) { break }
        if !(await a2.isBusy()) { break }
        ...
    }

BTW, while busy looks like a busy waiting anti-pattern.

I agree the example looks like an anti-pattern, but my actual usage of it isn't and that's beyond the scope of this question. I simply would like to know the appropriate syntax. If the appropriate syntax doesn't work I can file a compiler bug.

I submitted an issue.

I thought && would work, not sure why it doesn't (is it a bug?).

The following code compiles ok, see if it matches your expectation.

while await a1.isBusy(),
      await a2.isBusy() {
}

You could replace && with a comma , in the first example. I think it's the @autoclosure of the rhs parameter to && which can't currently support async.

2 Likes

thank you!

Good catch. This example proves your finding:

extension Bool {
	static func & (a: Bool, b: Bool) -> Bool {
		a && b
	}
}

func foo() async {
	while await a1.isBusy() & a2.isBusy() { // compiles ok
	}
}

extension Bool {
	static func & (a: Bool, b: @autoclosure () -> Bool) -> Bool {
		a && b()
	}
}

func foo() async {
	while await a1.isBusy() & a2.isBusy() { // Error
	}
}