Skip to content

Commit b5cce1f

Browse files
committed
[SE-0466] Don't infer @mainactor on types conforming to Sendable
When the default isolation is main-actor, don't infer @mainactor for a type that conforms to a protocol P in its primary definition when P inherits from Sendable. Such types should remain non-isolated because they're highly unlikely to be able to implement the P conformance (which cannot be isolated). Put this feature behind a new experimental flag, SendableProhibitsMainActorInference. Implements rdar://151029300
1 parent 1aaae85 commit b5cce1f

File tree

4 files changed

+91
-15
lines changed

4 files changed

+91
-15
lines changed

include/swift/Basic/Features.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,10 @@ EXPERIMENTAL_FEATURE(DefaultIsolationPerFile, false)
530530
/// Enable @_lifetime attribute
531531
SUPPRESSIBLE_EXPERIMENTAL_FEATURE(Lifetimes, true)
532532

533+
/// Disable @MainActor inference when the primary definition of a type conforms
534+
/// to SendableMetatype (or Sendable).
535+
EXPERIMENTAL_FEATURE(SendableProhibitsMainActorInference, true)
536+
533537
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
534538
#undef EXPERIMENTAL_FEATURE
535539
#undef UPCOMING_FEATURE

lib/AST/FeatureSet.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ UNINTERESTING_FEATURE(MacrosOnImports)
127127
UNINTERESTING_FEATURE(ExtensibleEnums)
128128
UNINTERESTING_FEATURE(NonisolatedNonsendingByDefault)
129129
UNINTERESTING_FEATURE(KeyPathWithMethodMembers)
130+
UNINTERESTING_FEATURE(SendableProhibitsMainActorInference)
130131

