Skip to content

Commit 734b1f1

Browse files
committed
[Frontend] Add a feature to guard use of @concurrent and nonisolated(nonsending) in swift interface files
1 parent 2b4ba84 commit 734b1f1

File tree

3 files changed

+119
-5
lines changed

3 files changed

+119
-5
lines changed

include/swift/Basic/Features.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ SUPPRESSIBLE_LANGUAGE_FEATURE(MemorySafetyAttributes, 458, "@unsafe attribute")
254254
LANGUAGE_FEATURE(ValueGenerics, 452, "Value generics feature (integer generics)")
255255
LANGUAGE_FEATURE(RawIdentifiers, 451, "Raw identifiers")
256256
LANGUAGE_FEATURE(SendableCompletionHandlers, 463, "Objective-C completion handler parameters are imported as @Sendable")
257+
LANGUAGE_FEATURE(AsyncExecutionBehaviorAttributes, 0, "@concurrent and nonisolated(nonsending)")
257258

258259
// Swift 6
259260
UPCOMING_FEATURE(ConciseMagicFile, 274, 6)

lib/AST/FeatureSet.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,57 @@ static bool usesFeatureBuiltinEmplaceTypedThrows(Decl *decl) {
502502
return false;
503503
}
504504

505+
static bool usesFeatureAsyncExecutionBehaviorAttributes(Decl *decl) {
506+
// Explicit `@concurrent` attribute on the declaration.
507+
if (decl->getAttrs().hasAttribute<ConcurrentAttr>())
508+
return true;
509+
510+
// Explicit `nonisolated(nonsending)` attribute on the declaration.
511+
if (auto *nonisolated = decl->getAttrs().getAttribute<NonisolatedAttr>()) {
512+
if (nonisolated->isNonSending())
513+
return true;
514+
}
515+
516+
auto hasCallerIsolatedAttr = [](TypeRepr *R) {
517+
if (!R)
518+
return false;
519+
520+
return R->findIf([](TypeRepr *repr) {
521+
if (isa<CallerIsolatedTypeRepr>(repr))
522+
return true;
523+
524+
// We don't check for @concurrent here because it's
525+
// not printed in type positions since it indicates
526+
// old "nonisolated" state.
527+
528+
return false;
529+
});
530+
};
531+
532+
auto *VD = dyn_cast<ValueDecl>(decl);
533+
if (!VD)
534+
return false;
535+
536+
// The declaration is going to be printed with `nonisolated(nonsending)`
537+
// attribute.
538+
if (getActorIsolation(VD).isCallerIsolationInheriting())
539+
return true;
540+
541+
// Check if any parameters that have `nonisolated(nonsending)` attribute.
542+
if (auto *PL = VD->getParameterList()) {
543+
if (llvm::any_of(*PL, [&](const ParamDecl *P) {
544+
return hasCallerIsolatedAttr(P->getTypeRepr());
545+
}))
546+
return true;
547+
}
548+
549+
// Check if result type has explicit `nonisolated(nonsending)` attribute.
550+
if (hasCallerIsolatedAttr(VD->getResultTypeRepr()))
551+
return true;
552+
553+
return false;
554+
}
555+
505556
// ----------------------------------------------------------------------------
506557
// MARK: - FeatureSet
507558
// ----------------------------------------------------------------------------

test/ModuleInterface/execution_behavior_attrs.swift

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,37 +6,99 @@
66
// REQUIRES: concurrency
77

