Skip to content

Commit 886a8ab

Browse files
committed
[Diagnostics] Improve diagnostics when passing async to a sync parameter
If `async` effect has been inferred from the body of the closure, let's find out the first occurrence of `async` node and point it out to make it clear why closure is `async`. Resolves: rdar://70610141
1 parent 53992d0 commit 886a8ab

File tree

4 files changed

+34
-5
lines changed

4 files changed

+34
-5
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,13 @@ ERROR(async_functiontype_mismatch,none,
559559
"invalid conversion from 'async' function of type %0 to "
560560
"synchronous function type %1", (Type, Type))
561561

562+
ERROR(cannot_pass_async_func_to_sync_parameter,none,
563+
"cannot pass function of type %0 "
564+
"to parameter expecting synchronous function type", (Type))
565+
566+
NOTE(async_in_closure_that_does_not_support_concurrency,none,
567+
"'async' in a closure that does not support concurrency", ())
568+
562569
// Key-path expressions.
563570
ERROR(expr_keypath_no_objc_runtime,none,
564571
"'#keyPath' can only be used with the Objective-C runtime", ())

lib/Sema/CSDiagnostics.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5884,6 +5884,28 @@ bool ThrowingFunctionConversionFailure::diagnoseAsError() {
58845884
}
58855885

58865886
bool AsyncFunctionConversionFailure::diagnoseAsError() {
5887+
auto *locator = getLocator();
5888+
5889+
if (locator->isLastElement<LocatorPathElt::ApplyArgToParam>()) {
5890+
emitDiagnostic(diag::cannot_pass_async_func_to_sync_parameter,
5891+
getFromType());
5892+
5893+
if (auto *closure = getAsExpr<ClosureExpr>(getAnchor())) {
5894+
auto asyncLoc = closure->getAsyncLoc();
5895+
5896+
// 'async' effect is inferred from the body of the closure.
5897+
if (asyncLoc.isInvalid()) {
5898+
if (auto asyncNode = findAsyncNode(closure)) {
5899+
emitDiagnosticAt(
5900+
::getLoc(asyncNode),
5901+
diag::async_in_closure_that_does_not_support_concurrency);
5902+
}
5903+
}
5904+
}
5905+
5906+
return true;
5907+
}
5908+
58875909
emitDiagnostic(diag::async_functiontype_mismatch, getFromType(),
58885910
getToType());
58895911
return true;

test/Concurrency/async_sequence_syntax.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ func missingTryInBlock<T : AsyncSequence>(_ seq: T) {
3333

3434
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
3535
func missingAsyncInBlock<T : AsyncSequence>(_ seq: T) {
36-
execute { // expected-error{{invalid conversion from 'async' function of type '() async -> Void' to synchronous function type '() -> Void'}}
36+
execute { // expected-error{{cannot pass function of type '() async -> Void' to parameter expecting synchronous function type}}
3737
do {
38-
for try await _ in seq { }
38+
for try await _ in seq { } // expected-note {{'async' in a closure that does not support concurrency}}
3939
} catch { }
4040
}
4141
}
@@ -81,4 +81,4 @@ func forAwaitWithConcreteType(_ seq: ThrowingAsyncSequence) throws { // expected
8181
for try await elt in seq { // expected-error {{'async' in a function that does not support concurrency}}
8282
_ = elt
8383
}
84-
}
84+
}

test/Concurrency/async_tasks.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ func buyVegetables(shoppingList: [String]) async throws -> [Vegetable] {
4747
func test_unsafeContinuations() async {
4848
// the closure should not allow async operations;
4949
// after all: if you have async code, just call it directly, without the unsafe continuation
50-
let _: String = withUnsafeContinuation { continuation in // expected-error{{invalid conversion from 'async' function of type '(UnsafeContinuation<String, Never>) async -> Void' to synchronous function type '(UnsafeContinuation<String, Never>) -> Void'}}
51-
let s = await someAsyncFunc() // rdar://70610141 for getting a better error message here
50+
let _: String = withUnsafeContinuation { continuation in // expected-error{{cannot pass function of type '(UnsafeContinuation<String, Never>) async -> Void' to parameter expecting synchronous function type}}
51+
let s = await someAsyncFunc() // expected-note {{'async' in a closure that does not support concurrency}}
5252
continuation.resume(returning: s)
5353
}
5454

0 commit comments

Comments
 (0)