[SE-0466] Under main actor default isolation, explicit nonisolated is not special

Given an explicitly-nonisolated type such as

    nonisolated struct S { }

all extensions of S were also being treated as nonisolated. This meant
that being implicitly nonisolated (i.e., when you're using nonisolated
default isolation) was different from explicitly-writing nonisolated,
which is unfortunate and confusing. Align the rules, such that an
extension of S will get default isolation:

    extension S {
      func f() { } // @MainActor if we're in main actor default isolation
    }
This commit is contained in:
Doug Gregor
2025-07-24 22:52:15 -07:00
parent 4ffe06ed7c
commit 5abbf2e4c9
5 changed files with 60 additions and 3 deletions

View File

@@ -127,6 +127,7 @@ UNINTERESTING_FEATURE(MacrosOnImports)
UNINTERESTING_FEATURE(ExtensibleEnums)
UNINTERESTING_FEATURE(NonisolatedNonsendingByDefault)
UNINTERESTING_FEATURE(KeyPathWithMethodMembers)
UNINTERESTING_FEATURE(NoExplicitNonIsolated)
static bool usesFeatureNonescapableTypes(Decl *decl) {
auto containsNonEscapable =

View File

@@ -1878,8 +1878,10 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
Opts.DefaultIsolationBehavior = DefaultIsolation::Nonisolated;
}
if (Opts.DefaultIsolationBehavior == DefaultIsolation::MainActor)
if (Opts.DefaultIsolationBehavior == DefaultIsolation::MainActor) {
Opts.enableFeature(Feature::InferIsolatedConformances);
Opts.enableFeature(Feature::NoExplicitNonIsolated);
}
#if !defined(NDEBUG) && SWIFT_ENABLE_EXPERIMENTAL_PARSER_VALIDATION
/// Enable round trip parsing via the new swift parser unless it is disabled

View File

@@ -6297,6 +6297,39 @@ computeDefaultInferredActorIsolation(ValueDecl *value) {
return {{ActorIsolation::forUnspecified(), {}}, nullptr, {}};
}
/// Determines when the given "self" isolation should override default
/// isolation.
static bool shouldSelfIsolationOverrideDefault(
ASTContext &ctx, const DeclContext *dc,
const ActorIsolation &selfIsolation) {
switch (selfIsolation) {
case ActorIsolation::ActorInstance:
case ActorIsolation::Erased:
case ActorIsolation::GlobalActor:
// Actor isolation always overrides.
return true;
case ActorIsolation::Unspecified:
// Unspecified isolation never overrides.
return false;
case ActorIsolation::Nonisolated:
case ActorIsolation::NonisolatedUnsafe:
case ActorIsolation::CallerIsolationInheriting:
// Explicit nonisolated used to overwrite default isolation all the time,
// but under NoExplicitNonIsolated it doesn't affect extensions.
if (isa<NominalTypeDecl>(dc))
return true;
// The NoExplicitNonIsolated feature
if (ctx.LangOpts.hasFeature(Feature::NoExplicitNonIsolated))
return false;
// Suppress when the default isolation is nonisolated.
return getDefaultIsolationForContext(dc) == DefaultIsolation::Nonisolated;
}
}
static InferredActorIsolation computeActorIsolation(Evaluator &evaluator,
ValueDecl *value) {
// If this declaration has actor-isolated "self", it's isolated to that
@@ -6629,7 +6662,8 @@ static InferredActorIsolation computeActorIsolation(Evaluator &evaluator,
// has isolation, use that.
if (auto selfTypeDecl = value->getDeclContext()->getSelfNominalTypeDecl()) {
auto selfTypeIsolation = getInferredActorIsolation(selfTypeDecl);
if (selfTypeIsolation.isolation) {
if (shouldSelfIsolationOverrideDefault(
ctx, value->getDeclContext(), selfTypeIsolation.isolation)) {
auto isolation = selfTypeIsolation.isolation;
if (ctx.LangOpts.hasFeature(Feature::NonisolatedNonsendingByDefault) &&