mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[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
This commit is contained in:
@@ -532,6 +532,10 @@ EXPERIMENTAL_FEATURE(DefaultIsolationPerFile, false)
|
||||
/// Enable @_lifetime attribute
|
||||
SUPPRESSIBLE_EXPERIMENTAL_FEATURE(Lifetimes, true)
|
||||
|
||||
/// Disable @MainActor inference when the primary definition of a type conforms
|
||||
/// to SendableMetatype (or Sendable).
|
||||
EXPERIMENTAL_FEATURE(SendableProhibitsMainActorInference, true)
|
||||
|
||||
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
|
||||
#undef EXPERIMENTAL_FEATURE
|
||||
#undef UPCOMING_FEATURE
|
||||
|
||||
@@ -125,6 +125,7 @@ UNINTERESTING_FEATURE(StructLetDestructuring)
|
||||
UNINTERESTING_FEATURE(MacrosOnImports)
|
||||
UNINTERESTING_FEATURE(NonisolatedNonsendingByDefault)
|
||||
UNINTERESTING_FEATURE(KeyPathWithMethodMembers)
|
||||
UNINTERESTING_FEATURE(SendableProhibitsMainActorInference)
|
||||
|
||||
// TODO: Return true for inlinable function bodies with module selectors in them
|
||||
UNINTERESTING_FEATURE(ModuleSelector)
|
||||
|
||||
@@ -155,6 +155,7 @@ addImplicitCodingKeys(NominalTypeDecl *target,
|
||||
enumDecl->setSynthesized();
|
||||
enumDecl->setAccess(AccessLevel::Private);
|
||||
|
||||
if (!C.LangOpts.hasFeature(Feature::SendableProhibitsMainActorInference)) {
|
||||
switch (C.LangOpts.DefaultIsolationBehavior) {
|
||||
case DefaultIsolation::MainActor:
|
||||
enumDecl->getAttrs().add(NonisolatedAttr::createImplicit(C));
|
||||
@@ -164,6 +165,7 @@ addImplicitCodingKeys(NominalTypeDecl *target,
|
||||
// Nothing to do.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// For classes which inherit from something Encodable or Decodable, we
|
||||
// provide case `super` as the first key (to be used in encoding super).
|
||||
|
||||
@@ -5936,6 +5936,65 @@ static void addAttributesForActorIsolation(ValueDecl *value,
|
||||
}
|
||||
}
|
||||
|
||||
/// Determine whether there is a SendableMetatype conformance that requires that the nominal type
|
||||
/// be nonisolated (preventing @MainActor inference).
|
||||
static bool sendableConformanceRequiresNonisolated(NominalTypeDecl *nominal) {
|
||||
ASTContext &ctx = nominal->getASTContext();
|
||||
if (!ctx.LangOpts.hasFeature(Feature::SendableProhibitsMainActorInference))
|
||||
return false;
|
||||
|
||||
if (isa<ProtocolDecl>(nominal))
|
||||
return false;
|
||||
|
||||
auto sendable = ctx.getProtocol(KnownProtocolKind::Sendable);
|
||||
auto sendableMetatype = ctx.getProtocol(KnownProtocolKind::SendableMetatype);
|
||||
if (!sendableMetatype)
|
||||
return false;
|
||||
|
||||
// Check whether any of the explicit conformances is to a
|
||||
// SendableMetatype-inheriting protocol. We exclude direct conformance to
|
||||
// Sendable here, because a global-actor-isolated type is implicitly Sendable,
|
||||
// and writing Sendable explicitly
|
||||
InvertibleProtocolSet inverses;
|
||||
bool anyObject = false;
|
||||
auto inherited = getDirectlyInheritedNominalTypeDecls(
|
||||
nominal, inverses, anyObject);
|
||||
for (const auto &entry : inherited) {
|
||||
auto proto = dyn_cast<ProtocolDecl>(entry.Item);
|
||||
if (proto && proto != sendable && proto->inheritsFrom(sendableMetatype))
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for member or extension macros that define conformances to
|
||||
// SendableMetatype-inheriting protocols.
|
||||
bool requiresNonisolated = false;
|
||||
auto checkMacro = [&](MacroRole role, MacroDecl *macro) {
|
||||
if (!macro || requiresNonisolated)
|
||||
return;
|
||||
|
||||
SmallVector<ProtocolDecl *, 2> conformances;
|
||||
macro->getIntroducedConformances(nominal, role, conformances);
|
||||
for (auto proto : conformances) {
|
||||
if (proto == sendableMetatype || proto->inheritsFrom(sendableMetatype)) {
|
||||
requiresNonisolated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
nominal->forEachAttachedMacro(
|
||||
MacroRole::Member,
|
||||
[&](CustomAttr * attr, MacroDecl *macro) {
|
||||
checkMacro(MacroRole::Member, macro);
|
||||
});
|
||||
nominal->forEachAttachedMacro(
|
||||
MacroRole::Extension,
|
||||
[&](CustomAttr * attr, MacroDecl *macro) {
|
||||
checkMacro(MacroRole::Extension, macro);
|
||||
});
|
||||
return requiresNonisolated;
|
||||
}
|
||||
|
||||
/// Determine the default isolation and isolation source for this declaration,
|
||||
/// which may still be overridden by other inference rules.
|
||||
static std::tuple<InferredActorIsolation, ValueDecl *,
|
||||
@@ -5955,12 +6014,23 @@ computeDefaultInferredActorIsolation(ValueDecl *value) {
|
||||
auto *dc = value->getInnermostDeclContext();
|
||||
while (dc && !inActorContext) {
|
||||
if (auto *nominal = dc->getSelfNominalTypeDecl()) {
|
||||
inActorContext = nominal->isAnyActor();
|
||||
if (nominal->isAnyActor())
|
||||
return {};
|
||||
}
|
||||
dc = dc->getParent();
|
||||
}
|
||||
|
||||
if (!inActorContext) {
|
||||
// If this is or is a non-type member of a nominal type that conforms to a
|
||||
// SendableMetatype-inheriting protocol in its primary definition, disable
|
||||
// @MainActor inference.
|
||||
auto nominalTypeDecl = dyn_cast<NominalTypeDecl>(value);
|
||||
if (!nominalTypeDecl && !isa<TypeDecl>(value)) {
|
||||
nominalTypeDecl = value->getDeclContext()->getSelfNominalTypeDecl();
|
||||
}
|
||||
if (nominalTypeDecl &&
|
||||
sendableConformanceRequiresNonisolated(nominalTypeDecl))
|
||||
return { };
|
||||
|
||||
// FIXME: deinit should be implicitly MainActor too.
|
||||
if (isa<TypeDecl>(value) || isa<ExtensionDecl>(value) ||
|
||||
isa<AbstractStorageDecl>(value) || isa<FuncDecl>(value) ||
|
||||
@@ -5973,7 +6043,6 @@ computeDefaultInferredActorIsolation(ValueDecl *value) {
|
||||
.withPreconcurrency(!ctx.LangOpts.isSwiftVersionAtLeast(6));
|
||||
return {{{isolation, {}}, nullptr, {}}};
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user