Skip to content

Commit c79fa23

Browse files
committed
Implicitly build "missing" conformances to Sendable.
When looking up a conformance to Sendable fails, implicitly create a "missing" builtin conformance. Such conformances allow type checking to continue even in the presence of Sendable-related problems. Diagnose these missing conformances when they are used in an actual program, as part of availability checking for conformances and when we are determining Sendability. This allows us to decide between an error, a warning, and suppressing the diagnostic entirely without affecting how the program is compiled. This is a step toward enabling selective enforcement of Sendable. Part of rdar://78269348.
1 parent 64c6707 commit c79fa23

File tree

13 files changed

+176
-45
lines changed

13 files changed

+176
-45
lines changed

include/swift/AST/ASTContext.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ namespace swift {
106106
class InheritedProtocolConformance;
107107
class SelfProtocolConformance;
108108
class SpecializedProtocolConformance;
109+
enum class BuiltinConformanceKind;
109110
class BuiltinProtocolConformance;
110111
enum class ProtocolConformanceState;
111112
class Pattern;
@@ -1046,7 +1047,8 @@ class ASTContext final {
10461047
BuiltinProtocolConformance *
10471048
getBuiltinConformance(Type type, ProtocolDecl *protocol,
10481049
GenericSignature genericSig,
1049-
ArrayRef<Requirement> conditionalRequirements);
1050+
ArrayRef<Requirement> conditionalRequirements,
1051+
BuiltinConformanceKind kind);
10501052

10511053
/// A callback used to produce a diagnostic for an ill-formed protocol
10521054
/// conformance that was type-checked before we're actually walking the

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4516,6 +4516,11 @@ ERROR(concurrent_value_inherit,none,
45164516
"`Sendable` class %1 cannot inherit from another class"
45174517
"%select{| other than 'NSObject'}0",
45184518
(bool, DeclName))
4519+
ERROR(non_sendable_type,none,
4520+
"type %0 does not conform to the 'Sendable' protocol", (Type))
4521+
ERROR(non_sendable_function_type,none,
4522+
"function type %0 must be marked '@Sendable' to conform to 'Sendable'",
4523+
(Type))
45194524

45204525
WARNING(unchecked_conformance_not_special,none,
45214526
"@unchecked conformance to %0 has no meaning", (Type))

include/swift/AST/ProtocolConformance.h

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -993,6 +993,15 @@ class InheritedProtocolConformance : public ProtocolConformance,
993993
}
994994
};
995995

996+
/// Describes the kind of a builtin conformance.
997+
enum class BuiltinConformanceKind {
998+
// A builtin conformance that has been synthesized by the implementation.
999+
Synthesized = 0,
1000+
// A missing conformance that we have nonetheless synthesized so that
1001+
// we can diagnose it later.
1002+
Missing,
1003+
};
1004+
9961005
/// A builtin conformance appears when a non-nominal type has a
9971006
/// conformance that is synthesized by the implementation.
9981007
class BuiltinProtocolConformance final : public RootProtocolConformance,
@@ -1002,15 +1011,17 @@ class BuiltinProtocolConformance final : public RootProtocolConformance,
10021011

10031012
ProtocolDecl *protocol;
10041013
GenericSignature genericSig;
1005-
size_t numConditionalRequirements;
1014+
size_t numConditionalRequirements : 31;
1015+
unsigned builtinConformanceKind : 1;
10061016

10071017
size_t numTrailingObjects(OverloadToken<Requirement>) const {
10081018
return numConditionalRequirements;
10091019
}
10101020

10111021
BuiltinProtocolConformance(Type conformingType, ProtocolDecl *protocol,
10121022
GenericSignature genericSig,
1013-
ArrayRef<Requirement> conditionalRequirements);
1023+
ArrayRef<Requirement> conditionalRequirements,
1024+
BuiltinConformanceKind kind);
10141025

10151026
public:
10161027
/// Get the protocol being conformed to.
@@ -1024,6 +1035,16 @@ class BuiltinProtocolConformance final : public RootProtocolConformance,
10241035
return genericSig;
10251036
}
10261037

