Skip to content

Commit b75d8f0

Browse files
authored
Merge pull request #81468 from DougGregor/no-main-actor-when-conforming-to-sendable
[SE-0466] Don't infer @mainactor on types conforming to Sendable
2 parents 01aa746 + ee9f6f8 commit b75d8f0

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
@@ -532,6 +532,10 @@ EXPERIMENTAL_FEATURE(DefaultIsolationPerFile, false)
532532
/// Enable @_lifetime attribute
533533
SUPPRESSIBLE_EXPERIMENTAL_FEATURE(Lifetimes, true)
534534

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

lib/AST/FeatureSet.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ UNINTERESTING_FEATURE(StructLetDestructuring)
125125
UNINTERESTING_FEATURE(MacrosOnImports)
126126
UNINTERESTING_FEATURE(NonisolatedNonsendingByDefault)
127127
UNINTERESTING_FEATURE(KeyPathWithMethodMembers)
128+
UNINTERESTING_FEATURE(SendableProhibitsMainActorInference)
128129

129130
// TODO: Return true for inlinable function bodies with module selectors in them
130131
UNINTERESTING_FEATURE(ModuleSelector)

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
@@ -5936,6 +5936,65 @@ static void addAttributesForActorIsolation(ValueDecl *value,
59365936
}
59375937
}
59385938

5939+
/// Determine whether there is a SendableMetatype conformance that requires that the nominal type
5940+
/// be nonisolated (preventing @MainActor inference).
5941+
static bool sendableConformanceRequiresNonisolated(NominalTypeDecl *nominal) {
5942+
ASTContext &ctx = nominal->getASTContext();
5943+
if (!ctx.LangOpts.hasFeature(Feature::SendableProhibitsMainActorInference))
5944+
return false;
5945+
5946+
if (isa<ProtocolDecl>(nominal))
5947+
return false;
5948+
5949+
auto sendable = ctx.getProtocol(KnownProtocolKind::Sendable);
5950+
auto sendableMetatype = ctx.getProtocol(KnownProtocolKind::SendableMetatype);
5951+
if (!sendableMetatype)
5952+
return false;
5953+
5954+
// Check whether any of the explicit conformances is to a
5955+
// SendableMetatype-inheriting protocol. We exclude direct conformance to
5956+
// Sendable here, because a global-actor-isolated type is implicitly Sendable,
5957+
// and writing Sendable explicitly
5958+
InvertibleProtocolSet inverses;
5959+
bool anyObject = false;
5960+
auto inherited = getDirectlyInheritedNominalTypeDecls(
5961+
nominal, inverses, anyObject);
5962+
for (const auto &entry : inherited) {
5963+
auto proto = dyn_cast<ProtocolDecl>(entry.Item);
5964+
if (proto && proto != sendable && proto->inheritsFrom(sendableMetatype))
5965+
return true;
5966+
}
5967+
5968+
// Check for member or extension macros that define conformances to
5969+
// SendableMetatype-inheriting protocols.
5970+
bool requiresNonisolated = false;
5971+
auto checkMacro = [&](MacroRole role, MacroDecl *macro) {
5972+
if (!macro || requiresNonisolated)
5973+
return;
5974+
5975+
SmallVector<ProtocolDecl *, 2> conformances;
5976+
macro->getIntroducedConformances(nominal, role, conformances);
5977+
for (auto proto : conformances) {
5978+
if (proto == sendableMetatype || proto->inheritsFrom(sendableMetatype)) {
5979+
requiresNonisolated = true;
5980+
break;
5981+
}
5982+
}
5983+
};
5984+
5985+
nominal->forEachAttachedMacro(
5986+
MacroRole::Member,
5987+
[&](CustomAttr * attr, MacroDecl *macro) {
5988+
checkMacro(MacroRole::Member, macro);
5989+
});
5990+
nominal->forEachAttachedMacro(
5991+
MacroRole::Extension,
5992+
[&](CustomAttr * attr, MacroDecl *macro) {
5993+
checkMacro(MacroRole::Extension, macro);
5994+
});
5995+
return requiresNonisolated;
5996+
}
5997+
59395998
/// Determine the default isolation and isolation source for this declaration,
59405999
/// which may still be overridden by other inference rules.
59416000
static std::tuple<InferredActorIsolation, ValueDecl *,
@@ -5955,24 +6014,34 @@ computeDefaultInferredActorIsolation(ValueDecl *value) {
59556014
auto *dc = value->getInnermostDeclContext();
59566015
while (dc && !inActorContext) {
59576016
if (auto *nominal = dc->getSelfNominalTypeDecl()) {
5958-
inActorContext = nominal->isAnyActor();
6017+
if (nominal->isAnyActor())
6018+
return {};
59596019
}
59606020
dc = dc->getParent();
59616021
}
59626022

5963-
if (!inActorContext) {
5964-
// FIXME: deinit should be implicitly MainActor too.
5965-
if (isa<TypeDecl>(value) || isa<ExtensionDecl>(value) ||
5966-
isa<AbstractStorageDecl>(value) || isa<FuncDecl>(value) ||
5967-
isa<ConstructorDecl>(value)) {
6023+
// If this is or is a non-type member of a nominal type that conforms to a
6024+
// SendableMetatype-inheriting protocol in its primary definition, disable
6025+
// @MainActor inference.
6026+
auto nominalTypeDecl = dyn_cast<NominalTypeDecl>(value);
6027+
if (!nominalTypeDecl && !isa<TypeDecl>(value)) {
6028+
nominalTypeDecl = value->getDeclContext()->getSelfNominalTypeDecl();
6029+
}
6030+
if (nominalTypeDecl &&
6031+
sendableConformanceRequiresNonisolated(nominalTypeDecl))
6032+
return { };
6033+
6034+
// FIXME: deinit should be implicitly MainActor too.
6035+
if (isa<TypeDecl>(value) || isa<ExtensionDecl>(value) ||
6036+
isa<AbstractStorageDecl>(value) || isa<FuncDecl>(value) ||
6037+
isa<ConstructorDecl>(value)) {
59686038
// Preconcurrency here is used to stage the diagnostics
59696039
// when users select `@MainActor` default isolation with
59706040
// non-strict concurrency modes (pre Swift 6).
59716041
auto isolation =
59726042
ActorIsolation::forGlobalActor(globalActor)
59736043
.withPreconcurrency(!ctx.LangOpts.isSwiftVersionAtLeast(6));
59746044
return {{{isolation, {}}, nullptr, {}}};
5975-
}
59766045
}
59776046

59786047
return {};

0 commit comments

Comments
 (0)