Skip to content

Commit e121dd3

Browse files
authored
Merge pull request #60672 from tshortli/global-actor-access-control
Sema: Diagnose global actor attribute access and availability
2 parents 30576e5 + c13dd18 commit e121dd3

12 files changed

+226
-0
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4892,6 +4892,14 @@ ERROR(global_actor_non_final_class,none,
48924892
"non-final class %0 cannot be a global actor", (DeclName))
48934893
ERROR(global_actor_top_level_var,none,
48944894
"top-level code variables cannot have a global actor", ())
4895+
ERROR(global_actor_access,none,
4896+
"%select{private|fileprivate|internal|public|open}0 %1 %2 "
4897+
"cannot have %select{private|fileprivate|internal|%error|%error}3 "
4898+
"global actor %4",
4899+
(AccessLevel, DescriptiveDeclKind, DeclName, AccessLevel, DeclName))
4900+
ERROR(global_actor_not_usable_from_inline,none,
4901+
"global actor for %0 %1 must be '@usableFromInline' or public",
4902+
(DescriptiveDeclKind, DeclName))
48954903

48964904
ERROR(actor_isolation_multiple_attr,none,
48974905
"%0 %1 has multiple actor-isolation attributes ('%2' and '%3')",

lib/Sema/TypeCheckAccess.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ class AccessControlCheckerBase {
108108
void checkGenericParamAccess(
109109
const GenericContext *ownerCtx,
110110
const ValueDecl *ownerDecl);
111+
112+
void checkGlobalActorAccess(const Decl *D);
111113
};
112114

113115
class TypeAccessScopeDiagnoser : private ASTWalker {
@@ -409,6 +411,41 @@ void AccessControlCheckerBase::checkGenericParamAccess(
409411
ownerDecl->getFormalAccess());
410412
}
411413

414+
void AccessControlCheckerBase::checkGlobalActorAccess(const Decl *D) {
415+
auto VD = dyn_cast<ValueDecl>(D);
416+
if (!VD)
417+
return;
418+
419+
auto globalActorAttr = D->getGlobalActorAttr();
420+
if (!globalActorAttr)
421+
return;
422+
423+
auto customAttr = globalActorAttr->first;
424+
auto globalActorDecl = globalActorAttr->second;
425+
checkTypeAccess(
426+
customAttr->getType(), customAttr->getTypeRepr(), VD,
427+
/*mayBeInferred*/ false,
428+
[&](AccessScope typeAccessScope, const TypeRepr *complainRepr,
429+
DowngradeToWarning downgradeToWarning) {
430+
if (checkUsableFromInline) {
431+
auto diag = D->diagnose(diag::global_actor_not_usable_from_inline,
432+
D->getDescriptiveKind(), VD->getName());
433+
highlightOffendingType(diag, complainRepr);
434+
return;
435+
}
436+
437+
auto globalActorAccess = typeAccessScope.accessLevelForDiagnostics();
438+
bool isExplicit = D->getAttrs().hasAttribute<AccessControlAttr>();
439+
auto declAccess = isExplicit
440+
? VD->getFormalAccess()
441+
: typeAccessScope.requiredAccessForDiagnostics();
442+
auto diag = D->diagnose(diag::global_actor_access, declAccess,
443+
D->getDescriptiveKind(), VD->getName(),
444+
globalActorAccess, globalActorDecl->getName());
445+
highlightOffendingType(diag, complainRepr);
446+
});
447+
}
448+
412449
namespace {
413450
class AccessControlChecker : public AccessControlCheckerBase,
414451
public DeclVisitor<AccessControlChecker> {
@@ -425,6 +462,7 @@ class AccessControlChecker : public AccessControlCheckerBase,
425462
return;
426463

427464
DeclVisitor<AccessControlChecker>::visit(D);
465+
checkGlobalActorAccess(D);
428466
}
429467

430468
// Force all kinds to be handled at a lower level.
@@ -1047,6 +1085,7 @@ class UsableFromInlineChecker : public AccessControlCheckerBase,
10471085
return;
10481086

10491087
DeclVisitor<UsableFromInlineChecker>::visit(D);
1088+
checkGlobalActorAccess(D);
10501089
}
10511090

10521091
// Force all kinds to be handled at a lower level.
@@ -1638,6 +1677,15 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
16381677
explicit DeclAvailabilityChecker(ExportContext where)
16391678
: Where(where) {}
16401679

1680+
void visit(Decl *D) {
1681+
DeclVisitor<DeclAvailabilityChecker>::visit(D);
1682+
1683+
if (auto globalActor = D->getGlobalActorAttr()) {
1684+
auto customAttr = globalActor->first;
1685+
checkType(customAttr->getType(), customAttr->getTypeRepr(), D);
1686+
}
1687+
}
1688+
16411689
// Force all kinds to be handled at a lower level.
16421690
void visitDecl(Decl *D) = delete;
16431691
void visitValueDecl(ValueDecl *D) = delete;

test/ClangImporter/objc_async.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ class BazFrame: NotIsolatedPictureFrame {
311311
}
312312
}
313313