131132
static bool usesFeatureNonescapableTypes(Decl *decl) {
132133
auto containsNonEscapable =

lib/Sema/DerivedConformance/DerivedConformanceCodable.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -155,14 +155,16 @@ addImplicitCodingKeys(NominalTypeDecl *target,
155155
enumDecl->setSynthesized();
156156
enumDecl->setAccess(AccessLevel::Private);
157157

158-
switch (C.LangOpts.DefaultIsolationBehavior) {
159-
case DefaultIsolation::MainActor:
160-
enumDecl->getAttrs().add(NonisolatedAttr::createImplicit(C));
161-
break;
162-
163-
case DefaultIsolation::Nonisolated:
164-
// Nothing to do.
165-
break;
158+
if (!C.LangOpts.hasFeature(Feature::SendableProhibitsMainActorInference)) {
159+
switch (C.LangOpts.DefaultIsolationBehavior) {
160+
case DefaultIsolation::MainActor:
161+
enumDecl->getAttrs().add(NonisolatedAttr::createImplicit(C));
162+
break;
163+
164+
case DefaultIsolation::Nonisolated:
165+
// Nothing to do.
166+
break;
167+
}
166168
}
167169

168170
// For classes which inherit from something Encodable or Decodable, we

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 76 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5956,6 +5956,65 @@ static void addAttributesForActorIsolation(ValueDecl *value,
59565956
}
59575957
}
59585958

5959+
/// Determine whether there is a SendableMetatype conformance that requires that the nominal type
5960+
/// be nonisolated (preventing @MainActor inference).
5961+
static bool sendableConformanceRequiresNonisolated(NominalTypeDecl *nominal) {
5962+
ASTContext &ctx = nominal->getASTContext();
5963+
if (!ctx.LangOpts.hasFeature(Feature::SendableProhibitsMainActorInference))
5964+
return false;
5965+
5966+
if (isa<ProtocolDecl>(nominal))
5967+
return false;
5968+
5969+
auto sendable = ctx.getProtocol(KnownProtocolKind::Sendable);
5970+
auto sendableMetatype = ctx.getProtocol(KnownProtocolKind::SendableMetatype);
5971+
if (!sendableMetatype)
5972+
return false;
5973+
5974+
// Check whether any of the explicit conformances is to a
5975+
// SendableMetatype-inheriting protocol. We exclude direct conformance to
5976+
// Sendable here, because a global-actor-isolated type is implicitly Sendable,
5977+
// and writing Sendable explicitly
5978+
InvertibleProtocolSet inverses;
5979+
bool anyObject = false;
5980+
auto inherited = getDirectlyInheritedNominalTypeDecls(
5981+
nominal, inverses, anyObject);
5982+
for (const auto &entry : inherited) {
5983+
auto proto = dyn_cast<ProtocolDecl>(entry.Item);
5984+
if (proto && proto != sendable && proto->inheritsFrom(sendableMetatype))
5985+
return true;
5986+
}
5987+
5988+
// Check for member or extension macros that define conformances to
5989+
// SendableMetatype-inheriting protocols.
5990+
bool requiresNonisolated = false;
5991+
auto checkMacro = [&](MacroRole role, MacroDecl *macro) {
5992+
if (!macro || requiresNonisolated)
5993+
return;
5994+
5995+
SmallVector<ProtocolDecl *, 2> conformances;
5996+
macro->getIntroducedConformances(nominal, role, conformances);
5997+
for (auto proto : conformances) {
5998+
if (proto == sendableMetatype || proto->inheritsFrom(sendableMetatype)) {
5999+
requiresNonisolated = true;
6000+
break;
6001+
}
6002+
}
6003+
};
6004+
6005+
nominal->forEachAttachedMacro(
6006+
MacroRole::Member,
6007+
[&](CustomAttr * attr, MacroDecl *macro) {
6008+
checkMacro(MacroRole::Member, macro);
6009+
});
6010+
nominal->forEachAttachedMacro(
6011+
MacroRole::Extension,
6012+
[&](CustomAttr * attr, MacroDecl *macro) {
6013+
checkMacro(MacroRole::Extension, macro);
6014+
});
6015+
return requiresNonisolated;
6016+
}
6017+
59596018
/// Determine the default isolation and isolation source for this declaration,
59606019
/// which may still be overridden by other inference rules.
59616020
static std::tuple<InferredActorIsolation, ValueDecl *,
@@ -5975,24 +6034,34 @@ computeDefaultInferredActorIsolation(ValueDecl *value) {
59756034
auto *dc = value->getInnermostDeclContext();
59766035
while (dc && !inActorContext) {
59776036
if (auto *nominal = dc->getSelfNominalTypeDecl()) {
5978-
inActorContext = nominal->isAnyActor();
6037+
if (nominal->isAnyActor())
6038+
return {};
59796039
}
59806040
dc = dc->getParent();
59816041
}
59826042

5983-
if (!inActorContext) {
5984-
// FIXME: deinit should be implicitly MainActor too.
5985-
if (isa<TypeDecl>(value) || isa<ExtensionDecl>(value) ||
5986-
isa<AbstractStorageDecl>(value) || isa<FuncDecl>(value) ||
5987-
isa<ConstructorDecl>(value)) {
6043+
// If this is or is a non-type member of a nominal type that conforms to a
6044+
// SendableMetatype-inheriting protocol in its primary definition, disable
6045+
// @MainActor inference.
6046+
auto nominalTypeDecl = dyn_cast<NominalTypeDecl>(value);
6047+
if (!nominalTypeDecl && !isa<TypeDecl>(value)) {
6048+
nominalTypeDecl = value->getDeclContext()->getSelfNominalTypeDecl();
6049+
}
6050+
if (nominalTypeDecl &&
6051+
sendableConformanceRequiresNonisolated(nominalTypeDecl))
6052+
return { };
6053+
6054+
// FIXME: deinit should be implicitly MainActor too.
6055+
if (isa<TypeDecl>(value) || isa<ExtensionDecl>(value) ||
6056+
isa<AbstractStorageDecl>(value) || isa<FuncDecl>(value) ||
6057+
isa<ConstructorDecl>(value)) {
59886058
// Preconcurrency here is used to stage the diagnostics
59896059
// when users select `@MainActor` default isolation with
59906060
// non-strict concurrency modes (pre Swift 6).
59916061
auto isolation =
59926062
ActorIsolation::forGlobalActor(globalActor)
59936063
.withPreconcurrency(!ctx.LangOpts.isSwiftVersionAtLeast(6));
59946064
return {{{isolation, {}}, nullptr, {}}};
5995-
}
59966065
}
59976066

59986067
return {};

0 commit comments

Comments
 (0)