mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
The `try await` ordering is both easier to read and indicates the order of operations better, because the suspension point occurs first and then one can observe a thrown error.
194 lines
7.1 KiB
Swift
194 lines
7.1 KiB
Swift
// RUN: %target-swift-frontend -typecheck -verify %s -enable-experimental-concurrency -verify-syntax-tree
|
|
|
|
// REQUIRES: concurrency
|
|
|
|
func test1(asyncfp : () async -> Int, fp : () -> Int) async {
|
|
_ = await asyncfp()
|
|
_ = await asyncfp() + asyncfp()
|
|
_ = await asyncfp() + fp()
|
|
_ = await fp() + 42 // expected-warning {{no calls to 'async' functions occur within 'await' expression}}
|
|
_ = asyncfp() // expected-error {{call is 'async' but is not marked with 'await'}}{{7-7=await }}
|
|
}
|
|
|
|
func getInt() async -> Int { return 5 }
|
|
|
|
// Locations where "await" is prohibited.
|
|
func test2(
|
|
defaulted: Int = await getInt() // expected-error{{'async' call cannot occur in a default argument}}
|
|
) async {
|
|
defer {
|
|
_ = await getInt() // expected-error{{'async' call cannot occur in a defer body}}
|
|
}
|
|
print("foo")
|
|
}
|
|
|
|
func test3() { // expected-note{{add 'async' to function 'test3()' to make it asynchronous}} {{none}}
|
|
// expected-note@-1{{add '@asyncHandler' to function 'test3()' to create an implicit asynchronous context}}{{1-1=@asyncHandler }}
|
|
_ = await getInt() // expected-error{{'async' in a function that does not support concurrency}}
|
|
}
|
|
|
|
enum SomeEnum: Int {
|
|
case foo = await 5 // expected-error{{raw value for enum case must be a literal}}
|
|
}
|
|
|
|
struct SomeStruct {
|
|
var x = await getInt() // expected-error{{'async' call cannot occur in a property initializer}}
|
|
static var y = await getInt() // expected-error{{'async' call cannot occur in a global variable initializer}}
|
|
}
|
|
|
|
func acceptAutoclosureNonAsync(_: @autoclosure () -> Int) async { }
|
|
func acceptAutoclosureAsync(_: @autoclosure () async -> Int) async { }
|
|
func acceptAutoclosureAsyncThrows(_: @autoclosure () async throws -> Int) async { }
|
|
func acceptAutoclosureAsyncThrowsRethrows(_: @autoclosure () async throws -> Int) async rethrows { }
|
|
|
|
func acceptAutoclosureNonAsyncBad(_: @autoclosure () async -> Int) -> Int { 0 }
|
|
// expected-error@-1{{'async' autoclosure parameter in a non-'async' function}}
|
|
// expected-note@-2{{add 'async' to function 'acceptAutoclosureNonAsyncBad' to make it asynchronous}} {{none}}
|
|
|
|
struct HasAsyncBad {
|
|
init(_: @autoclosure () async -> Int) { }
|
|
// expected-error@-1{{'async' autoclosure parameter in a non-'async' function}}
|
|
}
|
|
|
|
func testAutoclosure() async {
|
|
await acceptAutoclosureAsync(getInt()) // expected-error{{call is 'async' in an autoclosure argument that is not marked with 'await'}}{{32-32=await }}
|
|
await acceptAutoclosureNonAsync(getInt()) // expected-error{{'async' in an autoclosure that does not support concurrency}}
|
|
|
|
await acceptAutoclosureAsync(await getInt())
|
|
await acceptAutoclosureNonAsync(await getInt()) // expected-error{{'async' in an autoclosure that does not support concurrency}}
|
|
|
|
await acceptAutoclosureAsync(getInt()) // expected-error{{call is 'async' in an autoclosure argument that is not marked with 'await'}}{{32-32=await }}
|
|
await acceptAutoclosureNonAsync(getInt()) // expected-error{{'async' in an autoclosure that does not support concurrency}}
|
|
}
|
|
|
|
// Test inference of 'async' from the body of a closure.
|
|
func testClosure() {
|
|
let closure = {
|
|
await getInt()
|
|
}
|
|
|
|
let _: () -> Int = closure // expected-error{{invalid conversion from 'async' function of type '() async -> Int' to synchronous function type '() -> Int'}}
|
|
|
|
let closure2 = { () async -> Int in
|
|
print("here")
|
|
return await getInt()
|
|
}
|
|
|
|
let _: () -> Int = closure2 // expected-error{{invalid conversion from 'async' function of type '() async -> Int' to synchronous function type '() -> Int'}}
|
|
}
|
|
|
|
// Nesting async and await together
|
|
func throwingAndAsync() async throws -> Int { return 0 }
|
|
|
|
enum HomeworkError : Error {
|
|
case dogAteIt
|
|
}
|
|
|
|
func testThrowingAndAsync() async throws {
|
|
_ = try await throwingAndAsync()
|
|
_ = await try throwingAndAsync() // expected-warning{{'try' must precede 'await'}}{{7-13=}}{{17-17=await }}
|
|
_ = await (try throwingAndAsync())
|
|
_ = try (await throwingAndAsync())
|
|
|
|
// Errors
|
|
_ = await throwingAndAsync() // expected-error{{call can throw but is not marked with 'try'}}
|
|
// expected-note@-1{{did you mean to use 'try'?}}
|
|
// expected-note@-2{{did you mean to handle error as optional value?}}
|
|
// expected-note@-3{{did you mean to disable error propagation?}}
|
|
_ = try throwingAndAsync() // expected-error{{call is 'async' but is not marked with 'await'}}{{11-11=await }}
|
|
}
|
|
|
|
func testExhaustiveDoCatch() async {
|
|
do {
|
|
_ = try await throwingAndAsync()
|
|
} catch {
|
|
}
|
|
|
|
do {
|
|
_ = try await throwingAndAsync()
|
|
// expected-error@-1{{errors thrown from here are not handled because the enclosing catch is not exhaustive}}
|
|
} catch let e as HomeworkError {
|
|
}
|
|
|
|
// Ensure that we infer 'async' through an exhaustive do-catch.
|
|
let fn = {
|
|
do {
|
|
_ = try await throwingAndAsync()
|
|
} catch {
|
|
}
|
|
}
|
|
|
|
let _: Int = fn // expected-error{{cannot convert value of type '() async -> ()'}}
|
|
|
|
// Ensure that we infer 'async' through a non-exhaustive do-catch.
|
|
let fn2 = {
|
|
do {
|
|
_ = try await throwingAndAsync()
|
|
} catch let e as HomeworkError {
|
|
}
|
|
}
|
|
|
|
let _: Int = fn2 // expected-error{{cannot convert value of type '() async throws -> ()'}}
|
|
}
|
|
|
|
// String interpolation
|
|
func testStringInterpolation() async throws {
|
|
_ = "Eventually produces \(getInt())" // expected-error{{call is 'async' but is not marked with 'await'}}
|
|
_ = "Eventually produces \(await getInt())"
|
|
_ = await "Eventually produces \(getInt())"
|
|
}
|
|
|
|
func invalidAsyncFunction() async {
|
|
_ = try await throwingAndAsync() // expected-error {{errors thrown from here are not handled}}
|
|
}
|
|
|
|
func validAsyncFunction() async throws {
|
|
_ = try await throwingAndAsync()
|
|
}
|
|
|
|
// Async let checking
|
|
func mightThrow() throws { }
|
|
|
|
func getIntUnsafely() throws -> Int { 0 }
|
|
func getIntUnsafelyAsync() async throws -> Int { 0 }
|
|
|
|
extension Error {
|
|
var number: Int { 0 }
|
|
}
|
|
|
|
func testAsyncLet() async throws {
|
|
async let x = await getInt()
|
|
print(x) // expected-error{{reference to async let 'x' is not marked with 'await'}}
|
|
print(await x)
|
|
|
|
do {
|
|
try mightThrow()
|
|
} catch let e where e.number == x { // expected-error{{async let 'x' cannot be referenced in a catch guard expression}}
|
|
} catch {
|
|
}
|
|
|
|
async let x1 = getIntUnsafely() // okay, try is implicit here
|
|
|
|
async let x2 = getInt() // okay, await is implicit here
|
|
|
|
async let x3 = try getIntUnsafely()
|
|
async let x4 = try! getIntUnsafely()
|
|
async let x5 = try? getIntUnsafely()
|
|
|
|
_ = await x1 // expected-error{{reading 'async let' can throw but is not marked with 'try'}}
|
|
_ = await x2
|
|
_ = try await x3
|
|
_ = await x4
|
|
_ = await x5
|
|
}
|
|
|
|
// expected-note@+2 4{{add 'async' to function 'testAsyncLetOutOfAsync()' to make it asynchronous}} {{none}}
|
|
// expected-note@+1 4{{add '@asyncHandler' to function 'testAsyncLetOutOfAsync()' to create an implicit asynchronous context}} {{1-1=@asyncHandler }}
|
|
func testAsyncLetOutOfAsync() {
|
|
async let x = 1 // expected-error{{'async let' in a function that does not support concurrency}}
|
|
// FIXME: expected-error@-1{{'async' in a function that does not support concurrency}}
|
|
|
|
_ = await x // expected-error{{'async let' in a function that does not support concurrency}}
|
|
_ = x // expected-error{{'async let' in a function that does not support concurrency}}
|
|
}
|