Skip to content

Commit 694fad4

Browse files
committed
Adding unavailableFromAsync tests
This patch adds a bunch of tests to verify the behaviour of the @_unavailableFromAsync attribute. The attribute is only allowed on functions, so we verify that an error is emitted when it is applied to structs, extensions, classes, and actors. The attribute may be applied to constructors to disallow construction in an async context, but cannot be applied to destructors since a destructor must be callable from anywhere. Additionally, the attribute cannot be applied to asynchronous functions since an async function _must_ be called from an async context. Specific checks include - Errors are emitted in an async context (global function, e.g.) - Errors are emitted when the async context is nested in a sync context - Errors are not emitted from a sync context nested in an async context This patch also includes verification that the attribute is propagated across module boundaries and is be imported from ObjC functions. Lastly, this patch adds the IDE completion testing to verify that the attribute is considered.
1 parent 46d84be commit 694fad4

File tree

5 files changed

+147
-2
lines changed

5 files changed

+147
-2
lines changed

test/ClangImporter/objc_async.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules -disable-availability-checking %s -verify -verify-additional-file %swift_src_root/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h -warn-concurrency
1+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules %s -verify -verify-additional-file %swift_src_root/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h -warn-concurrency
22

33
// REQUIRES: objc_interop
44
// REQUIRES: concurrency
55
import Foundation
66
import ObjCConcurrency
77

8+
if #available(SwiftStdlib 5.5, *) {
9+
810
@MainActor func onlyOnMainActor() { }
911

