[ClangImporter] Import 'swift_attr("sending")' As a Type Attribute

Previously, `__attribute__((swift_attr("sending")))` would only be
resolved when attached to declarations. This patch expands it to be
a type attribute as well, which enables it to occur in the result and
parameter positions for blocks, function pointers, and lambdas.

Resolves rdar://148435359
This commit is contained in:
Robert Widmann
2025-05-07 17:59:51 +01:00
parent 62b7a6f380
commit edcde7c55c
4 changed files with 58 additions and 5 deletions

View File

@@ -726,20 +726,45 @@ namespace {
paramQualType->getPointeeType().isConstQualified())
paramQualType = paramQualType->getPointeeType();
// Mark any `sending` parameters if need be.
ImportTypeAttrs paramAttributes;
if (Impl.SwiftContext.LangOpts.hasFeature(Feature::SendingArgsAndResults)) {
getConcurrencyAttrs(Impl.SwiftContext, ImportTypeKind::Parameter,
paramAttributes, paramQualType);
}
auto swiftParamTy = Impl.importTypeIgnoreIUO(
paramQualType, paramImportKind, addImportDiagnostic,
AllowNSUIntegerAsInt, Bridging, ImportTypeAttrs(), OTK_Optional);
if (!swiftParamTy)
return Type();
ParameterTypeFlags flags;
flags = flags.withSending(
paramAttributes.contains(ImportTypeAttr::Sending));
// FIXME(https://github.com/apple/swift/issues/45134): If we were walking TypeLocs, we could actually get parameter names.
// The probably doesn't matter outside of a FuncDecl, which we'll have
// to special-case, but it's an interesting bit of data loss.
params.push_back(FunctionType::Param(swiftParamTy));
params.emplace_back(swiftParamTy, Identifier(), flags);
}
// Mark any `sending` result types if need be.
auto extInfo = FunctionType::ExtInfo();
ImportTypeAttrs resultAttributes;
if (Impl.SwiftContext.LangOpts.hasFeature(Feature::SendingArgsAndResults)) {
getConcurrencyAttrs(Impl.SwiftContext, ImportTypeKind::Result,
resultAttributes, type->getReturnType());
const bool sending = resultAttributes.contains(ImportTypeAttr::Sending);
extInfo = FunctionType::ExtInfo()
.intoBuilder()
.withSendingResult(sending)
.build();
}
// Form the function type.
return FunctionType::get(params, resultTy, FunctionType::ExtInfo());
return FunctionType::get(params, resultTy, extInfo);
}
ImportResult
@@ -1714,6 +1739,7 @@ void swift::getConcurrencyAttrs(ASTContext &SwiftContext,
SwiftContext.LangOpts.hasFeature(Feature::SendableCompletionHandlers) &&
importKind == ImportTypeKind::CompletionHandlerParameter;
bool isNonSendable = false;
bool isSending = false;
// Consider only immediate attributes, don't look through the typerefs
// because they are imported separately.
@@ -1721,10 +1747,13 @@ void swift::getConcurrencyAttrs(ASTContext &SwiftContext,
if (isMainActorAttr(attr)) {
isMainActor = true;
isSendable = true; // MainActor implies Sendable
} else if (attr->getAttribute() == "@Sendable")
} else if (attr->getAttribute() == "@Sendable") {
isSendable = true;
else if (attr->getAttribute() == "@_nonSendable")
} else if (attr->getAttribute() == "@_nonSendable") {
isNonSendable = true;
} else if (attr->getAttribute() == "sending") {
isSending = true;
}
});
if (isMainActor)
@@ -1733,6 +1762,8 @@ void swift::getConcurrencyAttrs(ASTContext &SwiftContext,
attrs |= ImportTypeAttr::Sendable;
if (isNonSendable)
attrs -= ImportTypeAttr::Sendable;
if (isSending)
attrs |= ImportTypeAttr::Sending;
}
ImportedType ClangImporter::Implementation::importType(

View File

@@ -217,6 +217,10 @@ enum class ImportTypeAttr : uint8_t {
///
/// This ensures that the parameter is not marked as Unmanaged.
CFUnretainedOutParameter = 1 << 5,
/// Type should be imported as though declaration was marked with
/// \c __attribute__((swift_attr("sending"))) .
Sending = 1 << 6,
};
/// Find and iterate over swift attributes embedded in the type

View File

@@ -43,4 +43,7 @@ sendUserDefinedFromGlobalFunction(NonSendableCStruct other) SWIFT_SENDING;
void sendUserDefinedIntoGlobalFunction(
NonSendableCStruct arg SWIFT_SENDING);
void sendingWithCompletionHandler(void (^completion)(SWIFT_SENDING NonSendableCStruct arg));
SWIFT_SENDING NonSendableCStruct sendingWithLazyReturn(SWIFT_SENDING NonSendableCStruct (^makeLazily)(void));
#pragma clang assume_nonnull end

View File

@@ -1,4 +1,4 @@
// 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
// 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
// REQUIRES: concurrency
// REQUIRES: swift_feature_SendingArgsAndResults
@@ -38,3 +38,18 @@ func funcTestSendingArg() async {
// expected-note @-1 {{'x' used after being passed as a 'sending' parameter}}
useValue(x) // expected-note {{access can happen concurrently}}
}
func funcTestSendingClosureArg() async {
sendingWithCompletionHandler { (x: sending NonSendableCStruct) in
sendUserDefinedIntoGlobalFunction(x) // expected-error {{sending 'x' risks causing data races}}
// expected-note @-1 {{'x' used after being passed as a 'sending' parameter}}
useValue(x) // expected-note {{access can happen concurrently}}
}
let x = sendingWithLazyReturn { () -> sending NonSendableCStruct in
NonSendableCStruct()
}
sendUserDefinedIntoGlobalFunction(x) // expected-error {{sending 'x' risks causing data races}}
// expected-note @-1 {{'x' used after being passed as a 'sending' parameter}}
useValue(x) // expected-note {{access can happen concurrently}}
}