Skip to content

Commit 9ccf206

Browse files
committed
[Concurrency] Tune overloading to to allow sync overloads in async contexts.
The existing overloading rules strongly prefer async functions within async contexts, and synchronous functions in synchronous contexts. However, when there are other differences in the signature, particularly parameters of function type that differ in async vs. synchronous, the overloading rule would force the use of the synchronous function even in cases where the synchronous function would be better. An example: func f(_: (Int) -> Int) { } func f(_: (Int) async -> Int) async { } func g(_ x: Int) -> Int { -x } func h() async { f(g) // currently selects async f, want to select synchronous f } Effect the semantics change by splitting the "sync/async mismatch" score in the constraint system into an "async in sync mismatch" score that is mostly disqualifying (because the call will always fail) and a less-important score for "sync used in an async context", which also includes conversion from a synchronous function to an asynchronous one. This way, only synchronous functions are still considered within a synchronous context, but we get more natural overloading behavior within an asynchronous context. The end result is intended to be equivalent to what one would get with reasync: func f(_: (Int) async -> Int) async { ... } Addresses rdar://74289867.
1 parent 57404f0 commit 9ccf206

File tree

6 files changed

+40
-8
lines changed

6 files changed

+40
-8
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -775,9 +775,11 @@ enum ScoreKind {
775775
SK_Hole,
776776
/// A reference to an @unavailable declaration.
777777
SK_Unavailable,
778-
/// A reference to an async function in a synchronous context, or
779-
/// vice versa.
780-
SK_AsyncSyncMismatch,
778+
/// A reference to an async function in a synchronous context.
779+
SK_AsyncInSyncMismatch,
780+
/// Synchronous function in an asynchronous context or a conversion of
781+
/// a synchronous function to an asynchronous one.
782+
SK_SyncInAsync,
781783
/// A use of the "forward" scan for trailing closures.
782784
SK_ForwardTrailingClosure,
783785
/// A use of a disfavored overload.

lib/Sema/CSRanking.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,11 @@ static StringRef getScoreKindName(ScoreKind kind) {
4040
case SK_Unavailable:
4141
return "use of an unavailable declaration";
4242

43-
case SK_AsyncSyncMismatch:
44-
return "async/synchronous mismatch";
43+
case SK_AsyncInSyncMismatch:
44+
return "async-in-synchronous mismatch";
45+
46+
case SK_SyncInAsync:
47+
return "sync-in-asynchronous";
4548

4649
case SK_ForwardTrailingClosure:
4750
return "forward scan when matching a trailing closure";

lib/Sema/CSSimplify.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1983,6 +1983,8 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
19831983
if (recordFix(fix))
19841984
return getTypeMatchFailure(locator);
19851985
}
1986+
1987+
increaseScore(SK_SyncInAsync);
19861988
}
19871989

19881990
// A @concurrent function can be a subtype of a non-@concurrent function.

lib/Sema/CSStep.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -711,7 +711,7 @@ bool DisjunctionStep::shouldStopAt(const DisjunctionChoice &choice) const {
711711
auto delta = LastSolvedChoice->second - getCurrentScore();
712712
bool hasUnavailableOverloads = delta.Data[SK_Unavailable] > 0;
713713
bool hasFixes = delta.Data[SK_Fix] > 0;
714-
bool hasAsyncMismatch = delta.Data[SK_AsyncSyncMismatch] > 0;
714+
bool hasAsyncMismatch = delta.Data[SK_AsyncInSyncMismatch] > 0;
715715
auto isBeginningOfPartition = choice.isBeginningOfPartition();
716716

717717
// Attempt to short-circuit evaluation of this disjunction only

lib/Sema/ConstraintSystem.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2820,8 +2820,10 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
28202820
// If we're choosing an asynchronous declaration within a synchronous
28212821
// context, or vice-versa, increase the async/async mismatch score.
28222822
if (auto func = dyn_cast<AbstractFunctionDecl>(decl)) {
2823-
if (func->isAsyncContext() != isAsynchronousContext(useDC))
2824-
increaseScore(SK_AsyncSyncMismatch);
2823+
if (func->isAsyncContext() != isAsynchronousContext(useDC)) {
2824+
increaseScore(
2825+
func->isAsyncContext() ? SK_AsyncInSyncMismatch : SK_SyncInAsync);
2826+
}
28252827
}
28262828

28272829
// If we're binding to an init member, the 'throws' need to line up

test/Constraints/async.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,26 @@ struct FunctionTypes {
115115
asyncNonThrowing = syncThrowing // expected-error{{invalid conversion}}
116116
}
117117
}
118+
119+
// Overloading when there is conversion from sync to async.
120+
func bar(_ f: (Int) -> Int) -> Int {
121+
return f(2)
122+
}
123+
124+
func bar(_ f: (Int) async -> Int) async -> Int {
125+
return await f(2)
126+
}
127+
128+
func incrementSync(_ x: Int) -> Int {
129+
return x + 1
130+
}
131+
132+
func incrementAsync(_ x: Int) async -> Int {
133+
return x + 1
134+
}
135+
136+
func testAsyncWithConversions() async {
137+
_ = bar(incrementSync)
138+
_ = bar { -$0 }
139+
_ = bar(incrementAsync) // expected-error{{call is 'async' but is not marked with 'await'}}
140+
}

0 commit comments

Comments
 (0)