Skip to content

Commit ffca696

Browse files
committed
[Isolated conformances] Diagnose conflict with isolated conformances and Sendable
1 parent f45271f commit ffca696

14 files changed

+214
-76
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2722,23 +2722,8 @@ WARNING(add_predates_concurrency_import,none,
27222722
GROUPED_WARNING(remove_predates_concurrency_import,PreconcurrencyImport,
27232723
DefaultIgnore,
27242724
"'@preconcurrency' attribute on module %0 has no effect", (Identifier))
2725-
NOTE(add_isolated_to_conformance,none,
2726-
"add 'isolated' to the %0 conformance to restrict it to %1 code",
2727-
(DeclName, ActorIsolation))
27282725
NOTE(add_preconcurrency_to_conformance,none,
27292726
"add '@preconcurrency' to the %0 conformance to defer isolation checking to run time", (DeclName))
2730-
ERROR(isolated_conformance_not_global_actor_isolated,none,
2731-
"isolated conformance is only permitted on global-actor-isolated types",
2732-
())
2733-
ERROR(isolated_conformance_experimental_feature,none,
2734-
"isolated conformances require experimental feature "
2735-
" 'IsolatedConformances'", ())
2736-
ERROR(nonisolated_conformance_depends_on_isolated_conformance,none,
2737-
"conformance of %0 to %1 depends on %2 conformance of %3 to %4; mark it as 'isolated'",
2738-
(Type, DeclName, ActorIsolation, Type, DeclName))
2739-
ERROR(isolated_conformance_mismatch_with_associated_isolation,none,
2740-
"%0 conformance of %1 to %2 cannot depend on %3 conformance of %4 to %5",
2741-
(ActorIsolation, Type, DeclName, ActorIsolation, Type, DeclName))
27422727
WARNING(remove_public_import,none,
27432728
"public import of %0 was not used in public declarations or inlinable code",
27442729
(Identifier))
@@ -8309,6 +8294,29 @@ ERROR(attr_abi_incompatible_with_silgen_name,none,
83098294
"the same purpose",
83108295
(DescriptiveDeclKind))
83118296

8297+
//===----------------------------------------------------------------------===//
8298+
// MARK: Isolated conformances
8299+
//===----------------------------------------------------------------------===//
8300+
ERROR(isolated_conformance_not_global_actor_isolated,none,
8301+
"isolated conformance is only permitted on global-actor-isolated types",
8302+
())
8303+
ERROR(isolated_conformance_experimental_feature,none,
8304+
"isolated conformances require experimental feature "
8305+
" 'IsolatedConformances'", ())
8306+
ERROR(nonisolated_conformance_depends_on_isolated_conformance,none,
8307+
"conformance of %0 to %1 depends on %2 conformance of %3 to %4; mark it as 'isolated'",
8308+
(Type, DeclName, ActorIsolation, Type, DeclName))
8309+
ERROR(isolated_conformance_mismatch_with_associated_isolation,none,
8310+
"%0 conformance of %1 to %2 cannot depend on %3 conformance of %4 to %5",
8311+
(ActorIsolation, Type, DeclName, ActorIsolation, Type, DeclName))
8312+
NOTE(add_isolated_to_conformance,none,
8313+
"add 'isolated' to the %0 conformance to restrict it to %1 code",
8314+
(DeclName, ActorIsolation))
8315+
ERROR(isolated_conformance_with_sendable,none,
8316+
"isolated conformance of %0 to %1 cannot be used to satisfy conformance "
8317+
"requirement for a %select{`Sendable`|`SendableMetatype`}2 type "
8318+
"parameter %3", (Type, DeclName, bool, Type))
8319+
83128320
//===----------------------------------------------------------------------===//
83138321
// MARK: @execution Attribute
83148322
//===----------------------------------------------------------------------===//

include/swift/AST/ProtocolConformanceRef.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,14 @@ class ProtocolConformanceRef {
140140
bool forEachMissingConformance(
141141
llvm::function_ref<bool(BuiltinProtocolConformance *missing)> fn) const;
142142

143+
/// Enumerate all of the isolated conformances in the given conformance.
144+
///
145+
/// The given `body` will be called on each isolated conformance. If it ever
146+
/// returns `true`, this function will abort the search and return `true`.
147+
bool forEachIsolatedConformance(
148+
llvm::function_ref<bool(ProtocolConformance*)> body
149+
) const;
150+
143151
using OpaqueValue = void*;
144152
OpaqueValue getOpaqueValue() const { return Union.getOpaqueValue(); }
145153
static ProtocolConformanceRef getFromOpaqueValue(OpaqueValue value) {

include/swift/AST/Requirement.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,9 +187,13 @@ class Requirement {
187187
/// \param subReqs An out parameter initialized to a list of simpler
188188
/// requirements which the caller must check to ensure this
189189
/// requirement is completely satisfied.
190+
/// \param isolatedConformances If non-NULL, will be provided with all of the
191+
/// isolated conformances that
190192
CheckRequirementResult checkRequirement(
191193
SmallVectorImpl<Requirement> &subReqs,
192-
bool allowMissing = false) const;
194+
bool allowMissing = false,
195+
SmallVectorImpl<ProtocolConformance *> *isolatedConformances = nullptr
196+
) const;
193197

194198
/// Determines if this substituted requirement can ever be satisfied,
195199
/// possibly with additional substitutions.

lib/AST/ProtocolConformanceRef.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,42 @@ bool ProtocolConformanceRef::forEachMissingConformance(
386386
return false;
387387
}
388388

389+
bool ProtocolConformanceRef::forEachIsolatedConformance(
390+
llvm::function_ref<bool(ProtocolConformance*)> body
391+
) const {
392+
if (isInvalid() || isAbstract())
393+
return false;
394+
395+
if (isPack()) {
396+
auto pack = getPack()->getPatternConformances();
397+
for (auto conformance : pack) {
398+
if (conformance.forEachIsolatedConformance(body))
399+
return true;
400+
}
401+
402+
return false;
403+
}
404+
405+
// Is this an isolated conformance?
406+
auto concrete = getConcrete();
407+
if (auto normal =
408+
dyn_cast<NormalProtocolConformance>(concrete->getRootConformance())) {
409+
if (normal->isIsolated()) {
410+
if (body(concrete))
411+
return true;
412+
}
413+
}
414+
415+
// Check conformances that are part of this conformance.
416+
auto subMap = concrete->getSubstitutionMap();
417+
for (auto conformance : subMap.getConformances()) {
418+
if (conformance.forEachIsolatedConformance(body))
419+
return true;
420+
}
421+
422+
return false;
423+
}
424+
389425
void swift::simple_display(llvm::raw_ostream &out, ProtocolConformanceRef conformanceRef) {
390426
if (conformanceRef.isAbstract()) {
391427
simple_display(out, conformanceRef.getAbstract());

lib/AST/Requirement.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,9 @@ ProtocolDecl *Requirement::getProtocolDecl() const {
8282

8383
CheckRequirementResult Requirement::checkRequirement(
8484
SmallVectorImpl<Requirement> &subReqs,
85-
bool allowMissing) const {
85+
bool allowMissing,
86+
SmallVectorImpl<ProtocolConformance *> *isolatedConformances
87+
) const {
8688
if (hasError())
8789
return CheckRequirementResult::SubstitutionFailure;
8890

@@ -122,6 +124,15 @@ CheckRequirementResult Requirement::checkRequirement(
122124
if (!conformance)
123125
return CheckRequirementResult::RequirementFailure;
124126

127+
// Collect isolated conformances.
128+
if (isolatedConformances) {
129+
conformance.forEachIsolatedConformance(
130+
[&](ProtocolConformance *isolatedConformance) {
131+
isolatedConformances->push_back(isolatedConformance);
132+
return false;
133+
});
134+
}
135+
125136
auto condReqs = conformance.getConditionalRequirements();
126137
if (condReqs.empty())
127138
return CheckRequirementResult::Success;

lib/AST/RequirementMachine/RequirementLowering.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ static void desugarSuperclassRequirement(
300300

301301
SmallVector<Requirement, 2> subReqs;
302302

303-
switch (req.checkRequirement(subReqs)) {
303+
switch (req.checkRequirement(subReqs, /*allowMissing=*/false)) {
304304
case CheckRequirementResult::Success:
305305
case CheckRequirementResult::PackRequirement:
306306
break;
@@ -333,7 +333,7 @@ static void desugarLayoutRequirement(
333333

334334
SmallVector<Requirement, 2> subReqs;
335335

336-
switch (req.checkRequirement(subReqs)) {
336+
switch (req.checkRequirement(subReqs, /*allowMissing=*/false)) {
337337
case CheckRequirementResult::Success:
338338
case CheckRequirementResult::PackRequirement:
339339
break;

lib/IDE/IDETypeChecking.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ struct SynthesizedExtensionAnalyzer::Implementation {
363363

364364
while (!subReqs.empty()) {
365365
auto req = subReqs.pop_back_val();
366-
switch (req.checkRequirement(subReqs)) {
366+
switch (req.checkRequirement(subReqs, /*allowMissing=*/false)) {
367367
case CheckRequirementResult::Success:
368368
case CheckRequirementResult::PackRequirement:
369369
case CheckRequirementResult::ConditionalConformance:

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -7669,43 +7669,6 @@ bool swift::diagnoseNonSendableFromDeinit(
76697669
var->getDescriptiveKind(), var->getName());
76707670
}
76717671

7672-
bool swift::forEachIsolatedConformance(
7673-
ProtocolConformanceRef conformance,
7674-
llvm::function_ref<bool(ProtocolConformance*)> body
7675-
) {
7676-
if (conformance.isInvalid() || conformance.isAbstract())
7677-
return false;
7678-
7679-
if (conformance.isPack()) {
7680-
auto pack = conformance.getPack()->getPatternConformances();
7681-
for (auto conformance : pack) {
7682-
if (forEachIsolatedConformance(conformance, body))
7683-
return true;
7684-
}
7685-
7686-
return false;
7687-
}
7688-
7689-
// Is this an isolated conformance?
7690-
auto concrete = conformance.getConcrete();
7691-
if (auto normal =
7692-
dyn_cast<NormalProtocolConformance>(concrete->getRootConformance())) {
7693-
if (normal->isIsolated()) {
7694-
if (body(concrete))
7695-
return true;
7696-
}
7697-
}
7698-
7699-
// Check conformances that are part of this conformance.
7700-
auto subMap = concrete->getSubstitutionMap();
7701-
for (auto conformance : subMap.getConformances()) {
7702-
if (forEachIsolatedConformance(conformance, body))
7703-
return true;
7704-
}
7705-
7706-
return false;
7707-
}
7708-
77097672
ActorIsolation swift::getConformanceIsolation(ProtocolConformance *conformance) {
77107673
auto rootNormal =
77117674
dyn_cast<NormalProtocolConformance>(conformance->getRootConformance());

lib/Sema/TypeCheckConcurrency.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -699,15 +699,6 @@ void introduceUnsafeInheritExecutorReplacements(
699699
void introduceUnsafeInheritExecutorReplacements(
700700
const DeclContext *dc, Type base, SourceLoc loc, LookupResult &result);
701701

702-
/// Enumerate all of the isolated conformances in the given conformance.
703-
///
704-
/// The given `body` will be called on each isolated conformance. If it ever
705-
/// returns `true`, this function will abort the search and return `true`.
706-
bool forEachIsolatedConformance(
707-
ProtocolConformanceRef conformance,
708-
llvm::function_ref<bool(ProtocolConformance*)> body
709-
);
710-
711702
/// Determine the isolation of the given conformance. This only applies to
712703
/// the immediate conformance, not any conformances on which it depends.
713704
ActorIsolation getConformanceIsolation(ProtocolConformance *conformance);

lib/Sema/TypeCheckGeneric.cpp

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -905,7 +905,7 @@ void TypeChecker::diagnoseRequirementFailure(
905905
const auto &req = reqFailureInfo.Req;
906906
const auto &substReq = reqFailureInfo.SubstReq;
907907

908-
Diag<Type, Type, Type> diagnostic;
908+
std::optional<Diag<Type, Type, Type>> diagnostic;
909909
Diag<Type, Type, StringRef> diagnosticNote;
910910

911911
const auto reqKind = req.getKind();
@@ -923,6 +923,24 @@ void TypeChecker::diagnoseRequirementFailure(
923923
break;
924924

925925
case RequirementKind::Conformance: {
926+
// If this was a failure due to isolated conformances conflicting with
927+
// a Sendable or MetatypeSendable requirement, diagnose that.
928+
if (reqFailureInfo.IsolatedConformanceProto) {
929+
ASTContext &ctx =
930+
reqFailureInfo.IsolatedConformanceProto->getASTContext();
931+
auto isolatedConformance = reqFailureInfo.IsolatedConformances.front();
932+
ctx.Diags.diagnose(
933+
errorLoc, diag::isolated_conformance_with_sendable,
934+
isolatedConformance->getType(),
935+
isolatedConformance->getProtocol()->getName(),
936+
reqFailureInfo
937+
.IsolatedConformanceProto->isSpecificProtocol(
938+
KnownProtocolKind::SendableMetatype),
939+
req.getFirstType());
940+
diagnosticNote = diag::type_does_not_inherit_or_conform_requirement;
941+
break;
942+
}
943+
926944
diagnoseConformanceFailure(substReq.getFirstType(),
927945
substReq.getProtocolDecl(), nullptr, errorLoc);
928946

@@ -951,9 +969,12 @@ void TypeChecker::diagnoseRequirementFailure(
951969
}
952970

953971
ASTContext &ctx = targetTy->getASTContext();
954-
// FIXME: Poor source-location information.
955-
ctx.Diags.diagnose(errorLoc, diagnostic, targetTy, substReq.getFirstType(),
956-
substSecondTy);
972+
973+
if (diagnostic) {
974+
// FIXME: Poor source-location information.
975+
ctx.Diags.diagnose(errorLoc, *diagnostic, targetTy, substReq.getFirstType(),
976+
substSecondTy);
977+
}
957978

958979
const auto genericParamBindingsText = gatherGenericParamBindingsText(
959980
{req.getFirstType(), secondTy}, genericParams, substitutions);
@@ -966,6 +987,7 @@ void TypeChecker::diagnoseRequirementFailure(
966987
}
967988

968989
CheckGenericArgumentsResult TypeChecker::checkGenericArgumentsForDiagnostics(
990+
GenericSignature signature,
969991
ArrayRef<Requirement> requirements,
970992
TypeSubstitutionFn substitutions) {
971993
using ParentConditionalConformances =
@@ -1004,7 +1026,9 @@ CheckGenericArgumentsResult TypeChecker::checkGenericArgumentsForDiagnostics(
10041026
auto substReq = item.SubstReq;
10051027

10061028
SmallVector<Requirement, 2> subReqs;
1007-
switch (substReq.checkRequirement(subReqs, /*allowMissing=*/true)) {
1029+
SmallVector<ProtocolConformance *, 2> isolatedConformances;
1030+
switch (substReq.checkRequirement(subReqs, /*allowMissing=*/true,
1031+
&isolatedConformances)) {
10081032
case CheckRequirementResult::Success:
10091033
break;
10101034

@@ -1036,6 +1060,38 @@ CheckGenericArgumentsResult TypeChecker::checkGenericArgumentsForDiagnostics(
10361060
hadSubstFailure = true;
10371061
break;
10381062
}
1063+
1064+
if (!isolatedConformances.empty()) {
1065+
// Dig out the original type parameter for the requirement.
1066+
// FIXME: req might not be the right pre-substituted requirement,
1067+
// if this came from a conditional requirement.
1068+
auto firstType = req.getFirstType();
1069+
1070+
// An isolated conformance cannot be used in a context where the type
1071+
// parameter can escape the isolation domain in which the conformance
1072+
// was formed. To establish this, we look for Sendable or SendableMetatype
1073+
// requirements on the type parameter itself.
1074+
ASTContext &ctx = firstType->getASTContext();
1075+
auto sendableProto = ctx.getProtocol(KnownProtocolKind::Sendable);
1076+
auto sendableMetatypeProto =
1077+
ctx.getProtocol(KnownProtocolKind::SendableMetatype);
1078+
if (firstType->isTypeParameter()) {
1079+
std::optional<ProtocolDecl *> failedProtocol;
1080+
if (sendableProto &&
1081+
signature->requiresProtocol(firstType, sendableProto))
1082+
failedProtocol = sendableProto;
1083+
else if (sendableMetatypeProto &&
1084+
signature->requiresProtocol(firstType, sendableMetatypeProto))
1085+
failedProtocol = sendableMetatypeProto;
1086+
1087+
if (failedProtocol) {
1088+
return CheckGenericArgumentsResult::createIsolatedConformanceFailure(
1089+
req, substReq,
1090+
TinyPtrVector<ProtocolConformance *>(isolatedConformances),
1091+
*failedProtocol);
1092+
}
1093+
}
1094+
}
10391095
}
10401096

10411097
if (hadSubstFailure)

0 commit comments

Comments
 (0)