Skip to content

Commit 1c813c6

Browse files
committed
[ClangImporter] Augment importType to infer concurrency attributes from types
Support for `swift_attr` has been extended to type contexts. Such attributes form `clang::AtributedType` that carries an optional `clang::Attr *`. Teach `importType` has to examine immediate sugar to determine whether it carries any concurrency information and, if so, add it to `ImportTypeAttrs` to be applied after import.
1 parent 578f82f commit 1c813c6

File tree

2 files changed

+123
-0
lines changed

2 files changed

+123
-0
lines changed

lib/ClangImporter/ImportType.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1633,6 +1633,52 @@ static ImportedType adjustTypeForConcreteImport(
16331633
return {importedType, isIUO};
16341634
}
16351635

1636+
static void applyTypeAttributes(ImportTypeKind importKind,
1637+
ImportTypeAttrs &attrs, clang::QualType type) {
1638+
bool isMainActor = false;
1639+
bool isSendable = false;
1640+
bool isNonSendable = false;
1641+
1642+
std::function<clang::QualType(clang::QualType)> skipUnrelatedSugar =
1643+
[&](clang::QualType type) -> clang::QualType {
1644+
if (auto *MQT = dyn_cast<clang::MacroQualifiedType>(type))
1645+
return MQT->isSugared() ? skipUnrelatedSugar(MQT->desugar()) : type;
1646+
1647+
if (auto *ET = dyn_cast<clang::ElaboratedType>(type))
1648+
return ET->isSugared() ? skipUnrelatedSugar(ET->desugar()) : type;
1649+
1650+
return type;
1651+
};
1652+
1653+
type = skipUnrelatedSugar(type);
1654+
1655+
// Consider only immediate attributes, don't look through the typerefs
1656+
// because they are imported separately.
1657+
while (const auto *AT = dyn_cast<clang::AttributedType>(type)) {
1658+
if (auto swiftAttr =
1659+
dyn_cast_or_null<clang::SwiftAttrAttr>(AT->getAttr())) {
1660+
if (isMainActorAttr(swiftAttr)) {
1661+
isMainActor = true;
1662+
isNonSendable =
1663+
importKind == ImportTypeKind::Parameter ||
1664+
importKind == ImportTypeKind::CompletionHandlerParameter;
1665+
} else if (swiftAttr->getAttribute() == "@Sendable")
1666+
isSendable = true;
1667+
else if (swiftAttr->getAttribute() == "@_nonSendable")
1668+
isNonSendable = true;
1669+
}
1670+
1671+
type = skipUnrelatedSugar(AT->getEquivalentType());
1672+
}
1673+
1674+
if (isMainActor)
1675+
attrs |= ImportTypeAttr::MainActor;
1676+
if (isSendable)
1677+
attrs |= ImportTypeAttr::Sendable;
1678+
if (isNonSendable)
1679+
attrs -= ImportTypeAttr::Sendable;
1680+
}
1681+
16361682
ImportedType ClangImporter::Implementation::importType(
16371683
clang::QualType type, ImportTypeKind importKind,
16381684
llvm::function_ref<void(Diagnostic &&)> addImportDiagnosticFn,
@@ -1679,6 +1725,8 @@ ImportedType ClangImporter::Implementation::importType(
16791725
optionality = translateNullability(*nullability, stripNonResultOptionality);
16801726
}
16811727

1728+
applyTypeAttributes(importKind, attrs, type);
1729+
16821730
// If this is a completion handler parameter, record the function type whose
16831731
// parameters will act as the results of the completion handler.
16841732
const clang::FunctionType *completionHandlerType = nullptr;
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// RUN: %empty-directory(%t/src)
2+
// RUN: %empty-directory(%t/sdk)
3+
// RUN: split-file %s %t/src
4+
5+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck %t/src/main.swift \
6+
// RUN: -import-objc-header %t/src/Test.h \
7+
// RUN: -strict-concurrency=complete \
8+
// RUN: -enable-experimental-feature SendableCompletionHandlers \
9+
// RUN: -module-name main -I %t -verify
10+
11+
// REQUIRES: objc_interop
12+
13+
//--- Test.h
14+
#define SWIFT_SENDABLE __attribute__((__swift_attr__("@Sendable")))
15+
#define NONSENDABLE __attribute__((__swift_attr__("@_nonSendable")))
16+
17+
#pragma clang assume_nonnull begin
18+
19+
@import Foundation;
20+
21+
@interface MyValue : NSObject
22+
@end
23+
24+
typedef void (^CompletionHandler)(void (^ SWIFT_SENDABLE)(void)) SWIFT_SENDABLE;
25+
26+
@interface Test : NSObject
27+
-(void) makeRequest:
28+
(NSString *)typeIdentifier
29+
loadHandler:(void (^SWIFT_SENDABLE )(void (SWIFT_SENDABLE ^)(void)))loadHandler;
30+
-(void) withSendableId: (void (^)(SWIFT_SENDABLE id)) handler;
31+
-(void) withSendableCustom: (void (^)(MyValue *_Nullable SWIFT_SENDABLE)) handler;
32+
-(void) withNonSendable:(NSString *)operation completionHandler:(void (^ _Nullable NONSENDABLE)(NSString *_Nullable, NSError * _Nullable)) handler;
33+
-(void) withAliasCompletionHandler:(CompletionHandler)handler;
34+
@end
35+
36+
// Placement of SWIFT_SENDABLE matters here
37+
void doSomethingConcurrently(__attribute__((noescape)) void SWIFT_SENDABLE (^block)(void));
38+
39+
#pragma clang assume_nonnull end
40+
41+
//--- main.swift
42+
43+
func test_sendable_attr_in_type_context(test: Test) {
44+
let fn: (String?, (any Error)?) -> Void = { _,_ in }
45+
46+
test.withNonSendable("", completionHandler: fn) // Ok
47+
48+
test.makeRequest("id") {
49+
doSomethingConcurrently($0) // Ok
50+
}
51+
52+
test.makeRequest("id") { callback in
53+
_ = { @Sendable in
54+
callback() // Ok
55+
}
56+
}
57+
58+
test.withSendableId { id in
59+
_ = { @Sendable in
60+
print(id) // Ok
61+
}
62+
}
63+
64+
test.withSendableCustom { val in
65+
_ = { @Sendable in
66+
print(val!)
67+
}
68+
}
69+
70+
let _: (@escaping @Sendable (@escaping @Sendable () -> Void) -> Void) -> Void = test.withAliasCompletionHandler
71+
72+
test.withAliasCompletionHandler { callback in
73+
doSomethingConcurrently(callback) // Ok
74+
}
75+
}

0 commit comments

Comments
 (0)