Skip to content

Commit e5c1491

Browse files
committed
[Concurrency] Ban actor-isolated operations from being @objc.
Actor-isolated operations must not be directly accessible from anywhere that is not already guaranteed to be running within the actor context. Prevent such operations from being `@objc`, because that would allow Objective-C code to violate actor isolation.
1 parent 34996bf commit e5c1491

File tree

3 files changed

+67
-4
lines changed

3 files changed

+67
-4
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4101,6 +4101,9 @@ ERROR(not_objc_function_async,none,
41014101
"'async' function cannot be represented in Objective-C", ())
41024102
NOTE(not_objc_function_type_async,none,
41034103
"'async' function types cannot be represented in Objective-C", ())
4104+
ERROR(actor_isolated_objc,none,
4105+
"actor-isolated %0 %1 cannot be @objc",
4106+
(DescriptiveDeclKind, DeclName))
41044107
NOTE(protocol_witness_async_conflict,none,
41054108
"candidate is %select{not |}0'async', but protocol requirement is%select{| not}0",
41064109
(bool))

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
//===----------------------------------------------------------------------===//
1717
#include "TypeCheckObjC.h"
1818
#include "TypeChecker.h"
19+
#include "TypeCheckConcurrency.h"
1920
#include "TypeCheckProtocol.h"
2021
#include "swift/AST/ASTContext.h"
2122
#include "swift/AST/Decl.h"
@@ -368,6 +369,32 @@ static bool checkObjCInForeignClassContext(const ValueDecl *VD,
368369
return true;
369370
}
370371

