Skip to content

Commit 7e473dc

Browse files
authored
Merge pull request #41737 from beccadax/anything-can-be-sendable
[ClangImporter] Allow @sendable on more params
2 parents 5331e27 + 51e5408 commit 7e473dc

File tree

5 files changed

+309
-3
lines changed

5 files changed

+309
-3
lines changed

include/swift/AST/DiagnosticsClangImporter.def

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,14 @@ WARNING(clang_error_code_must_be_sendable,none,
8989
"cannot make error code type '%0' non-sendable because Swift errors "
9090
"are always sendable", (StringRef))
9191

92+
WARNING(clang_param_ignored_sendable_attr,none,
93+
"cannot make parameter '%0' sendable type %1 cannot be made sendable "
94+
"by adding '@Sendable' or '& Sendable'",
95+
(StringRef, Type))
96+
NOTE(clang_param_should_be_implicitly_sendable,none,
97+
"parameter should be implicitly 'Sendable' because it is a completion "
98+
"handler", ())
99+
92100
WARNING(implicit_bridging_header_imported_from_module,none,
93101
"implicit import of bridging header '%0' via module %1 "
94102
"is deprecated and will be removed in a later version of Swift",

lib/ClangImporter/ImportType.cpp

Lines changed: 209 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "swift/AST/ParameterList.h"
3131
#include "swift/AST/Type.h"
3232
#include "swift/AST/Types.h"
33+
#include "swift/AST/TypeVisitor.h"
3334
#include "swift/ClangImporter/ClangModule.h"
3435
#include "swift/Parse/Token.h"
3536
#include "swift/Strings.h"
@@ -1733,6 +1734,197 @@ ImportedType ClangImporter::Implementation::importPropertyType(
17331734
Bridgeability::Full, optionality);
17341735
}
17351736

