Skip to content

Commit f7d81d5

Browse files
committed
[TypeCheckEffects] Downgrade missing await to a warning for initializer calls with a single unlabeled parameter
Mitigation for a historic incorrect type-checker behavior caused by one of the performance hacks that used to favor sync constructor overload over async one in async context if initializers take a single unlabeled argument. ```swift struct S { init(_: Int) {} init(_: Int) async {} } func test(v: Int) async { S(v) } ``` The type-checker should use `init(_: Int) async` in `test` context but used to select the sync overload. The hack is now gone but we need to downgrade an error to a warning to give the developers time to fix their code.
1 parent c767f7a commit f7d81d5

File tree

2 files changed

+45
-1
lines changed

2 files changed

+45
-1
lines changed

lib/Sema/TypeCheckEffects.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1400,6 +1400,29 @@ class ApplyClassifier {
14001400
// then look at all of the closure arguments.
14011401
LLVM_FALLTHROUGH;
14021402
} else {
1403+
// Mitigation for a historic incorrect type-checker behavior
1404+
// caused by one of the performance hacks that used to favor
1405+
// sync constructor overload over async one in async context
1406+
// if initializers take a single unlabeled argument.
1407+
//
1408+
// struct S {
1409+
// init(_: Int) {}
1410+
// init(_: Int) async {}
1411+
// }
1412+
//
1413+
// func test(v: Int) async { S(v) }
1414+
//
1415+
// The type-checker should use `init(_: Int) async` in `test` context
1416+
// but used to select the sync overload. The hack is now gone but we
1417+
// need to downgrade an error to a warning to give the developers time
1418+
// to fix their code.
1419+
if (kind == EffectKind::Async &&
1420+
fnRef.getKind() == AbstractFunction::Function) {
1421+
if (auto *ctor = dyn_cast<ConstructorRefCallExpr>(E->getFn())) {
1422+
if (ctor->getFn()->isImplicit() && args->isUnlabeledUnary())
1423+
result.setDowngradeToWarning(true);
1424+
}
1425+
}
14031426
break;
14041427
}
14051428

test/Constraints/async.swift

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-typecheck-verify-swift
1+
// RUN: %target-typecheck-verify-swift
22

33
// REQUIRES: concurrency
44

@@ -197,3 +197,24 @@ func test_sync_in_closure_context() {
197197
test(42) // Ok (select sync overloads and discards the result)
198198
}
199199
}
200+
201+
@available(SwiftStdlib 5.5, *)
202+
func test_async_calls_in_async_context(v: Int) async {
203+
final class Test : Sendable {
204+
init(_: Int) {}
205+
init(_: Int) async {}
206+
207+
func test(_: Int) {}
208+
func test(_: Int) async {}
209+
210+
static func test(_: Int) {}
211+
static func test(_: Int) async {}
212+
}
213+
214+
// Only implicit `.init` should be accepted with a warning due type-checker previously picking an incorrect overload.
215+
_ = Test(v) // expected-warning {{expression is 'async' but is not marked with 'await'; this is an error in the Swift 6 language mode}} expected-note {{call is 'async'}}
216+
_ = Test.init(v) // expected-error {{expression is 'async' but is not marked with 'await'}} expected-note {{call is 'async'}}
217+
218+
Test.test(v) // expected-error {{expression is 'async' but is not marked with 'await'}} expected-note {{call is 'async'}}
219+
Test(v).test(v) // expected-error {{expression is 'async' but is not marked with 'await'}} expected-note 2 {{call is 'async'}}
220+
}

0 commit comments

Comments
 (0)