Disable "Constant inferred to have type '()'" warning when using async let

Swift warns when you assign a Void result to a variable:

func updateState() {
  // Update internal state here. Note the lack of a return type
}

// ⚠️: Constant 'x' inferred to have type '()', which may be unexpected
let x = updateState()

This is usually a useful warning; usually if you’re assigning to a variable, you want to be able to do something with it, and there’s not much you can do with a Void.

However, if we’re using async let, this is less useful:

// Run both tasks in parallel
async let task1 = updateState()     // ⚠️: Constant 'task1' inferred...
async let task2 = updateMoreState() // ⚠️: Constant 'task2' inferred...

await (task1, task2)

This warning is less useful now, because now there is something useful we can do with the void return value — we can await it.

You can silence this warning by explicitly declaring the type:

// Run both tasks in parallel
async let task1: () = updateState()     // ✅ No more warning
async let task2: () = updateMoreState() // ✅ No more warning

await (task1, task2)

But it’s annoying to have to do that.

Any thoughts on removing this warning? Is this the kind of thing we’d need a formal proposal for? Or could I just submit a PR?

15 Likes

I think removing the warning here is a good idea. In the async let case you aren’t really returning void any more, you are sort of returning a promise of void, which isn’t the same thing at all, because there is a use for the assigned variable.

This is a change to a compiler warning and doesn't require evolution approval.

I do think it's a good change to make.

6 Likes

Additionally, you did not note the following warning, which requires an assignment like the following, to be silenced.

_ = await (task1, task2)
async let task1: Void = updateState()
async let task2: Void = updateMoreState()
await (task1, task2) // Expression of type '(Void, Void)' is unused

Ideally, I'd like to see that gone too. But as it is, I don't think the compiler should allow awaiting on multiple async lets in a tuple, because it's misleading and broken when matched up with the results of multiple calls stuffed into a tuple.

func wait() async {
  try! await Task.sleep(for: .seconds(1))
  print(#function)
}
func doNotWait()  { print(#function) }

async let (wait, doNotWait): (_, _) = (wait(), doNotWait())
_ = await (doNotWait, wait) // "wait()", "doNotWait()" 👎
async let (doNotWait, wait): (_, _) = (doNotWait(), wait())
_ = await (wait, doNotWait) //  "doNotWait()", "wait()" 👍

Neat idea, very supportive of cleaning that up, please give it a shot or reach out if you’d have trouble figuring out how to make the change :-)

apologies if this is overstepping, but i hit this recently and wondered how hard it would be to address the narrow-seeming issue of the type annotations, so made an attempt and put together this draft PR: [Sema]: suppress unexpected type warning on some async-let patterns by jamieQ · Pull Request #85615 · swiftlang/swift · GitHub. the current changeset only targets removing the warning when the inferred async-let binding is exactly Void, and doesn't consider the nature of the 'right hand side' of the binding at all. the existing logic in the relevant code also has various other 'possibly unexpected' types it handles, but none of the other cases jumped out to me as something we'd want to suppress in these scenarios. feel free to take a look at the implementation logic & test cases and lmk if you think otherwise.

if you were set on implementing this yourself @bjhomer also happy to defer to you – feel free to use the PR as a starting point/inspiration or whatever.


and on a different matter...

i found this quite surprising... is the right way to think of it that it is just the binding declaration order that governs how this works, not the final await expression tuple order?

1 Like

I’m perfectly happy to have someone else implement it! I had it on my list of “things to do when I have a free day”, but who knows when that will happen, and I have plenty of other things to keep me busy.

2 Likes