1038+
BuiltinConformanceKind getBuiltinConformanceKind() const {
1039+
return static_cast<BuiltinConformanceKind>(builtinConformanceKind);
1040+
}
1041+
1042+
/// Whether this represents a "missing" conformance that should be diagnosed
1043+
/// later.
1044+
bool isMissing() const {
1045+
return getBuiltinConformanceKind() == BuiltinConformanceKind::Missing;
1046+
}
1047+
10271048
/// Get any requirements that must be satisfied for this conformance to apply.
10281049
Optional<ArrayRef<Requirement>>
10291050
getConditionalRequirementsIfAvailable() const {

lib/AST/ASTContext.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2331,7 +2331,8 @@ BuiltinProtocolConformance *
23312331
ASTContext::getBuiltinConformance(
23322332
Type type, ProtocolDecl *protocol,
23332333
GenericSignature genericSig,
2334-
ArrayRef<Requirement> conditionalRequirements
2334+
ArrayRef<Requirement> conditionalRequirements,
2335+
BuiltinConformanceKind kind
23352336
) {
23362337
auto key = std::make_pair(type, protocol);
23372338
AllocationArena arena = getArena(type->getRecursiveProperties());
@@ -2343,7 +2344,7 @@ ASTContext::getBuiltinConformance(
23432344
totalSizeToAlloc<Requirement>(conditionalRequirements.size());
23442345
auto mem = this->Allocate(size, alignof(BuiltinProtocolConformance), arena);
23452346
entry = new (mem) BuiltinProtocolConformance(
2346-
type, protocol, genericSig, conditionalRequirements);
2347+
type, protocol, genericSig, conditionalRequirements, kind);
23472348
}
23482349
return entry;
23492350
}

lib/AST/Module.cpp

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

992+
/// Retrieve an invalid or missing conformance, as appropriate, when a
993+
/// legitimate conformance doesn't exist.
994+
static ProtocolConformanceRef getInvalidOrMissingConformance(
995+
Type type, ProtocolDecl *proto) {
996+
// Introduce "missing" conformances to Sendable, so that type checking
997+
// (and even code generation) can continue.
998+
ASTContext &ctx = proto->getASTContext();
999+
if (proto->isSpecificProtocol(KnownProtocolKind::Sendable) &&
1000+
ctx.LangOpts.WarnConcurrency) {
1001+
return ProtocolConformanceRef(
1002+
ctx.getBuiltinConformance(
1003+
type, proto, GenericSignature(), { },
1004+
BuiltinConformanceKind::Missing));
1005+
}
1006+
1007+
return ProtocolConformanceRef::forInvalid();
1008+
}
1009+
9921010
/// Synthesize a builtin tuple type conformance to the given protocol, if
9931011
/// appropriate.
9941012
static ProtocolConformanceRef getBuiltinTupleTypeConformance(
995-
Type type, const TupleType *tupleType, ProtocolDecl *protocol) {
1013+
Type type, const TupleType *tupleType, ProtocolDecl *protocol,
1014+
ModuleDecl *module) {
9961015
// Tuple type are Sendable when all of their element types are Sendable.
9971016
if (protocol->isSpecificProtocol(KnownProtocolKind::Sendable)) {
9981017
ASTContext &ctx = protocol->getASTContext();
@@ -1016,26 +1035,38 @@ static ProtocolConformanceRef getBuiltinTupleTypeConformance(
10161035
// If there were no generic parameters, just form the builtin conformance.
10171036
if (genericParams.empty()) {
10181037
return ProtocolConformanceRef(
1019-
ctx.getBuiltinConformance(type, protocol, GenericSignature(), { }));
1038+
ctx.getBuiltinConformance(type, protocol, GenericSignature(), { },
1039+
BuiltinConformanceKind::Synthesized));
10201040
}
10211041

10221042
// Form a generic conformance of (T1, T2, ..., TN): Sendable with signature
10231043
// <T1, T2, ..., TN> and conditional requirements T1: Sendable,
10241044
// T2: Sendable, ..., TN: Sendable.
10251045
auto genericTupleType = TupleType::get(genericElements, ctx);
1026-
auto genericSig = GenericSignature::get(genericParams, { });
1046+
auto genericSig = GenericSignature::get(
1047+
genericParams, conditionalRequirements);
10271048
auto genericConformance = ctx.getBuiltinConformance(
1028-
genericTupleType, protocol, genericSig, conditionalRequirements);
1049+
genericTupleType, protocol, genericSig, conditionalRequirements,
1050+
BuiltinConformanceKind::Synthesized);
10291051

10301052
// Compute the substitution map from the generic parameters of the
10311053
// generic conformance to actual types that were in the tuple type.
10321054
// Form a specialized conformance from that.
1033-
auto subMap = SubstitutionMap::get(genericSig, typeSubstitutions, { });
1055+
auto subMap = SubstitutionMap::get(
1056+
genericSig, [&](SubstitutableType *type) {
1057+
if (auto gp = dyn_cast<GenericTypeParamType>(type)) {
1058+
if (gp->getDepth() == 0)
1059+
return typeSubstitutions[gp->getIndex()];
1060+
}
1061+
1062+
return Type(type);
1063+
},
1064+
LookUpConformanceInModule(module));
10341065
return ProtocolConformanceRef(
10351066
ctx.getSpecializedConformance(type, genericConformance, subMap));
10361067
}
10371068

