Skip to content

Commit 7c5a59f

Browse files
authored
Merge pull request swiftlang#81359 from CodaFi/sending-type-beat
[ClangImporter] Import 'swift_attr("sending")' As a Type Attribute
2 parents f861cfc + edcde7c commit 7c5a59f

File tree

4 files changed

+58
-5
lines changed

4 files changed

+58
-5
lines changed

lib/ClangImporter/ImportType.cpp

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -726,20 +726,45 @@ namespace {
726726
paramQualType->getPointeeType().isConstQualified())
727727
paramQualType = paramQualType->getPointeeType();
728728

729+
// Mark any `sending` parameters if need be.
730+
ImportTypeAttrs paramAttributes;
731+
if (Impl.SwiftContext.LangOpts.hasFeature(Feature::SendingArgsAndResults)) {
732+
getConcurrencyAttrs(Impl.SwiftContext, ImportTypeKind::Parameter,
733+
paramAttributes, paramQualType);
734+
}
735+
729736
auto swiftParamTy = Impl.importTypeIgnoreIUO(
730737
paramQualType, paramImportKind, addImportDiagnostic,
731738
AllowNSUIntegerAsInt, Bridging, ImportTypeAttrs(), OTK_Optional);
732739
if (!swiftParamTy)
733740
return Type();
734741

742+
ParameterTypeFlags flags;
743+
flags = flags.withSending(
744+
paramAttributes.contains(ImportTypeAttr::Sending));
745+
735746
// FIXME(https://github.com/apple/swift/issues/45134): If we were walking TypeLocs, we could actually get parameter names.
736747
// The probably doesn't matter outside of a FuncDecl, which we'll have
737748
// to special-case, but it's an interesting bit of data loss.
738-
params.push_back(FunctionType::Param(swiftParamTy));
749+
params.emplace_back(swiftParamTy, Identifier(), flags);
750+
}
751+
752+
// Mark any `sending` result types if need be.
753+
auto extInfo = FunctionType::ExtInfo();
754+
ImportTypeAttrs resultAttributes;
755+
if (Impl.SwiftContext.LangOpts.hasFeature(Feature::SendingArgsAndResults)) {
756+
getConcurrencyAttrs(Impl.SwiftContext, ImportTypeKind::Result,
757+
resultAttributes, type->getReturnType());
758+
759+
const bool sending = resultAttributes.contains(ImportTypeAttr::Sending);
760+
extInfo = FunctionType::ExtInfo()
761+
.intoBuilder()
762+
.withSendingResult(sending)
763+
.build();
739764
}
740765

741766
// Form the function type.
742-
return FunctionType::get(params, resultTy, FunctionType::ExtInfo());
767+
return FunctionType::get(params, resultTy, extInfo);
743768
}
744769

745770
ImportResult
@@ -1714,17 +1739,21 @@ void swift::getConcurrencyAttrs(ASTContext &SwiftContext,
17141739
SwiftContext.LangOpts.hasFeature(Feature::SendableCompletionHandlers) &&
17151740
importKind == ImportTypeKind::CompletionHandlerParameter;
17161741
bool isNonSendable = false;
1742+
bool isSending = false;
17171743

17181744
// Consider only immediate attributes, don't look through the typerefs
17191745
// because they are imported separately.
17201746
findSwiftAttributes(type, [&](const clang::SwiftAttrAttr *attr) {
17211747
if (isMainActorAttr(attr)) {
17221748
isMainActor = true;
17231749
isSendable = true; // MainActor implies Sendable
1724-
} else if (attr->getAttribute() == "@Sendable")
1750+
} else if (attr->getAttribute() == "@Sendable") {
17251751
isSendable = true;
1726-
else if (attr->getAttribute() == "@_nonSendable")
1752+
} else if (attr->getAttribute() == "@_nonSendable") {
17271753
isNonSendable = true;
1754+
} else if (attr->getAttribute() == "sending") {
1755+
isSending = true;
1756+
}
17281757
});
17291758

17301759
if (isMainActor)
@@ -1733,6 +1762,8 @@ void swift::getConcurrencyAttrs(ASTContext &SwiftContext,
17331762
attrs |= ImportTypeAttr::Sendable;
17341763
if (isNonSendable)
17351764
attrs -= ImportTypeAttr::Sendable;
1765+
if (isSending)
1766+
attrs |= ImportTypeAttr::Sending;
17361767
}
17371768

17381769
ImportedType ClangImporter::Implementation::importType(

lib/ClangImporter/ImporterImpl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,10 @@ enum class ImportTypeAttr : uint8_t {
217217
///
218218
/// This ensures that the parameter is not marked as Unmanaged.
219219
CFUnretainedOutParameter = 1 << 5,
220+
221+
/// Type should be imported as though declaration was marked with
222+
/// \c __attribute__((swift_attr("sending"))) .
223+
Sending = 1 << 6,
220224
};
221225

222226
/// Find and iterate over swift attributes embedded in the type

test/ClangImporter/Inputs/sending.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,7 @@ sendUserDefinedFromGlobalFunction(NonSendableCStruct other) SWIFT_SENDING;
4343
void sendUserDefinedIntoGlobalFunction(
4444
NonSendableCStruct arg SWIFT_SENDING);
4545

46+
void sendingWithCompletionHandler(void (^completion)(SWIFT_SENDING NonSendableCStruct arg));
47+
SWIFT_SENDING NonSendableCStruct sendingWithLazyReturn(SWIFT_SENDING NonSendableCStruct (^makeLazily)(void));
48+
4649
#pragma clang assume_nonnull end

test/ClangImporter/sending.swift

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-frontend -swift-version 6 -disable-availability-checking -emit-sil -o /dev/null %s -parse-as-library -enable-experimental-feature SendingArgsAndResults -verify -import-objc-header %S/Inputs/sending.h
1+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource -I %t) -swift-version 6 -disable-availability-checking -emit-sil -o /dev/null %s -parse-as-library -enable-experimental-feature SendingArgsAndResults -verify -import-objc-header %S/Inputs/sending.h
22

33
// REQUIRES: concurrency
44
// REQUIRES: swift_feature_SendingArgsAndResults
@@ -38,3 +38,18 @@ func funcTestSendingArg() async {
3838
// expected-note @-1 {{'x' used after being passed as a 'sending' parameter}}
3939
useValue(x) // expected-note {{access can happen concurrently}}
4040
}
41+
42+
func funcTestSendingClosureArg() async {
43+
sendingWithCompletionHandler { (x: sending NonSendableCStruct) in
44+
sendUserDefinedIntoGlobalFunction(x) // expected-error {{sending 'x' risks causing data races}}
45+
// expected-note @-1 {{'x' used after being passed as a 'sending' parameter}}
46+
useValue(x) // expected-note {{access can happen concurrently}}
47+
}
48+
49+
let x = sendingWithLazyReturn { () -> sending NonSendableCStruct in
50+
NonSendableCStruct()
51+
}
52+
sendUserDefinedIntoGlobalFunction(x) // expected-error {{sending 'x' risks causing data races}}
53+
// expected-note @-1 {{'x' used after being passed as a 'sending' parameter}}
54+
useValue(x) // expected-note {{access can happen concurrently}}
55+
}

0 commit comments

Comments
 (0)