Skip to content

Commit c1bc470

Browse files
authored
Merge pull request swiftlang#36580 from DougGregor/import-main-actor-sendable-closures
2 parents 402ce13 + 45d3c88 commit c1bc470

File tree

6 files changed

+110
-25
lines changed

6 files changed

+110
-25
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8018,6 +8018,19 @@ SourceFile &ClangImporter::Implementation::getClangSwiftAttrSourceFile(
80188018
return *sourceFile;
80198019
}
80208020

8021+
Optional<bool> swift::importer::isMainActorAttr(
8022+
ASTContext &ctx, const clang::SwiftAttrAttr *swiftAttr) {
8023+
if (swiftAttr->getAttribute() == "@MainActor" ||
8024+
swiftAttr->getAttribute() == "@MainActor(unsafe)" ||
8025+
swiftAttr->getAttribute() == "@UIActor") {
8026+
bool isUnsafe = swiftAttr->getAttribute() == "@MainActor(unsafe)" ||
8027+
!ctx.LangOpts.isSwiftVersionAtLeast(6);
8028+
return isUnsafe;
8029+
}
8030+
8031+
return None;
8032+
}
8033+
80218034
/// Import Clang attributes as Swift attributes.
80228035
void ClangImporter::Implementation::importAttributes(
80238036
const clang::NamedDecl *ClangDecl,
@@ -8188,11 +8201,8 @@ void ClangImporter::Implementation::importAttributes(
81888201
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(*AI)) {
81898202
// FIXME: Hard-core @MainActor and @UIActor, because we don't have a
81908203
// point at which to do name lookup for imported entities.
8191-
if (swiftAttr->getAttribute() == "@MainActor" ||
8192-
swiftAttr->getAttribute() == "@MainActor(unsafe)" ||
8193-
swiftAttr->getAttribute() == "@UIActor") {
8194-
bool isUnsafe = swiftAttr->getAttribute() == "@MainActor(unsafe)" ||
8195-
!C.LangOpts.isSwiftVersionAtLeast(6);
8204+
if (auto isMainActor = isMainActorAttr(SwiftContext, swiftAttr)) {
8205+
bool isUnsafe = *isMainActor;
81968206
if (Type mainActorType = getMainActorType()) {
81978207
auto typeExpr = TypeExpr::createImplicit(mainActorType, SwiftContext);
81988208
auto attr = CustomAttr::create(SwiftContext, SourceLoc(), typeExpr);

lib/ClangImporter/ImportType.cpp

Lines changed: 52 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1693,17 +1693,61 @@ ImportedType ClangImporter::Implementation::importPropertyType(
16931693
Bridgeability::Full, optionality);
16941694
}
16951695

1696-
/// Apply the @noescape attribute
1697-
static Type applyNoEscape(Type type) {
1696+
/// Apply an attribute to a function type.
1697+
static Type applyToFunctionType(
1698+
Type type, llvm::function_ref<ASTExtInfo(ASTExtInfo)> transform) {
16981699
// Recurse into optional types.
16991700
if (Type objectType = type->getOptionalObjectType()) {
1700-
return OptionalType::get(applyNoEscape(objectType));
1701+
return OptionalType::get(applyToFunctionType(objectType, transform));
17011702
}
17021703

17031704
// Apply @noescape to function types.
17041705
if (auto funcType = type->getAs<FunctionType>()) {
17051706
return FunctionType::get(funcType->getParams(), funcType->getResult(),
1706-
funcType->getExtInfo().withNoEscape());
1707+
transform(funcType->getExtInfo()));
1708+
}
1709+
1710+
return type;
1711+
}
1712+
1713+
Type ClangImporter::Implementation::applyParamAttributes(
1714+
const clang::ParmVarDecl *param, Type type) {
1715+
if (!param->hasAttrs())
1716+
return type;
1717+
1718+
for (auto attr : param->getAttrs()) {
1719+
// Map __attribute__((noescape)) to @noescape.
1720+
if (isa<clang::NoEscapeAttr>(attr)) {
1721+
type = applyToFunctionType(type, [](ASTExtInfo extInfo) {
1722+
return extInfo.withNoEscape();
1723+
});
1724+
1725+
continue;
1726+
}
1727+
1728+
auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr);
1729+
if (!swiftAttr)
1730+
continue;
1731+
1732+
// Map the main-actor attribute.
1733+
if (isMainActorAttr(SwiftContext, swiftAttr)) {
1734+
if (Type mainActor = getMainActorType()) {
1735+
type = applyToFunctionType(type, [&](ASTExtInfo extInfo) {
1736+
return extInfo.withGlobalActor(mainActor);
1737+
});
1738+
}
1739+
1740+
continue;
1741+
}
1742+
1743+
// Map @Sendable.
1744+
if (swiftAttr->getAttribute() == "@Sendable") {
1745+
type = applyToFunctionType(type, [](ASTExtInfo extInfo) {
1746+
return extInfo.withConcurrent();
1747+
});
1748+
1749+
continue;
1750+
}
17071751
}
17081752

17091753
return type;
@@ -1883,13 +1927,8 @@ ParameterList *ClangImporter::Implementation::importFunctionParameterList(
18831927
swiftParamTy = importedType.getType();
18841928
}
18851929

1886-
// Map __attribute__((noescape)) to @noescape.
1887-
if (param->hasAttr<clang::NoEscapeAttr>()) {
1888-
Type newParamTy = applyNoEscape(swiftParamTy);
1889-
if (newParamTy.getPointer() != swiftParamTy.getPointer()) {
1890-
swiftParamTy = newParamTy;
1891-
}
1892-
}
1930+
// Apply attributes to the type.
1931+
swiftParamTy = applyParamAttributes(param, swiftParamTy);
18931932

18941933
// Figure out the name for this parameter.
18951934
Identifier bodyName = importFullName(param, CurrentVersion)
@@ -2384,15 +2423,8 @@ ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType(
23842423
llvm_unreachable("async info computed incorrectly?");
23852424
}
23862425

2387-
// Map __attribute__((noescape)) to @noescape.
2388-
bool addNoEscapeAttr = false;
2389-
if (param->hasAttr<clang::NoEscapeAttr>()) {
2390-
Type newParamTy = applyNoEscape(swiftParamTy);
2391-
if (newParamTy.getPointer() != swiftParamTy.getPointer()) {
2392-
swiftParamTy = newParamTy;
2393-
addNoEscapeAttr = true;
2394-
}
2395-
}
2426+
// Apply Clang attributes to the parameter type.
2427+
swiftParamTy = applyParamAttributes(param, swiftParamTy);
23962428

23972429
// Figure out the name for this parameter.
23982430
Identifier bodyName = importFullName(param, CurrentVersion)

lib/ClangImporter/ImporterImpl.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,8 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
830830
void importAttributes(const clang::NamedDecl *ClangDecl, Decl *MappedDecl,
831831
const clang::ObjCContainerDecl *NewContext = nullptr);
832832

833+
Type applyParamAttributes(const clang::ParmVarDecl *param, Type type);
834+
833835
/// If we already imported a given decl, return the corresponding Swift decl.
834836
/// Otherwise, return nullptr.
835837
Decl *importDeclCached(const clang::NamedDecl *ClangDecl, Version version,
@@ -1562,6 +1564,14 @@ class SwiftNameLookupExtension : public clang::ModuleFileExtension {
15621564
const llvm::BitstreamCursor &stream) override;
15631565
};
15641566

1567+
/// Determines whether the given swift_attr attribute describes the main
1568+
/// actor.
1569+
///
1570+
/// \returns None if this is not a main-actor attribute, and a Boolean
1571+
/// indicating whether (unsafe) was provided in the attribute otherwise.
1572+
Optional<bool> isMainActorAttr(
1573+
ASTContext &ctx, const clang::SwiftAttrAttr *swiftAttr);
1574+
15651575
}
15661576
}
15671577

test/ClangImporter/objc_async.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import Foundation
66
import ObjCConcurrency
77

8+
@MainActor func onlyOnMainActor() { }
9+
810
func testSlowServer(slowServer: SlowServer) async throws {
911
let _: Int = await slowServer.doSomethingSlow("mail")
1012
let _: Bool = await slowServer.checkAvailability()
@@ -49,6 +51,8 @@ func testSlowServer(slowServer: SlowServer) async throws {
4951

5052

5153
_ = await slowServer.operations()
54+
55+
_ = await slowServer.runOnMainThread()
5256
}
5357

5458
func testSlowServerSynchronous(slowServer: SlowServer) {
@@ -63,6 +67,11 @@ func testSlowServerSynchronous(slowServer: SlowServer) {
6367

6468
let s = slowServer.operations
6569
_ = s + []
70+
71+
slowServer.runOnMainThread { s in
72+
print(s)
73+
onlyOnMainActor() // okay because runOnMainThread has a @MainActor closure
74+
}
6675
}
6776

6877
func testSlowServerOldSchool(slowServer: SlowServer) {
@@ -73,6 +82,18 @@ func testSlowServerOldSchool(slowServer: SlowServer) {
7382
_ = slowServer.allOperations
7483
}
7584

85+
func testSendable(fn: () -> Void) { // expected-note{{parameter 'fn' is implicitly non-concurrent}}
86+
doSomethingConcurrently(fn)
87+
// expected-error@-1{{passing non-concurrent parameter 'fn' to function expecting a @Sendable closure}}
88+
89+
var x = 17
90+
doSomethingConcurrently {
91+
print(x) // expected-error{{reference to captured var 'x' in concurrently-executing code}}
92+
x = x + 1 // expected-error{{mutation of captured var 'x' in concurrently-executing code}}
93+
// expected-error@-1{{reference to captured var 'x' in concurrently-executing code}}
94+
}
95+
}
96+
7697
// Check import of attributes
7798
func globalAsync() async { }
7899

test/IDE/print_clang_objc_async.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import _Concurrency
2626
// CHECK-DAG: func doSomethingFun(_ operation: String) async
2727
// CHECK-DAG: func dance(_ step: String) async -> String
2828
// CHECK-DAG: func __leap(_ height: Int) async -> String
29+
// CHECK-DAG: func runOnMainThread(completionHandler completion: (@MainActor (String) -> Void)? = nil)
30+
// CHECK-DAG: func runOnMainThread() async -> String
2931
// CHECK: {{^[}]$}}
3032

3133
// CHECK-LABEL: protocol RefrigeratorDelegate
@@ -45,3 +47,5 @@ import _Concurrency
4547
// CHECK-NEXT: {{^[}]$}}
4648

4749
// CHECK: {{^}}var MAGIC_NUMBER: Int32 { get }
50+
51+
// CHECK: func doSomethingConcurrently(_ block: @Sendable () -> Void)

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
#pragma clang assume_nonnull begin
55

6+
#define MAIN_ACTOR __attribute__((__swift_attr__("@MainActor")))
7+
68
@protocol ServiceProvider
79
@property(readonly) NSArray<NSString *> *allOperations;
810
-(void)allOperationsWithCompletionHandler:(void (^)(NSArray<NSString *> *))completion;
@@ -66,6 +68,9 @@ typedef void (^CompletionHandler)(NSString * _Nullable, NSString * _Nullable_res
6668
-(void)doSomethingFlaggyWithCompletionHandler:(void (^)(BOOL, NSString *_Nullable, NSError *_Nullable))completionHandler __attribute__((swift_async_error(nonzero_argument, 1)));
6769
-(void)doSomethingZeroFlaggyWithCompletionHandler:(void (^)(NSString *_Nullable, BOOL, NSError *_Nullable))completionHandler __attribute__((swift_async_error(zero_argument, 2)));
6870
-(void)doSomethingMultiResultFlaggyWithCompletionHandler:(void (^)(BOOL, NSString *_Nullable, NSError *_Nullable, NSString *_Nullable))completionHandler __attribute__((swift_async_error(zero_argument, 1)));
71+
72+
73+
-(void)runOnMainThreadWithCompletionHandler:(MAIN_ACTOR void (^ _Nullable)(NSString *))completion;
6974
@end
7075

7176
@protocol RefrigeratorDelegate<NSObject>
@@ -139,4 +144,7 @@ __attribute__((__swift_attr__("@MainActor(unsafe)")))
139144
-(void)onButtonPress;
140145
@end
141146

147+
// Do something concurrently, but without escaping.
148+
void doSomethingConcurrently(__attribute__((noescape)) __attribute__((swift_attr("@Sendable"))) void (^block)(void));
149+
142150
#pragma clang assume_nonnull end

0 commit comments

Comments
 (0)