Skip to content

Commit 4ffe06e

Browse files
committed
[SE-0466] Extensions and members thereof can apply default isolation
Extensions of nonisolated types can get default isolation from context (e.g., @mainactor) independently. This was previously true for members, but not conformances, and recently regressed for members. Fix the logic to consistently allow such extensions to be default main-actor isolated, which affects both their members and conformances. Fixes rdar://156644976.
1 parent 155d7fb commit 4ffe06e

File tree

2 files changed

+69
-7
lines changed

2 files changed

+69
-7
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6109,6 +6109,23 @@ static bool sendableConformanceRequiresNonisolated(NominalTypeDecl *nominal) {
61096109
return requiresNonisolated;
61106110
}
61116111

6112+
/// Determine the default isolation for the given declaration context.
6113+
static DefaultIsolation getDefaultIsolationForContext(const DeclContext *dc) {
6114+
// Check whether there is a file-specific setting.
6115+
if (auto *sourceFile = dc->getParentSourceFile()) {
6116+
if (auto defaultIsolationInFile = sourceFile->getDefaultIsolation())
6117+
return defaultIsolationInFile.value();
6118+
}
6119+
6120+
// If we're in the main module, check the language option.
6121+
ASTContext &ctx = dc->getASTContext();
6122+
if (dc->getParentModule() == ctx.MainModule)
6123+
return ctx.LangOpts.DefaultIsolationBehavior;
6124+
6125+
// Otherwise, default to nonisolated.
6126+
return DefaultIsolation::Nonisolated;
6127+
}
6128+
61126129
/// Determine the default isolation and isolation source for this declaration,
61136130
/// which may still be overridden by other inference rules.
61146131
static std::tuple<InferredActorIsolation, ValueDecl *,
@@ -6152,12 +6169,27 @@ computeDefaultInferredActorIsolation(ValueDecl *value) {
61526169
return {};
61536170

61546171
if (dc != dyn_cast<DeclContext>(value)) {
6172+
// If the nominal type is global-actor-isolated, there's nothing
6173+
// more to look for.
61556174
if (getActorIsolation(nominal).isMainActor())
61566175
break;
61576176

6158-
return {};
6177+
// If this is an extension of a nonisolated type, its isolation
6178+
// is independent of the type.
6179+
if (auto ext = dyn_cast<ExtensionDecl>(dc)) {
6180+
// If there were isolation attributes on the extension, respect
6181+
// them.
6182+
if (getIsolationFromAttributes(ext).has_value())
6183+
return {};
6184+
6185+
// Keep looking.
6186+
} else {
6187+
// The type is nonisolated, so its members are nonisolated.
6188+
return {};
6189+
}
61596190
}
61606191
}
6192+
61616193
dc = dc->getParent();
61626194
}
61636195

@@ -6187,12 +6219,8 @@ computeDefaultInferredActorIsolation(ValueDecl *value) {
61876219
return {};
61886220
};
61896221

6190-
DefaultIsolation defaultIsolation = ctx.LangOpts.DefaultIsolationBehavior;
6191-
if (auto *SF = value->getDeclContext()->getParentSourceFile()) {
6192-
if (auto defaultIsolationInFile = SF->getDefaultIsolation())
6193-
defaultIsolation = defaultIsolationInFile.value();
6194-
}
6195-
6222+
DefaultIsolation defaultIsolation =
6223+
getDefaultIsolationForContext(value->getDeclContext());
61966224
// If we are required to use main actor... just use that.
61976225
if (defaultIsolation == DefaultIsolation::MainActor)
61986226
if (auto result =
@@ -8439,6 +8467,19 @@ ActorIsolation swift::inferConformanceIsolation(
84398467
// isolated to a global actor, we may use the conforming type's isolation.
84408468
auto nominalIsolation = getActorIsolation(nominal);
84418469
if (!nominalIsolation.isGlobalActor()) {
8470+
// If we are in an extension of the type, we might still infer an
8471+
// isolated conformance depending on default isolation and on the extension
8472+
// itself.
8473+
if (auto ext = dyn_cast<ExtensionDecl>(dc)) {
8474+
// If there's an isolation-related attribute on the extension, use it.
8475+
if (auto attrIsolation = getIsolationFromAttributes(ext))
8476+
return *attrIsolation;
8477+
8478+
// If we're defaulting to main-actor isolation, use that.
8479+
if (getDefaultIsolationForContext(dc) == DefaultIsolation::MainActor)
8480+
return ActorIsolation::forMainActor(ctx);
8481+
}
8482+
84428483
return ActorIsolation::forNonisolated(false);
84438484
}
84448485

test/Concurrency/assume_mainactor_typechecker_errors.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,24 @@ func testTaskDetached() async {
7979
await k.doSomething()
8080
}
8181
}
82+
83+
// @MainActor
84+
extension Int {
85+
func memberOfInt() { } // expected-note{{calls to instance method 'memberOfInt()' from outside of its actor context are implicitly asynchronous}}
86+
}
87+
88+
nonisolated func testMemberOfInt(i: Int) {
89+
i.memberOfInt() // expected-swift5-warning{{call to main actor-isolated instance method 'memberOfInt()' in a synchronous nonisolated context}}
90+
// expected-swift6-error@-1{{call to main actor-isolated instance method 'memberOfInt()' in a synchronous nonisolated context}}
91+
}
92+
93+
protocol SendableProto: Sendable { }
94+
95+
struct MyStruct: SendableProto { }
96+
97+
extension MyStruct: CustomStringConvertible {
98+
var description: String {
99+
17.memberOfInt() // okay, on main actor
100+
return "hello"
101+
}
102+
}

0 commit comments

Comments
 (0)