1038-
return ProtocolConformanceRef::forInvalid();
1069+
return getInvalidOrMissingConformance(type, protocol);
10391070
}
10401071

10411072
/// Whether the given function type conforms to Sendable.
@@ -1064,10 +1095,11 @@ static ProtocolConformanceRef getBuiltinFunctionTypeConformance(
10641095
isSendableFunctionType(functionType)) {
10651096
ASTContext &ctx = protocol->getASTContext();
10661097
return ProtocolConformanceRef(
1067-
ctx.getBuiltinConformance(type, protocol, GenericSignature(), { }));
1098+
ctx.getBuiltinConformance(type, protocol, GenericSignature(), { },
1099+
BuiltinConformanceKind::Synthesized));
10681100
}
10691101

1070-
return ProtocolConformanceRef::forInvalid();
1102+
return getInvalidOrMissingConformance(type, protocol);
10711103
}
10721104

10731105
/// Synthesize a builtin metatype type conformance to the given protocol, if
@@ -1078,10 +1110,11 @@ static ProtocolConformanceRef getBuiltinMetaTypeTypeConformance(
10781110
if (protocol->isSpecificProtocol(KnownProtocolKind::Sendable)) {
10791111
ASTContext &ctx = protocol->getASTContext();
10801112
return ProtocolConformanceRef(
1081-
ctx.getBuiltinConformance(type, protocol, GenericSignature(), { }));
1113+
ctx.getBuiltinConformance(type, protocol, GenericSignature(), { },
1114+
BuiltinConformanceKind::Synthesized));
10821115
}
10831116

1084-
return ProtocolConformanceRef::forInvalid();
1117+
return getInvalidOrMissingConformance(type, protocol);
10851118
}
10861119

10871120
/// Synthesize a builtin type conformance to the given protocol, if
@@ -1092,10 +1125,11 @@ static ProtocolConformanceRef getBuiltinBuiltinTypeConformance(
10921125
if (protocol->isSpecificProtocol(KnownProtocolKind::Sendable)) {
10931126
ASTContext &ctx = protocol->getASTContext();
10941127
return ProtocolConformanceRef(
1095-
ctx.getBuiltinConformance(type, protocol, GenericSignature(), { }));
1128+
ctx.getBuiltinConformance(type, protocol, GenericSignature(), { },
1129+
BuiltinConformanceKind::Synthesized));
10961130
}
10971131

1098-
return ProtocolConformanceRef::forInvalid();
1132+
return getInvalidOrMissingConformance(type, protocol);
10991133
}
11001134

11011135
ProtocolConformanceRef
@@ -1133,14 +1167,18 @@ LookupConformanceInModuleRequest::evaluate(
11331167
return ProtocolConformanceRef(protocol);
11341168
}
11351169