372+
/// Actor-isolated declarations cannot be @objc.
373+
static bool checkObjCActorIsolation(const ValueDecl *VD,
374+
ObjCReason Reason) {
375+
// Check actor isolation.
376+
bool Diagnose = shouldDiagnoseObjCReason(Reason, VD->getASTContext());
377+
378+
switch (getActorIsolation(const_cast<ValueDecl *>(VD))) {
379+
case ActorIsolation::ActorInstance:
380+
// Actor-isolated functions cannot be @objc.
381+
if (Diagnose) {
382+
VD->diagnose(
383+
diag::actor_isolated_objc, VD->getDescriptiveKind(), VD->getName());
384+
describeObjCReason(VD, Reason);
385+
if (auto FD = dyn_cast<FuncDecl>(VD)) {
386+
addAsyncNotes(const_cast<FuncDecl *>(FD));
387+
}
388+
}
389+
return true;
390+
391+
case ActorIsolation::ActorPrivileged:
392+
case ActorIsolation::Independent:
393+
case ActorIsolation::Unspecified:
394+
return false;
395+
}
396+
}
397+
371398
static VersionRange getMinOSVersionForClassStubs(const llvm::Triple &target) {
372399
if (target.isMacOSX())
373400
return VersionRange::allGTE(llvm::VersionTuple(10, 15, 0));
@@ -512,6 +539,8 @@ bool swift::isRepresentableInObjC(
512539
return false;
513540
if (checkObjCInExtensionContext(AFD, Diagnose))
514541
return false;
542+
if (checkObjCActorIsolation(AFD, Reason))
543+
return false;
515544

516545
if (AFD->isOperator()) {
517546
AFD->diagnose((isa<ProtocolDecl>(AFD->getDeclContext())
@@ -686,10 +715,12 @@ bool swift::isRepresentableInObjC(
686715
Type resultType = FD->mapTypeIntoContext(FD->getResultInterfaceType());
687716
if (auto tupleType = resultType->getAs<TupleType>()) {
688717
for (const auto &tupleElt : tupleType->getElements()) {
689-
addCompletionHandlerParam(tupleElt.getType());
718+
if (addCompletionHandlerParam(tupleElt.getType()))
719+
return false;
690720
}
691721
} else {
692-
addCompletionHandlerParam(resultType);
722+
if (addCompletionHandlerParam(resultType))
723+
return false;
693724
}
694725

695726
// For a throwing asynchronous function, an Error? parameter is added
@@ -939,6 +970,8 @@ bool swift::isRepresentableInObjC(const VarDecl *VD, ObjCReason Reason) {
939970

940971
if (checkObjCInForeignClassContext(VD, Reason))
941972
return false;
973+
if (checkObjCActorIsolation(VD, Reason))
974+
return false;
942975

943976
if (!Diagnose || Result)
944977
return Result;
@@ -967,6 +1000,8 @@ bool swift::isRepresentableInObjC(const SubscriptDecl *SD, ObjCReason Reason) {
9671000
return false;
9681001
if (checkObjCWithGenericParams(SD, Reason))
9691002
return false;
1003+
if (checkObjCActorIsolation(SD, Reason))
1004+
return false;
9701005

9711006
// ObjC doesn't support class subscripts.
9721007
if (!SD->isInstanceMember()) {

test/attr/attr_objc_async.swift

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
// RUN: not %target-swift-frontend -typecheck -dump-ast -disable-objc-attr-requires-foundation-module %s -swift-version 5 -enable-source-import -I %S/Inputs -enable-experimental-concurrency > %t.ast
44
// RUN: %FileCheck -check-prefix CHECK-DUMP %s < %t.ast
55
// REQUIRES: objc_interop
6+
// REQUIRES: concurrency
67
import Foundation
78

8-
// CHECK: class Concurrency
9-
class Concurrency {
9+
// CHECK: class MyClass
10+
class MyClass {
1011
// CHECK: @objc func doBigJob() async -> Int
1112
// CHECK-DUMP: func_decl{{.*}}doBigJob{{.*}}foreign_async=@convention(block) (Int) -> (),completion_handler_param=0
1213
@objc func doBigJob() async -> Int { return 0 }
@@ -21,3 +22,27 @@ class Concurrency {
2122
@objc class func createAsynchronously() async -> Self? { nil }
2223
// expected-error@-1{{asynchronous method returning 'Self' cannot be '@objc'}}
2324
}
25+
26+
// Actor class exporting Objective-C entry points.
27+
28+
// CHECK: class MyActor
29+
actor class MyActor {
30+
// CHECK: @objc func doBigJob() async -> Int
31+
// CHECK-DUMP: func_decl{{.*}}doBigJob{{.*}}foreign_async=@convention(block) (Int) -> (),completion_handler_param=0
32+
@objc func doBigJob() async -> Int { return 0 }
33+
34+
// CHECK: @objc func doBigJobOrFail(_: Int) async throws -> (AnyObject, Int)
35+
// CHECK-DUMP: func_decl{{.*}}doBigJobOrFail{{.*}}foreign_async=@convention(block) (Optional<AnyObject>, Int, Optional<Error>) -> (),completion_handler_param=1,error_param=2
36+
@objc func doBigJobOrFail(_: Int) async throws -> (AnyObject, Int) { return (self, 0) }
37+
38+
// Actor-isolated entities cannot be exposed to Objective-C.
39+
@objc func synchronousBad() { } // expected-error{{actor-isolated instance method 'synchronousBad()' cannot be @objc}}
40+
// expected-note@-1{{add 'async' to function 'synchronousBad()' to make it asynchronous}}
41+
// expected-note@-2{{add '@asyncHandler' to function 'synchronousBad()' to create an implicit asynchronous context}}
42+
43+
@objc var badProp: AnyObject { self } // expected-error{{actor-isolated property 'badProp' cannot be @objc}}
44+
@objc subscript(index: Int) -> AnyObject { self } // expected-error{{actor-isolated subscript 'subscript(_:)' cannot be @objc}}
45+
46+
// CHECK: @objc @actorIndependent func synchronousGood()
47+
@objc @actorIndependent func synchronousGood() { }
48+
}

0 commit comments

Comments
 (0)