1737+
namespace {
1738+
1739+
class GetSendableType :
1740+
private TypeVisitor<GetSendableType, std::pair<Type, bool>> {
1741+
ASTContext &ctx;
1742+
1743+
public:
1744+
GetSendableType(ASTContext &ctx) : ctx(ctx) {}
1745+
1746+
/// The result of a conversion. Contains the converted type and a \c bool that
1747+
/// is \c true if the operation found something to change, or \c false
1748+
/// otherwise.
1749+
using Result = std::pair<Type, /*found=*/bool>;
1750+
1751+
/// Returns a modified version of \p type that has been made explicitly
1752+
/// \c Sendable by adding an \c \@Sendable attribute to a function type
1753+
/// or forming a protocol composition with \c & \c Sendable.
1754+
Result convert(Type type) { return visit(type); }
1755+
1756+
private:
1757+
/// Decide how to represent the given type in a protocol composition. This
1758+
/// is specialized for \c ProtocolCompositionType to avoid nesting
1759+
/// compositions.
1760+
///
1761+
/// \param members The types to include in the composition.
1762+
/// \return \c true if the composition should include \c AnyObject, \c false
1763+
/// otherwise.
1764+
bool getAsComposition(ProtocolCompositionType *ty,
1765+
SmallVectorImpl<Type> &members) {
1766+
llvm::append_range(members, ty->getMembers());
1767+
return ty->hasExplicitAnyObject();
1768+
}
1769+
1770+
/// Decide how to represent the given type in a protocol composition. This
1771+
/// is specialized for \c ProtocolCompositionType to avoid nesting
1772+
/// compositions.
1773+
///
1774+
/// \param members The types to include in the composition.
1775+
/// \return \c true if the composition should include \c AnyObject, \c false
1776+
/// otherwise.
1777+
bool getAsComposition(TypeBase *ty, SmallVectorImpl<Type> &members) {
1778+
members.push_back(ty);
1779+
return false;
1780+
}
1781+
1782+
// MARK: Visitor Actions
1783+
1784+
/// Visitor action: Replace this type with a protocol composition that
1785+
/// includes \c Sendable.
1786+
template <typename Ty> Result compose(Ty *ty) {
1787+
SmallVector<Type, 8> members;
1788+
bool explicitAnyObject = getAsComposition(ty, members);
1789+
1790+
auto proto = ctx.getProtocol(KnownProtocolKind::Sendable);
1791+
members.push_back(proto->getDeclaredInterfaceType());
1792+
1793+
return {
1794+
ProtocolCompositionType::get(ctx, members, explicitAnyObject), true };
1795+
}
1796+
1797+
/// Visitor action: Recurse into the children of this type and try to add
1798+
/// \c Sendable to them.
1799+
Result recurse(Type ty) {
1800+
bool anyFound = false;
1801+
1802+
Type newTy = ty.transformRec([&](TypeBase *childTy) -> Optional<Type> {
1803+
// We want to visit the first level of children.
1804+
if (childTy == ty.getPointer())
1805+
return None;
1806+
1807+
auto result = this->visit(childTy);
1808+
anyFound |= result.second;
1809+
return result.first;
1810+
});
1811+
1812+
return { newTy, anyFound };
1813+
}
1814+
1815+
/// Visitor action: Ignore this type; do not modify it and do not recurse into
1816+
/// it to find other types to modify.
1817+
Result pass(Type ty, bool found = false) {
1818+
return { ty, found };
1819+
}
1820+
1821+
// Macros to define visitors based on these actions.
1822+
#define VISIT(CLASS, ACT) Result visit##CLASS(CLASS *ty) { return ACT(ty); }
1823+
#define NEVER_VISIT(CLASS) Result visit##CLASS(CLASS *ty) { \
1824+
llvm_unreachable("can't have " #CLASS " in imported clang type"); \
1825+
return pass(ty); \
1826+
}
1827+
1828+
// MARK: Visitors
1829+
1830+
friend TypeVisitor<GetSendableType, Result>;
1831+
1832+
Result visitErrorType(ErrorType *ty) {
1833+
// Pass, but suppress diagnostic about not finding anything `Sendable`.
1834+
return pass(ty, /*found=*/true);
1835+
}
1836+
1837+
NEVER_VISIT(UnresolvedType)
1838+
NEVER_VISIT(PlaceholderType)
1839+
NEVER_VISIT(BuiltinType)
1840+
1841+
VISIT(TupleType, recurse)
1842+
1843+
NEVER_VISIT(ReferenceStorageType)
1844+
1845+
VISIT(EnumType, pass)
1846+
VISIT(StructType, pass)
1847+
VISIT(ClassType, compose)
1848+
VISIT(ProtocolType, compose)
1849+
1850+
Result visitBoundGenericType(BoundGenericType *ty) {
1851+
assert(!isa<BoundGenericClassType>(ty) && "classes handled elsewhere");
1852+
1853+
// These types are produced during bridging and have conditional
1854+
// conformances to Sendable depending on their generic parameters, so we
1855+
// want to make their generic parameters `Sendable`.
1856+
if (ty->isOptional() || ty->isArray() || ty->isSet() ||
1857+
ty->isDictionary())
1858+
return recurse(ty);
1859+
1860+
// Other non-class generic types (e.g. pointers) cannot be made Sendable.
1861+
return pass(ty);
1862+
}
1863+
1864+
VISIT(BoundGenericClassType, compose)
1865+
NEVER_VISIT(UnboundGenericType)
1866+
1867+
VISIT(AnyMetatypeType, recurse)
1868+
1869+
VISIT(ModuleType, pass)
1870+
VISIT(DynamicSelfType, pass)
1871+
1872+
NEVER_VISIT(SubstitutableType)
1873+
NEVER_VISIT(DependentMemberType)
1874+
1875+
Result visitAnyFunctionType(AnyFunctionType *ty) {
1876+
auto newFn = applyToFunctionType(ty, [](ASTExtInfo extInfo) {
1877+
return extInfo.withConcurrent();
1878+
});
1879+
return { newFn, true };
1880+
}
1881+
1882+
NEVER_VISIT(SILFunctionType)
1883+
NEVER_VISIT(SILBlockStorageType)
1884+
NEVER_VISIT(SILBoxType)
1885+
NEVER_VISIT(SILTokenType)
1886+
1887+
VISIT(ProtocolCompositionType, compose)
1888+
1889+
// ProtocolCompositionType doesn't handle ParameterizedProtocolType
1890+
// correctly, but we currently never import anything with it, so forbid it
1891+
// until we find we need it.
1892+
NEVER_VISIT(ParameterizedProtocolType)
1893+
1894+
VISIT(ExistentialType, recurse)
1895+
NEVER_VISIT(LValueType)
1896+
VISIT(InOutType, recurse)
1897+
1898+
NEVER_VISIT(PackType)
1899+
NEVER_VISIT(PackExpansionType)
1900+
NEVER_VISIT(TypeVariableType)
1901+
1902+
VISIT(SugarType, recurse)
1903+
1904+
Result visitTypeAliasType(TypeAliasType *ty) {
1905+
// Try converting the underlying type.
1906+
Type underlying = ty->getSinglyDesugaredType();
1907+
auto result = visit(underlying);
1908+
1909+
// If nothing that could be made Sendable was found in the underlying type,
1910+
// keep the sugar.
1911+
if (!result.second)
1912+
return pass(ty);
1913+
1914+
// If something Sendable-capable *was* found but the operation was a no-op,
1915+
// keep the sugar but indicate that we did find something to avoid a
1916+
// diagnostic.
1917+
if (result.first->getCanonicalType() == underlying->getCanonicalType())
1918+
return pass(ty, /*found=*/true);
1919+
1920+
// We found something and it did change the type. Desugar to the converted
1921+
// underlying type.
1922+
return result;
1923+
}
1924+
};
1925+
1926+
} // anonymous namespace
1927+
17361928
Type ClangImporter::Implementation::applyParamAttributes(
17371929
const clang::ParmVarDecl *param, Type type, bool sendableByDefault) {
17381930
bool sendableRequested = sendableByDefault;
@@ -1780,9 +1972,23 @@ Type ClangImporter::Implementation::applyParamAttributes(
17801972
}
17811973

17821974
if (!sendableDisqualified && sendableRequested) {
1783-
type = applyToFunctionType(type, [](ASTExtInfo extInfo) {
1784-
return extInfo.withConcurrent();
1785-
});
1975+
bool changed;
1976+
std::tie(type, changed) = GetSendableType(SwiftContext).convert(type);
1977+
1978+
// Diagnose if we couldn't find a place to add `Sendable` to the type.
1979+
if (!changed) {
1980+
auto parentDecl = cast<clang::Decl>(param->getDeclContext());
1981+
1982+
addImportDiagnostic(parentDecl,
1983+
Diagnostic(diag::clang_param_ignored_sendable_attr,
1984+
param->getName(), type),
1985+
param->getLocation());
1986+
1987+
if (sendableByDefault)
1988+
addImportDiagnostic(parentDecl,
1989+
Diagnostic(diag::clang_param_should_be_implicitly_sendable),
1990+
param->getLocation());
1991+
}
17861992
}
17871993

17881994
return type;

test/ClangImporter/objc_async.swift

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,4 +295,57 @@ func check() async {
295295
_ = await BazFrame(size: 0)
296296
}
297297

298+
func testSender(
299+
sender: NXSender,
300+
sendableObject: SendableClass,
301+
nonSendableObject: NonSendableClass,
302+
sendableSubclassOfNonSendableObject: NonSendableClass & Sendable,
303+
sendableProtos: LabellyProtocol & ObjCClub & Sendable,
304+
nonSendableProtos: LabellyProtocol & ObjCClub,
305+
sendableGeneric: GenericObject<SendableClass> & Sendable,
306+
nonSendableGeneric: GenericObject<SendableClass>,
307+
ptr: UnsafeMutableRawPointer,
308+
stringArray: [String]
309+
) {
310+
sender.sendAny(sendableObject)
311+
sender.sendAny(nonSendableObject)
312+
// expected-warning@-1 {{conformance of 'NonSendableClass' to 'Sendable' is unavailable}}
313+
314+
sender.sendOptionalAny(sendableObject)
315+
sender.sendOptionalAny(nonSendableObject)
316+
// expected-warning@-1 {{conformance of 'NonSendableClass' to 'Sendable' is unavailable}}
317+
318+
sender.sendSendable(sendableObject)
319+
320+
sender.sendSendableSubclasses(nonSendableObject)
321+
// expected-warning@-1 {{conformance of 'NonSendableClass' to 'Sendable' is unavailable}}
322+
sender.sendSendableSubclasses(sendableSubclassOfNonSendableObject)
323+
// expected-warning@-1 {{conformance of 'NonSendableClass' to 'Sendable' is unavailable}}
324+
// FIXME(rdar://89992569): Should allow for the possibility that NonSendableClass will have a Sendable subclass
325+
326+
sender.sendProto(sendableProtos)
327+
sender.sendProto(nonSendableProtos)
328+
// expected-error@-1 {{argument type 'any LabellyProtocol & ObjCClub' does not conform to expected type 'Sendable'}}
329+
// FIXME(rdar://89992095): Should be a warning because we're in -warn-concurrency
330+
331+
sender.sendProtos(sendableProtos)
332+
sender.sendProtos(nonSendableProtos)
333+
// expected-error@-1 {{argument type 'any LabellyProtocol & ObjCClub' does not conform to expected type 'Sendable'}}
334+
// FIXME(rdar://89992095): Should be a warning because we're in -warn-concurrency
335+
336+
sender.sendAnyArray([sendableObject])
337+
sender.sendAnyArray([nonSendableObject])
338+
// expected-warning@-1 {{conformance of 'NonSendableClass' to 'Sendable' is unavailable}}
339+
340+
sender.sendGeneric(sendableGeneric)
341+
// expected-warning@-1 {{type 'GenericObject<SendableClass>' does not conform to the 'Sendable' protocol}}
342+
// FIXME(rdar://89992569): Should allow for the possibility that GenericObject will have a Sendable subclass
343+
sender.sendGeneric(nonSendableGeneric)
344+
// expected-error@-1 {{argument type 'GenericObject<SendableClass>' does not conform to expected type 'Sendable'}}
345+
// FIXME(rdar://89992095): Should be a warning because we're in -warn-concurrency
346+
347+
sender.sendPtr(ptr)
348+
sender.sendStringArray(stringArray)
349+
}
350+
298351
} // SwiftStdlib 5.5