88
public struct Test {
9-
// CHECK: nonisolated(nonsending) public init() async
9+
// CHECK: #if compiler(>=5.3) && $AsyncExecutionBehaviorAttributes
10+
// CHECK-NEXT: nonisolated(nonsending) public init() async
11+
// CHECK-NEXT: #endif
1012
nonisolated(nonsending)
1113
public init() async {
1214
}
1315

14-
// CHECK: @concurrent public func test() async
16+
// CHECK: #if compiler(>=5.3) && $AsyncExecutionBehaviorAttributes
17+
// CHECK-NEXT: @concurrent public init(something: Swift.Int) async
18+
// CHECK-NEXT: #endif
19+
@concurrent
20+
public init(something: Int) async {
21+
}
22+
23+
// CHECK-NOT: #if compiler(>=5.3) && $AsyncExecutionBehaviorAttributes
24+
// CHECK: public init(noExplicit: Swift.String) async
25+
// CHECK-NOT: #endif
26+
public init(noExplicit: String) async {
27+
}
28+
29+
// CHECK: #if compiler(>=5.3) && $AsyncExecutionBehaviorAttributes
30+
// CHECK-NEXT: @concurrent public func test() async
31+
// CHECK-NEXT: #endif
1532
@concurrent
1633
public func test() async {
1734
}
1835

19-
// CHECK: public func other(_: nonisolated(nonsending) () async -> Swift.Void)
36+
// CHECK: #if compiler(>=5.3) && $AsyncExecutionBehaviorAttributes
37+
// CHECK-NEXT: public func other(_: nonisolated(nonsending) () async -> Swift.Void)
38+
// CHECK-NEXT: #endif
2039
public func other(_: nonisolated(nonsending) () async -> Void) {}
2140

22-
// CHECK: nonisolated(nonsending) public var testOnVar: Swift.Int {
41+
// CHECK-NOT: #if compiler(>=5.3) && $AsyncExecutionBehaviorAttributes
42+
// CHECK: public func concurrentResult(_: () async -> Swift.Void) -> (Swift.Int) async -> Swift.Void
43+
// CHECK-NOT: #endif
44+
public func concurrentResult(_: () async -> Void) -> @concurrent (Int) async -> Void {}
45+
46+
// CHECK: #if compiler(>=5.3) && $AsyncExecutionBehaviorAttributes
47+
// CHECK-NEXT: public func nestedPositions1(_: Swift.Array<[Swift.String : nonisolated(nonsending) (Swift.Int) async -> Swift.Void]>)
48+
// CHECK-NEXT: #endif
49+
public func nestedPositions1(_: Array<[String: nonisolated(nonsending) (Int) async -> Void]>) {}
50+
51+
// CHECK-NOT: #if compiler(>=5.3) && $AsyncExecutionBehaviorAttributes
52+
// CHECK: public func nestedPositions2(_: Swift.Array<[Swift.String : (Swift.Int) async -> Swift.Void]>)
53+
// CHECK-NOT: #endif
54+
public func nestedPositions2(_: Array<[String: @concurrent (Int) async -> Void]>) {}
55+
56+
// CHECK: #if compiler(>=5.3) && $AsyncExecutionBehaviorAttributes
57+
// CHECK-NEXT: nonisolated(nonsending) public var testOnVar: Swift.Int {
2358
// CHECK-NEXT: get async
2459
// CHECK-NEXT: }
60+
// CHECK-NEXT: #endif
2561
nonisolated(nonsending)
2662
public var testOnVar: Int {
2763
get async {
2864
42
2965
}
3066
}
3167

32-
// CHECK: nonisolated(nonsending) public subscript(onSubscript _: Swift.Int) -> Swift.Bool {
68+
// CHECK: #if compiler(>=5.3) && $AsyncExecutionBehaviorAttributes
69+
// CHECK-NEXT: @concurrent public var testOnVarConcurrent: Swift.Int {
70+
// CHECK-NEXT: get async
71+
// CHECK-NEXT: }
72+
// CHECK-NEXT: #endif
73+
@concurrent
74+
public var testOnVarConcurrent: Int {
75+
get async {
76+
42
77+
}
78+
}
79+
80+
// CHECK: #if compiler(>=5.3) && $AsyncExecutionBehaviorAttributes
81+
// CHECK-NEXT: nonisolated(nonsending) public subscript(onSubscript _: Swift.Int) -> Swift.Bool {
3382
// CHECK-NEXT: get async
3483
// CHECK-NEXT: }
84+
// CHECK-NEXT: #endif
3585
nonisolated(nonsending)
3686
public subscript(onSubscript _: Int) -> Bool {
3787
get async {
3888
false
3989
}
4090
}
91+
92+
// CHECK: #if compiler(>=5.3) && $AsyncExecutionBehaviorAttributes
93+
// CHECK-NEXT: @concurrent public subscript(concurrentOnSubscript _: Swift.Int) -> Swift.Bool {
94+
// CHECK-NEXT: get async
95+
// CHECK-NEXT: }
96+
// CHECK-NEXT: #endif
97+
@concurrent
98+
public subscript(concurrentOnSubscript _: Int) -> Bool {
99+
get async {
100+
false
101+
}
102+
}
41103
}
42104

0 commit comments

Comments
 (0)