Skip to content

Commit 3253a4f

Browse files
committed
[SE-0302] Implement structural conformances to Sendable.
Add implement conformance of structural types to Sendable as appropriate: * A tuple type is Sendable when its element types are Sendable * A metatype type is Sendable * A function type is Sendable if it is @sendable, thin, or C * A builtin type is always Sendable Implements rdar://76836578.
1 parent ecd830e commit 3253a4f

File tree

3 files changed

+170
-3
lines changed

3 files changed

+170
-3
lines changed

lib/AST/Module.cpp

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,115 @@ ProtocolConformanceRef ModuleDecl::lookupConformance(Type type,
989989
getASTContext().evaluator, request, ProtocolConformanceRef::forInvalid());
990990
}
991991

992+
/// Synthesize a builtin tuple type conformance to the given protocol, if
993+
/// appropriate.
994+
static ProtocolConformanceRef getBuiltinTupleTypeConformance(
995+
Type type, const TupleType *tupleType, ProtocolDecl *protocol) {
996+
// Tuple type are Sendable when all of their element types are Sendable.
997+
if (protocol->isSpecificProtocol(KnownProtocolKind::Sendable)) {
998+
ASTContext &ctx = protocol->getASTContext();
999+
1000+
// Create the pieces for a generic tuple type (T1, T2, ... TN) and a
1001+
// generic signature <T1, T2, ..., TN>.
1002+
SmallVector<GenericTypeParamType *, 4> genericParams;
1003+
SmallVector<Type, 4> typeSubstitutions;
1004+
SmallVector<TupleTypeElt, 4> genericElements;
1005+
SmallVector<Requirement, 4> conditionalRequirements;
1006+
for (const auto &elt : tupleType->getElements()) {
1007+
auto genericParam = GenericTypeParamType::get(0, genericParams.size(), ctx);
1008+
genericParams.push_back(genericParam);
1009+
typeSubstitutions.push_back(elt.getRawType());
1010+
genericElements.push_back(elt.getWithType(genericParam));
1011+
conditionalRequirements.push_back(
1012+
Requirement(RequirementKind::Conformance, genericParam,
1013+
protocol->getDeclaredType()));
1014+
}
1015+
1016+
// If there were no generic parameters, just form the builtin conformance.
1017+
if (genericParams.empty()) {
1018+
return ProtocolConformanceRef(
1019+
ctx.getBuiltinConformance(type, protocol, GenericSignature(), { }));
1020+
}
1021+
1022+
// Form a generic conformance of (T1, T2, ..., TN): Sendable with signature
1023+
// <T1, T2, ..., TN> and conditional requirements T1: Sendable,
1024+
// T2: Sendable, ..., TN: Sendable.
1025+
auto genericTupleType = TupleType::get(genericElements, ctx);
1026+
auto genericSig = GenericSignature::get(genericParams, { });
1027+
auto genericConformance = ctx.getBuiltinConformance(
1028+
genericTupleType, protocol, genericSig, conditionalRequirements);
1029+
1030+
// Compute the substitution map from the generic parameters of the
1031+
// generic conformance to actual types that were in the tuple type.
1032+
// Form a specialized conformance from that.
1033+
auto subMap = SubstitutionMap::get(genericSig, typeSubstitutions, { });
1034+
return ProtocolConformanceRef(
1035+
ctx.getSpecializedConformance(type, genericConformance, subMap));
1036+
}
1037+
1038+
return ProtocolConformanceRef::forInvalid();
1039+
}
1040+
1041+
/// Whether the given function type conforms to Sendable.
1042+
static bool isSendableFunctionType(const FunctionType *functionType) {
1043+
if (functionType->isSendable())
1044+
return true;
1045+
1046+
// C and thin function types have no captures, so they are Sendable.
1047+
switch (functionType->getExtInfo().getRepresentation()) {
1048+
case FunctionTypeRepresentation::Block:
1049+
case FunctionTypeRepresentation::Swift:
1050+
return false;
1051+
1052+
case FunctionTypeRepresentation::CFunctionPointer:
1053+
case FunctionTypeRepresentation::Thin:
1054+
return true;
1055+
}
1056+
}
1057+
1058+
/// Synthesize a builtin function type conformance to the given protocol, if
1059+
/// appropriate.
1060+
static ProtocolConformanceRef getBuiltinFunctionTypeConformance(
1061+
Type type, const FunctionType *functionType, ProtocolDecl *protocol) {
1062+
// @Sendable function types are Sendable.
1063+
if (protocol->isSpecificProtocol(KnownProtocolKind::Sendable) &&
1064+
isSendableFunctionType(functionType)) {
1065+
ASTContext &ctx = protocol->getASTContext();
1066+
return ProtocolConformanceRef(
1067+
ctx.getBuiltinConformance(type, protocol, GenericSignature(), { }));
1068+
}
1069+
1070+
return ProtocolConformanceRef::forInvalid();
1071+
}
1072+
1073+
/// Synthesize a builtin metatype type conformance to the given protocol, if
1074+
/// appropriate.
1075+
static ProtocolConformanceRef getBuiltinMetaTypeTypeConformance(
1076+
Type type, const AnyMetatypeType *metatypeType, ProtocolDecl *protocol) {
1077+
// All metatypes are Sendable.
1078+
if (protocol->isSpecificProtocol(KnownProtocolKind::Sendable)) {
1079+
ASTContext &ctx = protocol->getASTContext();
1080+
return ProtocolConformanceRef(
1081+
ctx.getBuiltinConformance(type, protocol, GenericSignature(), { }));
1082+
}
1083+
1084+
return ProtocolConformanceRef::forInvalid();
1085+
}
1086+
1087+
/// Synthesize a builtin type conformance to the given protocol, if
1088+
/// appropriate.
1089+
static ProtocolConformanceRef getBuiltinBuiltinTypeConformance(
1090+
Type type, const BuiltinType *builtinType, ProtocolDecl *protocol) {
1091+
// All builtin are Sendable.
1092+
if (protocol->isSpecificProtocol(KnownProtocolKind::Sendable)) {
1093+
ASTContext &ctx = protocol->getASTContext();
1094+
return ProtocolConformanceRef(
1095+
ctx.getBuiltinConformance(type, protocol, GenericSignature(), { }));
1096+
}
1097+
1098+
return ProtocolConformanceRef::forInvalid();
1099+
}
1100+
9921101
ProtocolConformanceRef
9931102
LookupConformanceInModuleRequest::evaluate(
9941103
Evaluator &evaluator, LookupConformanceDescriptor desc) const {
@@ -1043,6 +1152,26 @@ LookupConformanceInModuleRequest::evaluate(
10431152
if (type->is<UnresolvedType>() || type->is<PlaceholderType>())
10441153
return ProtocolConformanceRef(protocol);
10451154

1155+
// Tuple types can conform to protocols.
1156+
if (auto tupleType = type->getAs<TupleType>()) {
1157+
return getBuiltinTupleTypeConformance(type, tupleType, protocol);
1158+
}
1159+
1160+
// Function types can conform to protocols.
1161+
if (auto functionType = type->getAs<FunctionType>()) {
1162+
return getBuiltinFunctionTypeConformance(type, functionType, protocol);
1163+
}
1164+
1165+
// Metatypes can conform to protocols.
1166+
if (auto metatypeType = type->getAs<AnyMetatypeType>()) {
1167+
return getBuiltinMetaTypeTypeConformance(type, metatypeType, protocol);
1168+
}
1169+
1170+
// Builtin types can conform to protocols.
1171+
if (auto builtinType = type->getAs<BuiltinType>()) {
1172+
return getBuiltinBuiltinTypeConformance(type, builtinType, protocol);
1173+
}
1174+
10461175
auto nominal = type->getAnyNominal();
10471176

10481177
// If we don't have a nominal type, there are no conformances.

lib/AST/ProtocolConformance.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -963,9 +963,15 @@ void SpecializedProtocolConformance::computeConditionalRequirements() const {
963963
// Substitute the conditional requirements so that they're phrased in
964964
// terms of the specialized types, not the conformance-declaring decl's
965965
// types.
966-
auto nominal = GenericConformance->getType()->getAnyNominal();
967-
auto module = nominal->getModuleContext();
968-
auto subMap = getType()->getContextSubstitutionMap(module, nominal);
966+
ModuleDecl *module;
967+
SubstitutionMap subMap;
968+
if (auto nominal = GenericConformance->getType()->getAnyNominal()) {
969+
module = nominal->getModuleContext();
970+
subMap = getType()->getContextSubstitutionMap(module, nominal);
971+
} else {
972+
module = getProtocol()->getModuleContext();
973+
subMap = getSubstitutionMap();
974+
}
969975

970976
SmallVector<Requirement, 4> newReqs;
971977
for (auto oldReq : *parentCondReqs) {
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
func acceptSendable<T: Sendable>(_: T) { } // expected-note{{required by global function 'acceptSendable' where 'T' = '() -> Void'}}
4+
5+
class NotSendable { }
6+
7+
func testSendableBuiltinConformances(
8+
i: Int, ns: NotSendable, sf: @escaping @Sendable () -> Void,
9+
nsf: @escaping () -> Void, mt: NotSendable.Type,
10+
cf: @escaping @convention(c) () -> Void,
11+
funSendable: [(Int, @Sendable () -> Void, NotSendable.Type)?],
12+
funNotSendable: [(Int, () -> Void, NotSendable.Type)?]
13+
) {
14+
acceptSendable(())
15+
acceptSendable(mt)
16+
acceptSendable(sf)
17+
acceptSendable(cf)
18+
acceptSendable((i, sf, mt))
19+
acceptSendable((i, label: sf))
20+
acceptSendable(funSendable)
21+
22+
// Errors
23+
acceptSendable((i, ns)) // expected-error{{global function 'acceptSendable' requires that 'NotSendable' conform to 'Sendable'}}
24+
acceptSendable(nsf) // expected-error{{type '() -> Void' cannot conform to 'Sendable'}}
25+
// FIXME: expected-note@-1{{only concrete types such as structs, enums and classes can conform to protocols}}
26+
acceptSendable((nsf, i)) // expected-error{{type '() -> Void' cannot conform to 'Sendable'}}
27+
// FIXME: expected-note@-1{{only concrete types such as structs, enums and classes can conform to protocols}}
28+
// expected-note@-2{{requirement from conditional conformance of '(() -> Void, Int)' to 'Sendable'}}
29+
acceptSendable(funNotSendable) // expected-error{{type '() -> Void' cannot conform to 'Sendable'}}
30+
// FIXME: expected-note@-1{{only concrete types such as structs, enums and classes can conform to protocols}}
31+
// expected-note@-2{{requirement from conditional conformance of '(Int, () -> Void, NotSendable.Type)' to 'Sendable'}}
32+
}

0 commit comments

Comments
 (0)