test/IDE/print_clang_objc_async.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,20 @@ import _Concurrency
124124
// CHECK: func doSomethingConcurrently(_ block: @Sendable () -> Void)
125125

126126
// CHECK: @MainActor @objc protocol TripleMainActor {
127+
128+
// CHECK-LABEL: class NXSender :
129+
// CHECK-NEXT: func sendAny(_ obj: Sendable)
130+
// CHECK-NEXT: func sendOptionalAny(_ obj: Sendable?)
131+
// CHECK-NEXT: func sendSendable(_ sendable: SendableClass & Sendable)
132+
// CHECK-NEXT: func sendSendableSubclasses(_ sendableSubclass: NonSendableClass & Sendable)
133+
// CHECK-NEXT: func sendProto(_ obj: LabellyProtocol & Sendable)
134+
// CHECK-NEXT: func sendProtos(_ obj: LabellyProtocol & ObjCClub & Sendable)
135+
// CHECK-NEXT: func sendAnyArray(_ array: [Sendable])
136+
// CHECK-NEXT: func sendGeneric(_ generic: GenericObject<SendableClass> & Sendable)
137+
// CHECK-NEXT: func sendPtr(_ val: UnsafeMutableRawPointer)
138+
// CHECK-NEXT: func sendStringArray(_ obj: [String])
139+
// CHECK-NEXT: func sendAnyTypedef(_ obj: Sendable)
140+
// CHECK-NEXT: func sendAnyTypedefs(_ objs: [Sendable])
141+
// CHECK-NEXT: func sendBlockTypedef(_ block: @escaping @Sendable (Any) -> Void)
142+
// CHECK-NEXT: func sendBlockTypedefs(_ blocks: [@Sendable @convention(block) (Any) -> Void])
143+
// CHECK-NEXT: func sendUnbound(_ array: [Sendable])

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,4 +267,26 @@ typedef NSString *NonSendableStringStruct NS_EXTENSIBLE_STRING_ENUM NONSENDABLE;
267267

