Skip to content

Commit e65eecb

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 0334abd commit e65eecb

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
@@ -6088,6 +6088,23 @@ static bool sendableConformanceRequiresNonisolated(NominalTypeDecl *nominal) {
60886088
return requiresNonisolated;
60896089
}
60906090

6091+
/// Determine the default isolation for the given declaration context.
6092+
static DefaultIsolation getDefaultIsolationForContext(const DeclContext *dc) {
6093+
// Check whether there is a file-specific setting.
6094+
if (auto *sourceFile = dc->getParentSourceFile()) {
6095+
if (auto defaultIsolationInFile = sourceFile->getDefaultIsolation())
6096+
return defaultIsolationInFile.value();
6097+
}
6098+
6099+
// If we're in the main module, check the language option.
6100+
ASTContext &ctx = dc->getASTContext();
6101+
if (dc->getParentModule() == ctx.MainModule)
6102+
return ctx.LangOpts.DefaultIsolationBehavior;
6103+
6104+
// Otherwise, default to nonisolated.
6105+
return DefaultIsolation::Nonisolated;
6106+
}
6107+
60916108
/// Determine the default isolation and isolation source for this declaration,
60926109
/// which may still be overridden by other inference rules.
60936110
static std::tuple<InferredActorIsolation, ValueDecl *,
@@ -6131,12 +6148,27 @@ computeDefaultInferredActorIsolation(ValueDecl *value) {
61316148
return {};
61326149

61336150
if (dc != dyn_cast<DeclContext>(value)) {
6151+
// If the nominal type is global-actor-isolated, there's nothing
6152+
// more to look for.
61346153
if (getActorIsolation(nominal).isMainActor())
61356154
break;
61366155

6137-
return {};
6156+
// If this is an extension of a nonisolated type, its isolation
6157+
// is independent of the type.
6158+
if (auto ext = dyn_cast<ExtensionDecl>(dc)) {
6159+
// If there were isolation attributes on the extension, respect
6160+
// them.
6161+
if (getIsolationFromAttributes(ext).has_value())
6162+
return {};
6163+
6164+
// Keep looking.
6165+
} else {
6166+
// The type is nonisolated, so its members are nonisolated.
6167+
return {};
6168+
}
61386169
}
61396170
}
6171+
61406172
dc = dc->getParent();
61416173
}
61426174

@@ -6166,12 +6198,8 @@ computeDefaultInferredActorIsolation(ValueDecl *value) {
61666198
return {};
61676199
};
61686200

6169-
DefaultIsolation defaultIsolation = ctx.LangOpts.DefaultIsolationBehavior;
6170-
if (auto *SF = value->getDeclContext()->getParentSourceFile()) {
6171-
if (auto defaultIsolationInFile = SF->getDefaultIsolation())
6172-
defaultIsolation = defaultIsolationInFile.value();
6173-
}
6174-
6201+
DefaultIsolation defaultIsolation =
6202+
getDefaultIsolationForContext(value->getDeclContext());
61756203
// If we are required to use main actor... just use that.
61766204
if (defaultIsolation == DefaultIsolation::MainActor)
61776205
if (auto result =
@@ -8426,6 +8454,19 @@ ActorIsolation swift::inferConformanceIsolation(
84268454
// isolated to a global actor, we may use the conforming type's isolation.
84278455
auto nominalIsolation = getActorIsolation(nominal);
84288456
if (!nominalIsolation.isGlobalActor()) {
8457+
// If we are in an extension of the type, we might still infer an
8458+
// isolated conformance depending on default isolation and on the extension
8459+
// itself.
8460+
if (auto ext = dyn_cast<ExtensionDecl>(dc)) {
8461+
// If there's an isolation-related attribute on the extension, use it.
8462+
if (auto attrIsolation = getIsolationFromAttributes(ext))
8463+
return *attrIsolation;
8464+
8465+
// If we're defaulting to main-actor isolation, use that.
8466+
if (getDefaultIsolationForContext(dc) == DefaultIsolation::MainActor)
8467+
return ActorIsolation::forMainActor(ctx);
8468+
}
8469+
84298470
return ActorIsolation::forNonisolated(false);
84308471
}
84318472

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)