Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to fix "'async' call in an autoclosure that does not support concurrency"?

I have these two functions (which compile without errors):

func hasLocalChanges() -> Bool {
    return false
}

func hasRemoteChanges() async -> Bool {
    return await Task{ true }.value
}

Now, let's say I want to introduce a third function which will look like this:

func hasChanges() async -> Bool {
    return self.hasLocalChanges() || await self.hasRemoteChanges()
}

Then this function will give me a compiler error saying:

'async' call in an autoclosure that does not support concurrency

But why is that?

I can resolve the error by swapping the operands …

func hasChanges() async -> Bool {
    return await self.hasRemoteChanges() || self.hasLocalChanges()
}

…, which, again, will make the code compile without errors.

But I really want to make use of lazy evaluation and have the asynchronous function be executed last. So the only way to achieve that would be to say …

func hasChanges() async -> Bool {
    if !self.hasLocalChanges() {
        return await self.hasRemoteChanges()
    } else {
        return true
    }
}

…, which seems a little cumbersome.

Can anyone explain to me why I am getting that error in the first place?

like image 421
goofy4224 Avatar asked Oct 15 '25 20:10

goofy4224


1 Answers

It's because || is actually a function that wants the right hand side to look like rhs: @autoclosure () throws -> Bool (vs. e.g rhs: @autoclosure () async throws -> Bool)

See the source here: https://github.com/apple/swift/blob/e6cbf5483237aa593bdbafb6c7db7ebcb2d0e26a/stdlib/public/core/Bool.swift#L320

When you move the await first, it resolves into a boolean and then self.hasLocalChanges() is a non async () throws -> Bool

Examples that compile below

    func hasChanges() async -> Bool {
        return or( a: await hasRemoteChanges(), b: hasLocalChanges())
    }
    
    // This is pretty much what `||` looks like today
    func or(a: Bool, b: @autoclosure () throws -> Bool) -> Bool {
        return a ? true : try b()
    }
    func hasChanges() async -> Bool {
        return await or( a: hasLocalChanges(), b: await hasRemoteChanges())
    }
    
    // This is what an `async` friendly `||` could look like. 
    // Note that the caller would need to await the result.
    func or(a: Bool, b: @autoclosure () async throws -> Bool) async -> Bool {
        return a ? true : try! await b()
    }
like image 85
aciniglio Avatar answered Oct 17 '25 10:10

aciniglio



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!