1136-
return ProtocolConformanceRef::forInvalid();
1170+
return getInvalidOrMissingConformance(type, protocol);
11371171
}
11381172

11391173
// An existential conforms to a protocol if the protocol is listed in the
11401174
// existential's list of conformances and the existential conforms to
11411175
// itself.
1142-
if (type->isExistentialType())
1143-
return mod->lookupExistentialConformance(type, protocol);
1176+
if (type->isExistentialType()) {
1177+
auto result = mod->lookupExistentialConformance(type, protocol);
1178+
if (result.isInvalid())
1179+
return getInvalidOrMissingConformance(type, protocol);
1180+
return result;
1181+
}
11441182

11451183
// Type variables have trivial conformances.
11461184
if (type->isTypeVariableOrMember())
@@ -1154,7 +1192,7 @@ LookupConformanceInModuleRequest::evaluate(
11541192

11551193
// Tuple types can conform to protocols.
11561194
if (auto tupleType = type->getAs<TupleType>()) {
1157-
return getBuiltinTupleTypeConformance(type, tupleType, protocol);
1195+
return getBuiltinTupleTypeConformance(type, tupleType, protocol, mod);
11581196
}
11591197

11601198
// Function types can conform to protocols.
@@ -1176,13 +1214,13 @@ LookupConformanceInModuleRequest::evaluate(
11761214

11771215
// If we don't have a nominal type, there are no conformances.
11781216
if (!nominal || isa<ProtocolDecl>(nominal))
1179-
return ProtocolConformanceRef::forInvalid();
1217+
return getInvalidOrMissingConformance(type, protocol);
11801218

11811219
// Find the (unspecialized) conformance.
11821220
SmallVector<ProtocolConformance *, 2> conformances;
11831221
if (!nominal->lookupConformance(mod, protocol, conformances)) {
11841222
if (!protocol->isSpecificProtocol(KnownProtocolKind::Sendable))
1185-
return ProtocolConformanceRef::forInvalid();
1223+
return getInvalidOrMissingConformance(type, protocol);
11861224

11871225
// Try to infer Sendable conformance.
11881226
GetImplicitSendableRequest cvRequest{nominal};
@@ -1191,7 +1229,7 @@ LookupConformanceInModuleRequest::evaluate(
11911229
conformances.clear();
11921230
conformances.push_back(conformance);
11931231
} else {
1194-
return ProtocolConformanceRef::forInvalid();
1232+
return getInvalidOrMissingConformance(type, protocol);
11951233
}
11961234
}
11971235

lib/AST/ProtocolConformance.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1591,10 +1591,13 @@ ProtocolConformanceRef::getCanonicalConformanceRef() const {
15911591
BuiltinProtocolConformance::BuiltinProtocolConformance(
15921592
Type conformingType, ProtocolDecl *protocol,
15931593
GenericSignature genericSig,
1594-
ArrayRef<Requirement> conditionalRequirements
1594+
ArrayRef<Requirement> conditionalRequirements,
1595+
BuiltinConformanceKind kind
15951596
) : RootProtocolConformance(ProtocolConformanceKind::Builtin, conformingType),
15961597
protocol(protocol), genericSig(genericSig),
1597-
numConditionalRequirements(conditionalRequirements.size()) {
1598+
numConditionalRequirements(conditionalRequirements.size()),
1599+
builtinConformanceKind(static_cast<unsigned>(kind))
1600+
{
15981601
std::uninitialized_copy(conditionalRequirements.begin(),
15991602
conditionalRequirements.end(),
16001603
getTrailingObjects<Requirement>());

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
//===----------------------------------------------------------------------===//
1616

1717
#include "TypeCheckAvailability.h"
18+
#include "TypeCheckConcurrency.h"
1819
#include "TypeChecker.h"
1920
#include "TypeCheckObjC.h"
2021
#include "MiscDiagnostics.h"
@@ -3478,6 +3479,12 @@ void swift::diagnoseTypeAvailability(const TypeRepr *TR, Type T, SourceLoc loc,
34783479
diagnoseTypeAvailability(T, loc, where, flags);
34793480
}
34803481

3482+
static void diagnoseMissingConformance(
3483+
SourceLoc loc, Type type, ProtocolDecl *proto) {
3484+
assert(proto->isSpecificProtocol(KnownProtocolKind::Sendable));
3485+
diagnoseMissingSendableConformance(loc, type);
3486+
}
3487+
34813488
bool
34823489
swift::diagnoseConformanceAvailability(SourceLoc loc,
34833490
ProtocolConformanceRef conformance,
@@ -3491,6 +3498,15 @@ swift::diagnoseConformanceAvailability(SourceLoc loc,
34913498
const ProtocolConformance *concreteConf = conformance.getConcrete();
34923499
const RootProtocolConformance *rootConf = concreteConf->getRootConformance();
34933500

3501+
// Diagnose "missing" conformances where we needed a conformance but
3502+
// didn't have one.
3503+
if (auto builtinConformance = dyn_cast<BuiltinProtocolConformance>(rootConf)){
3504+
if (builtinConformance->isMissing()) {
3505+
diagnoseMissingConformance(loc, builtinConformance->getType(),
3506+
builtinConformance->getProtocol());
3507+
}
3508+
}
3509+
34943510
auto *DC = where.getDeclContext();
34953511

34963512
auto maybeEmitAssociatedTypeNote = [&]() {

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,13 +546,44 @@ static bool isSendableClosure(
546546
return false;
547547
}
548548

549+
/// Look for missing Sendable conformances
550+
static bool hasMissingSendableConformance(
551+
ModuleDecl *module, ProtocolConformanceRef conformance) {
552+
if (!conformance.isConcrete())
553+
return false;
554+
555+
// Is this a missing Sendable conformance?
556+
const ProtocolConformance *concreteConf = conformance.getConcrete();
557+
const RootProtocolConformance *rootConf = concreteConf->getRootConformance();
558+
if (auto builtinConformance = dyn_cast<BuiltinProtocolConformance>(rootConf)){
559+
if (builtinConformance->isMissing() && builtinConformance->getProtocol()->isSpecificProtocol(
560+
KnownProtocolKind::Sendable)) {
561+
return true;
562+
}
563+
}
564+
565+
// Check conformances that are part of this conformance.
566+
auto subMap = concreteConf->getSubstitutions(module);
567+
for (auto conformance : subMap.getConformances()) {
568+
if (hasMissingSendableConformance(module, conformance))
569+
return true;
570+
}
571+
572+
return false;
573+
}
574+
549575
/// Determine whether the given type is suitable as a concurrent value type.
550576
bool swift::isSendableType(ModuleDecl *module, Type type) {
551577
auto proto = module->getASTContext().getProtocol(KnownProtocolKind::Sendable);
552578
if (!proto)
553579
return true;
554580

555-
return !TypeChecker::conformsToProtocol(type, proto, module).isInvalid();
581+
auto conformance = TypeChecker::conformsToProtocol(type, proto, module);
582+
if (conformance.isInvalid())
583+
return false;
584+
585+
// Look for missing Sendable conformances.
586+
return !hasMissingSendableConformance(module, conformance);
556587
}
557588

558589
static bool diagnoseNonConcurrentParameter(
@@ -653,6 +684,17 @@ bool swift::diagnoseNonConcurrentTypesInReference(
653684
return false;
654685
}
655686

687+
void swift::diagnoseMissingSendableConformance(SourceLoc loc, Type type) {
688+
ASTContext &ctx = type->getASTContext();
689+
if (type->is<FunctionType>()) {
690+
ctx.Diags.diagnose(loc, diag::non_sendable_function_type, type)
691+
.warnUntilSwiftVersion(6);
692+
} else {
693+
ctx.Diags.diagnose(loc, diag::non_sendable_type, type)
694+
.warnUntilSwiftVersion(6);
695+
}
696+
}
697+
656698
/// Determine whether this is the main actor type.
657699
/// FIXME: the diagnostics engine has a copy of this.
658700
static bool isMainActor(Type type) {

0 commit comments

Comments
 (0)