You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[SE-0296] Allow overloads that differ only in async (swiftlang#1392)
* [SE-0296] Allow overloads that differ only in async
* Fix typo, and enhance wording
* [SE-0296] Detail resolution of overloads that differ only in async
* [SE-0296] Fix typo
Copy file name to clipboardexpand all lines: proposals/0296-async-await.md
+38-5
Original file line number
Diff line number
Diff line change
@@ -471,25 +471,58 @@ These two functions have different names and signatures, even though they share
471
471
doSomething() // problem: can call either, unmodified Swift rules prefer the `async` version
472
472
```
473
473
474
-
Swift's overloading rules prefer to call a function with fewer default arguments, so the addition of the `async` function would break existing code that called the original `doSomething(completionHandler:)` with no completion handler. This would get an error along the lines of:
474
+
A similar problem exists for APIs that evolve into providing both a synchronous and an asynchronous version of the same function, with the same signature. Such pairs allow APIs to provide a new asynchronous function which better fits in the Swift asynchronous landscape, without breaking backward compatibility. New asynchronous functions can support, for example, cancellation (covered in the [Structured Concurrency](https://github.com/DougGregor/swift-evolution/blob/structured-concurrency/proposals/nnnn-structured-concurrency.md) proposal).
475
+
476
+
```swift
477
+
// Existing synchronous API
478
+
funcdoSomethingElse() { ... }
479
+
480
+
// New and enhanced asynchronous API
481
+
funcdoSomethingElse() async { ... }
482
+
```
483
+
484
+
In the first case, Swift's overloading rules prefer to call a function with fewer default arguments, so the addition of the `async` function would break existing code that called the original `doSomething(completionHandler:)` with no completion handler. This would get an error along the lines of:
475
485
476
486
```
477
487
error: `async` function cannot be called from non-asynchronous context
478
488
```
479
489
480
490
This presents problems for code evolution, because developers of existing asynchronous libraries would have to either have a hard compatiblity break (e.g, to a new major version) or would need have different names for all of the new `async` versions. The latter would likely result in a scheme such as [C#'s pervasive `Async` suffix](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/task-asynchronous-programming-model).
481
491
492
+
The second case, where both functions have the same signature and only differ in `async`, is normally rejected by existing Swift's overloading rules. Those do not allow two functions to differ only in their *effects*, and one can not define two functions that only differ in `throws`, for example.
493
+
494
+
```
495
+
// error: redeclaration of function `doSomethingElse()`.
496
+
```
497
+
498
+
This also presents a problem for code evolution, because developers of existing libraries just could not preserve their existing synchronous APIs, and support new asynchronous features.
499
+
482
500
Instead, we propose an overload-resolution rule to select the appropriate function based on the context of the call. Given a call, overload resolution prefers non-`async` functions within a synchronous context (because such contexts cannot contain a call to an `async` function). Furthermore, overload resolution prefers `async` functions within an asynchronous context (because such contexts should avoid stepping out of the asynchronous model into blocking APIs). When overload resolution selects an `async` function, that call is still subject to the rule that it must occur within an `await` expression.
483
501
484
-
Note that we follow the design of `throws`in disallowing overloads that differ *only* in `async`:
502
+
The overload-resolution rule depends on the synchronous or asynchronous context, in which the compiler selects one and only one overload. The selection of the async overload requires an `await` expression, as all introductions of a potential suspension point:
// In an asynchronous context, the async overload is preferred:
507
+
awaitdoSomething()
508
+
// Compiler error: Expression is 'async' but is not marked with 'await'
509
+
doSomething()
510
+
}
511
+
```
512
+
513
+
In non-`async` functions, and closures without any `await` expression, the compiler selects the non-`async` overload:
489
514
490
-
// error: redeclaration of function `doSomething()`.
515
+
```swift
516
+
funcf() async {
517
+
let f2 = {
518
+
// In a synchronous context, the non-async overload is preferred:
519
+
doSomething()
520
+
}
521
+
f2()
522
+
}
491
523
```
492
524
525
+
493
526
### Autoclosures
494
527
495
528
A function may not take an autoclosure parameter of `async` function type unless the function itself is `async`. For example, the following declaration is ill-formed:
0 commit comments