314+
@available(SwiftStdlib 5.5, *)
314315
@SomeGlobalActor
315316
class BazFrameIso: PictureFrame { // expected-error {{global actor 'SomeGlobalActor'-isolated class 'BazFrameIso' has different actor isolation from main actor-isolated superclass 'PictureFrame'}}
316317
}
@@ -387,6 +388,7 @@ extension SomeWrapper: Sendable where T: Sendable {}
387388

388389

389390
// rdar://96830159
391+
@available(SwiftStdlib 5.5, *)
390392
@MainActor class SendableCompletionHandler {
391393
var isolatedThing: [String] = []
392394
// expected-note@-1 {{property declared here}}

test/Concurrency/concurrent_value_inference.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,11 @@ actor MyGlobalActor {
9191
static let shared = MyGlobalActor()
9292
}
9393

94+
@available(SwiftStdlib 5.1, *)
9495
@MyGlobalActor
9596
class C3 { }
9697

98+
@available(SwiftStdlib 5.1, *)
9799
class C4: C3 { }
98100

99101
// Make Sendable unavailable, but be sure not to diagnose it.
@@ -104,6 +106,7 @@ struct S2 {
104106
@available(*, unavailable)
105107
extension S2: Sendable { }
106108

109+
@available(SwiftStdlib 5.1, *)
107110
func testCV(
108111
c1: C1, c2: C2, c3: C3, c4: C4, s1: S1, e1: E1, e2: E2,
109112
gs1: GS1<Int>, gs2: GS2<Int>,

test/Concurrency/flow_isolation_nonstrict.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ actor CheckDeinitFromActor {
3030
}
3131
}
3232

33+
@available(SwiftStdlib 5.1, *)
3334
@MainActor class X {
3435
var ns: NonSendableType = NonSendableType()
3536

test/Concurrency/require-explicit-sendable.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,16 @@ struct S11: Sendable {
101101
@_nonSendable public struct S12 { }
102102

103103
// Don't complain about global-actor-qualified classes or their subclasses.
104+
@available(SwiftStdlib 5.1, *)
104105
@MainActor
105106
open class TestThing {}
107+
108+
@available(SwiftStdlib 5.1, *)
106109
open class TestSubThing : TestThing {}
107110

111+
@available(SwiftStdlib 5.1, *)
108112
@MainActor(unsafe)
109113
open class TestThing2 {}
114+
115+
@available(SwiftStdlib 5.1, *)
110116
open class TestSubThing2 : TestThing2 {}

test/Concurrency/sendable_conformance_checking.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,10 @@ final class SubKlass: Klass<[S]> {}
159159
public struct S {}
160160

161161
// rdar://88700507 - redundant conformance of @MainActor-isolated subclass to 'Sendable'
162+
@available(SwiftStdlib 5.1, *)
162163
@MainActor class MainSuper {}
164+
165+
@available(SwiftStdlib 5.1, *)
163166
class MainSub: MainSuper, @unchecked Sendable {}
164167

165168
class SendableSuper: @unchecked Sendable {}

test/SILGen/functions_uninhabited_param.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ enum E {
2525
static func f(_: E) {}
2626
}
2727

28+
@available(SwiftStdlib 5.1, *)
2829
@MainActor
2930
class Bar {
3031
var foo: (E) -> Void = { _ in }

test/SPI/spi_global_actor.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
// REQUIRES: concurrency
4+
5+
@available(SwiftStdlib 5.1, *)
6+
@_spi(Foo)
7+
@globalActor
8+
public struct SPIGA { // expected-note {{type declared here}}
9+
public actor Actor {}
10+
public static let shared = Actor()
11+
}
12+
13+
@available(SwiftStdlib 5.1, *)
14+
@SPIGA // expected-error {{cannot use struct 'SPIGA' here; it is SPI}}
15+
public struct PublicStruct {}
16+
17+
@available(SwiftStdlib 5.1, *)
18+
@_spi(Foo)
19+
@SPIGA
20+
public struct SPIStruct {}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// RUN: %target-typecheck-verify-swift -target %target-cpu-apple-macosx10.50
2+
3+
// REQUIRES: concurrency
4+
// REQUIRES: OS=macosx
5+
6+
actor SomeActor {}
7+
8+
@globalActor struct AlwaysAvailableGA {
9+
static let shared = SomeActor()
10+
}
11+
12+
@available(macOS 10.51, *)
13+
@globalActor struct Available10_51GA {
14+
static let shared = SomeActor()
15+
}
16+
17+
@available(*, unavailable)
18+
@globalActor struct UnavailableGA { // expected-note {{'UnavailableGA' has been explicitly marked unavailable here}}
19+
static let shared = SomeActor()
20+
}
21+
22+
@AlwaysAvailableGA
23+
struct AlwaysAvailableWithAlwaysAvailableGA {}
24+
25+
@Available10_51GA // expected-error {{'Available10_51GA' is only available in macOS 10.51 or newer}}
26+
struct AlwaysAvailableWithAvailable10_51GA {} // expected-note {{add @available attribute to enclosing struct}}
27+
28+
@available(macOS 10.51, *)
29+
@Available10_51GA
30+
struct Always10_51WithAvailable10_51GA {}
31+
32+
@UnavailableGA // expected-error {{'UnavailableGA' is unavailable}}
33+
struct AlwaysAvailableWithUnavailableGA {}
34+
35+
@available(*, unavailable)
36+
@UnavailableGA
37+
struct UnavailableWithUnavailableGA {}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// RUN: %target-typecheck-verify-swift -swift-version 5
2+
3+
// REQUIRES: concurrency
4+
5+
@available(SwiftStdlib 5.1, *)
6+
@globalActor
7+
private struct PrivateGA { // expected-note 2 {{type declared here}}
8+
actor Actor {}
9+
static let shared = Actor()
10+
}
11+
12+
@available(SwiftStdlib 5.1, *)
13+
@globalActor
14+
internal struct InternalGA { // expected-note {{type declared here}}
15+
actor Actor {}
16+
static let shared = Actor()
17+
}
18+
19+
@available(SwiftStdlib 5.1, *)
20+
@globalActor
21+
@usableFromInline
22+
internal struct UFIGA {
23+
@usableFromInline actor Actor {}
24+
@usableFromInline static let shared = Actor()
25+
}
26+
27+
@available(SwiftStdlib 5.1, *)
28+
@globalActor
29+
public struct PublicGA {
30+
public actor Actor {}
31+
public static let shared = Actor()
32+
}
33+
34+
// expected-error@+2 {{internal struct 'UFIStructPrivateGA' cannot have private global actor 'PrivateGA'}}
35+
@available(SwiftStdlib 5.1, *)
36+
@PrivateGA @usableFromInline internal struct UFIStructPrivateGA {} // expected-error {{global actor for struct 'UFIStructPrivateGA' must be '@usableFromInline' or public}}
37+
@available(SwiftStdlib 5.1, *)
38+
@InternalGA @usableFromInline internal struct UFIStructInternalGA {} // expected-error {{global actor for struct 'UFIStructInternalGA' must be '@usableFromInline' or public}}
39+
@available(SwiftStdlib 5.1, *)
40+
@UFIGA @usableFromInline internal struct UFIStructUFIGA {}
41+
@available(SwiftStdlib 5.1, *)
42+
@PublicGA @usableFromInline internal struct UFIStructPublicGA {}
43+
44+
@available(SwiftStdlib 5.1, *)
45+
@inlinable public func testNestedFuncs() {
46+
// FIXME: Functions isolated to non-resilient global actors nested in
47+
// inlinable functions should be diagnosed.
48+
@PrivateGA func inlineFuncPrivateGA() {}
49+
@InternalGA func inlineFuncInternalGA() {}
50+
@UFIGA func inlineFuncUFIGA() {}
51+
@PublicGA func inlineFuncPublicGA() {}
52+
}
53+
54+
@available(SwiftStdlib 5.1, *)
55+
@inlinable public func testNestedClosures() {
56+
// FIXME: Closures isolated to non-resilient global actors nested in
57+
// inlinable functions should be diagnosed.
58+
_ = { @PrivateGA in }
59+
_ = { @InternalGA in }
60+
_ = { @UFIGA in }
61+
_ = { @PublicGA in }
62+
}

test/attr/global_actor.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,38 @@ struct Container {
100100
extension SomeActor {
101101
@GA1 nonisolated func conflict1() { } // expected-error 3{{instance method 'conflict1()' has multiple actor-isolation attributes ('nonisolated' and 'GA1')}}
102102
}
103+
104+
105+
// -----------------------------------------------------------------------
106+
// Access
107+
// -----------------------------------------------------------------------
108+
109+
@globalActor
110+
private struct PrivateGA { // expected-note 2 {{type declared here}}
111+
actor Actor {}
112+
static let shared = Actor()
113+
}
114+
115+
@globalActor
116+
internal struct InternalGA { // expected-note 1 {{type declared here}}
117+
actor Actor {}
118+
static let shared = Actor()
119+
}
120+
121+
@globalActor
122+
public struct PublicGA {
123+
public actor Actor {}
124+
public static let shared = Actor()
125+
}
126+
127+
@PrivateGA private struct PrivateStructPrivateGA {}
128+
@InternalGA private struct PrivateStructInternalGA {}
129+
@PublicGA private struct PrivateStructPublicGA {}
130+
131+
@PrivateGA internal struct InternalStructPrivateGA {} // expected-error {{internal struct 'InternalStructPrivateGA' cannot have private global actor 'PrivateGA'}}
132+
@InternalGA internal struct InternalStructInternalGA {}
133+
@PublicGA internal struct InternalStructPublicGA {}
134+
135+
@PrivateGA open class OpenClassPrivateGA {} // expected-error {{open class 'OpenClassPrivateGA' cannot have private global actor 'PrivateGA'}}
136+
@InternalGA open class OpenClassInternalGA {} // expected-error {{open class 'OpenClassInternalGA' cannot have internal global actor 'InternalGA'}}
137+
@PublicGA open class OpenClassPublicGA {}

0 commit comments

Comments
 (0)