1012
func testSlowServer(slowServer: SlowServer) async throws {
@@ -43,6 +45,8 @@ func testSlowServer(slowServer: SlowServer) async throws {
4345
let _: Int = await slowServer.bestName("hello")
4446
let _: Int = await slowServer.customize("hello")
4547

48+
slowServer.unavailableMethod() // expected-warning{{'unavailableMethod' is unavailable from asynchronous contexts}}
49+
4650
let _: String = await slowServer.dance("slide")
4751
let _: String = await slowServer.__leap(17)
4852

@@ -213,3 +217,5 @@ func testMirrored(instance: ClassWithAsync) async {
213217
}
214218
}
215219
}
220+
221+
} // SwiftStdlib 5.5
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
@_unavailableFromAsync
2+
public func unavailableFunction() { }
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/UnavailableFunction.swiftmodule -module-name UnavailableFunction -warn-concurrency %S/Inputs/UnavailableFunction.swift
3+
// RUN: %target-swift-frontend -typecheck -verify -I %t %s
4+
5+
// REQUIRES: concurrency
6+
7+
import UnavailableFunction
8+
9+
@available(SwiftStdlib 5.1, *)
10+
func okay() {}
11+
12+
// expected-error@+1{{'@_unavailableFromAsync' attribute cannot be applied to this declaration}}
13+
@_unavailableFromAsync
14+
@available(SwiftStdlib 5.1, *)
15+
struct Foo { }
16+
17+
// expected-error@+1{{'@_unavailableFromAsync' attribute cannot be applied to this declaration}}
18+
@_unavailableFromAsync
19+
@available(SwiftStdlib 5.1, *)
20+
extension Foo { }
21+
22+
// expected-error@+1{{'@_unavailableFromAsync' attribute cannot be applied to this declaration}}
23+
@_unavailableFromAsync
24+
@available(SwiftStdlib 5.1, *)
25+
class Bar {
26+
// expected-error@+1{{'@_unavailableFromAsync' attribute cannot be applied to this declaration}}
27+
@_unavailableFromAsync
28+
deinit { }
29+
}
30+
31+
// expected-error@+1{{'@_unavailableFromAsync' attribute cannot be applied to this declaration}}
32+
@_unavailableFromAsync
33+
@available(SwiftStdlib 5.1, *)
34+
actor Baz { }
35+
36+
@available(SwiftStdlib 5.1, *)
37+
struct Bop {
38+
@_unavailableFromAsync
39+
init() {} // expected-note 4 {{'init()' declared here}}
40+
41+
init(a: Int) { }
42+
}
43+
44+
@available(SwiftStdlib 5.1, *)
45+
extension Bop {
46+
@_unavailableFromAsync
47+
func foo() {} // expected-note 4 {{'foo()' declared here}}
48+
49+
50+
@_unavailableFromAsync
51+
mutating func muppet() { } // expected-note 4 {{'muppet()' declared here}}
52+
}
53+
54+
@_unavailableFromAsync
55+
@available(SwiftStdlib 5.1, *)
56+
func foo() {} // expected-note 4 {{'foo()' declared here}}
57+
58+
@available(SwiftStdlib 5.1, *)
59+
func makeAsyncClosuresSynchronously(bop: inout Bop) -> (() async -> Void) {
60+
return { () async -> Void in
61+
// Unavailable methods
62+
_ = Bop() // expected-warning@:9{{'init' is unavailable from asynchronous contexts}}
63+
_ = Bop(a: 32)
64+
bop.foo() // expected-warning@:9{{'foo' is unavailable from asynchronous contexts}}
65+
bop.muppet() // expected-warning@:9{{'muppet' is unavailable from asynchronous contexts}}
66+
unavailableFunction() // expected-warning@:5{{'unavailableFunction' is unavailable from asynchronous contexts}}
67+
68+
// Can use them from synchronous closures
69+
_ = { Bop() }()
70+
_ = { bop.foo() }()
71+
_ = { bop.muppet() }()
72+
73+
// Unavailable global function
74+
foo() // expected-warning{{'foo' is unavailable from asynchronous contexts}}
75+
76+
// Okay function
77+
okay()
78+
}
79+
}
80+
81+
@available(SwiftStdlib 5.1, *)
82+
@_unavailableFromAsync
83+
func asyncFunc() async { // expected-error{{asynchronous global function 'asyncFunc()' must be available from asynchronous contexts}}
84+
85+
var bop = Bop(a: 32)
86+
_ = Bop() // expected-warning@:7{{'init' is unavailable from asynchronous contexts}}
87+
bop.foo() // expected-warning@:7{{'foo' is unavailable from asynchronous contexts}}
88+
bop.muppet() // expected-warning@:7{{'muppet' is unavailable from asynchronous contexts}}
89+
unavailableFunction() // expected-warning@:3{{'unavailableFunction' is unavailable from asynchronous contexts}}
90+
91+
// Unavailable global function
92+
foo() // expected-warning{{'foo' is unavailable from asynchronous contexts}}
93+
94+
// Available function
95+
okay()
96+
97+
_ = { () -> Void in
98+
// Check unavailable things inside of a nested synchronous closure
99+
_ = Bop()
100+
foo()
101+
bop.foo()
102+
bop.muppet()
103+
unavailableFunction()
104+
105+
_ = { () async -> Void in
106+
// Check Unavailable things inside of a nested async closure
107+
foo() // expected-warning@:7{{'foo' is unavailable from asynchronous contexts}}
108+
bop.foo() // expected-warning@:11{{'foo' is unavailable from asynchronous contexts}}
109+
bop.muppet() // expected-warning@:11{{'muppet' is unavailable from asynchronous contexts}}
110+
_ = Bop() // expected-warning@:11{{'init' is unavailable from asynchronous contexts}}
111+
unavailableFunction() // expected-warning@:7{{'unavailableFunction' is unavailable from asynchronous contexts}}
112+
}
113+
}
114+
115+
_ = { () async -> Void in
116+
_ = Bop() // expected-warning@:9{{'init' is unavailable from asynchronous contexts}}
117+
foo() // expected-warning@:5{{'foo' is unavailable from asynchronous contexts}}
118+
bop.foo() // expected-warning@:9{{'foo' is unavailable from asynchronous contexts}}
119+
bop.muppet() // expected-warning@:9{{'muppet' is unavailable from asynchronous contexts}}
120+
unavailableFunction() // expected-warning@:5{{'unavailableFunction' is unavailable from asynchronous contexts}}
121+
122+
_ = {
123+
foo()
124+
bop.foo()
125+
_ = Bop()
126+
unavailableFunction()
127+
}
128+
}
129+
130+
}