268268
ASSUME_NONSENDABLE_END
269269

270+
typedef id ObjectTypedef;
271+
typedef void(^BlockTypedef)(id);
272+
273+
@interface NXSender : NSObject
274+
275+
- (void)sendAny:(SENDABLE id)obj;
276+
- (void)sendOptionalAny:(nullable SENDABLE id)obj;
277+
- (void)sendSendable:(SENDABLE SendableClass *)sendable;
278+
- (void)sendSendableSubclasses:(SENDABLE NonSendableClass *)sendableSubclass;
279+
- (void)sendProto:(SENDABLE id <LabellyProtocol>)obj;
280+
- (void)sendProtos:(SENDABLE id <LabellyProtocol, ObjCClub>)obj;
281+
- (void)sendAnyArray:(SENDABLE NSArray<id> *)array;
282+
- (void)sendGeneric:(SENDABLE GenericObject<SendableClass *> *)generic;
283+
- (void)sendPtr:(SENDABLE void *)val; // bad
284+
- (void)sendStringArray:(SENDABLE NSArray<NSString *> *)obj; // bad
285+
- (void)sendAnyTypedef:(SENDABLE ObjectTypedef)obj;
286+
- (void)sendAnyTypedefs:(SENDABLE NSArray<ObjectTypedef> *)objs;
287+
- (void)sendBlockTypedef:(SENDABLE BlockTypedef)block;
288+
- (void)sendBlockTypedefs:(SENDABLE NSArray<BlockTypedef> *)blocks;
289+
- (void)sendUnbound:(SENDABLE NSArray *)array;
290+
@end
291+
270292
#pragma clang assume_nonnull end

0 commit comments

Comments
 (0)