test/IDE/complete_decl_attribute.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ struct MyStruct {}
7171
// KEYWORD2-NEXT: Keyword/None: derivative[#Func Attribute#]; name=derivative
7272
// KEYWORD2-NEXT: Keyword/None: transpose[#Func Attribute#]; name=transpose
7373
// KEYWORD2-NEXT: Keyword/None: noDerivative[#Func Attribute#]; name=noDerivative
74-
// KEYWORD2-NEXT: Keyword/None: Sendable[#Func Attribute#]; name=Sendable
74+
// KEYWORD2-NEXT: Keyword/None: Sendable[#Func Attribute#]; name=Sendable
75+
// KEYWORD2-NEXT: Keyword/None: _unavailableFromAsync[#Func Attribute#]; name=_unavailableFromAsync
7576
// KEYWORD2-NOT: Keyword
7677
// KEYWORD2: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct
7778
// KEYWORD2: End completions
@@ -155,6 +156,7 @@ struct _S {
155156
// ON_INIT-DAG: Keyword/None: inlinable[#Constructor Attribute#]; name=inlinable
156157
// ON_INIT-DAG: Keyword/None: usableFromInline[#Constructor Attribute#]; name=usableFromInline
157158
// ON_INIT-DAG: Keyword/None: discardableResult[#Constructor Attribute#]; name=discardableResult
159+
// ON_INIT-DAG: Keyword/None: _unavailableFromAsync[#Constructor Attribute#]; name=_unavailableFromAsync
158160
// ON_INIT: End completions
159161

160162
@#^ON_PROPERTY^# var foo
@@ -196,6 +198,7 @@ struct _S {
196198
// ON_METHOD-DAG: Keyword/None: transpose[#Func Attribute#]; name=transpose
197199
// ON_METHOD-DAG: Keyword/None: Sendable[#Func Attribute#]; name=Sendable
198200
// ON_METHOD-DAG: Keyword/None: noDerivative[#Func Attribute#]; name=noDerivative
201+
// ON_METHOD-DAG: Keyword/None: _unavailableFromAsync[#Func Attribute#]; name=_unavailableFromAsync
199202
// ON_METHOD-NOT: Keyword
200203
// ON_METHOD: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct
201204
// ON_METHOD: End completions
@@ -257,6 +260,7 @@ struct _S {
257260
// ON_MEMBER_LAST-DAG: Keyword/None: transpose[#Declaration Attribute#]; name=transpose
258261
// ON_MEMBER_LAST-DAG: Keyword/None: noDerivative[#Declaration Attribute#]; name=noDerivative
259262
// ON_MEMBER_LAST-DAG: Keyword/None: Sendable[#Declaration Attribute#]; name=Sendable
263+
// ON_MEMBER_LAST-DAG: Keyword/None: _unavailableFromAsync[#Declaration Attribute#]; name=_unavailableFromAsync
260264
// ON_MEMBER_LAST-NOT: Keyword
261265
// ON_MEMBER_LAST: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct
262266
// ON_MEMBER_LAST-NOT: Decl[PrecedenceGroup]
@@ -307,6 +311,7 @@ func dummy2() {}
307311
// KEYWORD_LAST-DAG: Keyword/None: transpose[#Declaration Attribute#]; name=transpose
308312
// KEYWORD_LAST-DAG: Keyword/None: noDerivative[#Declaration Attribute#]; name=noDerivative
309313
// KEYWORD_LAST-DAG: Keyword/None: Sendable[#Declaration Attribute#]; name=Sendable
314+
// KEYWORD_LAST-DAG: Keyword/None: _unavailableFromAsync[#Declaration Attribute#]; name=_unavailableFromAsync
310315
// KEYWORD_LAST-NOT: Keyword
311316
// KEYWORD_LAST: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct
312317
// KEYWORD_LAST: End completions

test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ typedef void (^CompletionHandler)(NSString * _Nullable, NSString * _Nullable_res
6666

6767
-(void)customizedWithString:(NSString *)operation completionHandler:(void (^)(NSInteger))handler __attribute__((swift_name("customize(with:completionHandler:)"))) __attribute__((swift_async_name("customize(_:)")));
6868

69+
-(void)unavailableMethod __attribute__((__swift_attr__("@_unavailableFromAsync")));
70+
6971
-(void)dance:(NSString *)step andThen:(void (^)(NSString *))doSomething __attribute__((swift_async(not_swift_private,2)));
7072
-(void)leap:(NSInteger)height andThen:(void (^)(NSString *))doSomething __attribute__((swift_async(swift_private,2)));
7173

0 commit comments

Comments
 (0)