Skip to content

Commit 99555bd

Browse files
authored
Merge pull request #83341 from DougGregor/default-isolation-extensions-6.2
[6.2] Extensions and members thereof can apply default isolation
2 parents 6af1ab8 + 20a10c6 commit 99555bd

File tree

6 files changed

+192
-19
lines changed

6 files changed

+192
-19
lines changed

include/swift/Basic/Features.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,10 @@ EXPERIMENTAL_FEATURE(LayoutPrespecialization, true)
332332

333333
EXPERIMENTAL_FEATURE(AccessLevelOnImport, true)
334334

335+
/// Disable the special behavior of of explicit 'nonisolated' vs. being
336+
/// implicitly nonisolated.
337+
EXPERIMENTAL_FEATURE(NoExplicitNonIsolated, true)
338+
335339
/// Enables a module to allow non resilient access from other
336340
/// modules within a package if built from source.
337341
EXPERIMENTAL_FEATURE(AllowNonResilientAccessInPackage, false)

lib/AST/FeatureSet.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ UNINTERESTING_FEATURE(MacrosOnImports)
127127
UNINTERESTING_FEATURE(ExtensibleEnums)
128128
UNINTERESTING_FEATURE(NonisolatedNonsendingByDefault)
129129
UNINTERESTING_FEATURE(KeyPathWithMethodMembers)
130+
UNINTERESTING_FEATURE(NoExplicitNonIsolated)
130131

131132
static bool usesFeatureNonescapableTypes(Decl *decl) {
132133
auto containsNonEscapable =

lib/Frontend/CompilerInvocation.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1878,8 +1878,10 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
18781878
Opts.DefaultIsolationBehavior = DefaultIsolation::Nonisolated;
18791879
}
18801880

1881-
if (Opts.DefaultIsolationBehavior == DefaultIsolation::MainActor)
1881+
if (Opts.DefaultIsolationBehavior == DefaultIsolation::MainActor) {
18821882
Opts.enableFeature(Feature::InferIsolatedConformances);
1883+
Opts.enableFeature(Feature::NoExplicitNonIsolated);
1884+
}
18831885

18841886
#if !defined(NDEBUG) && SWIFT_ENABLE_EXPERIMENTAL_PARSER_VALIDATION
18851887
/// Enable round trip parsing via the new swift parser unless it is disabled

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 104 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5301,6 +5301,33 @@ getIsolationFromAttributes(const Decl *decl, bool shouldDiagnose = true,
53015301
llvm_unreachable("Forgot about an attribute?");
53025302
}
53035303

5304+
/// Determine the default isolation for the given declaration context.
5305+
static DefaultIsolation getDefaultIsolationForContext(const DeclContext *dc) {
5306+
// Check whether there is a file-specific setting.
5307+
if (auto *sourceFile = dc->getParentSourceFile()) {
5308+
if (auto defaultIsolationInFile = sourceFile->getDefaultIsolation())
5309+
return defaultIsolationInFile.value();
5310+
}
5311+
5312+
// If we're in the main module, check the language option.
5313+
ASTContext &ctx = dc->getASTContext();
5314+
if (dc->getParentModule() == ctx.MainModule)
5315+
return ctx.LangOpts.DefaultIsolationBehavior;
5316+
5317+
// Otherwise, default to nonisolated.
5318+
return DefaultIsolation::Nonisolated;
5319+
}
5320+
5321+
/// Determines whether explicit 'nonisolated' is different from 'unspecified'
5322+
/// in the given context.
5323+
static bool explicitNonisolatedIsSpecial(const DeclContext *dc) {
5324+
ASTContext &ctx = dc->getASTContext();
5325+
if (ctx.LangOpts.hasFeature(Feature::NoExplicitNonIsolated))
5326+
return false;
5327+
5328+
return getDefaultIsolationForContext(dc) == DefaultIsolation::Nonisolated;
5329+
}
5330+
53045331
/// Infer isolation from witnessed protocol requirements.
53055332
static std::optional<InferredActorIsolation>
53065333
getIsolationFromWitnessedRequirements(ValueDecl *value) {
@@ -5317,11 +5344,13 @@ getIsolationFromWitnessedRequirements(ValueDecl *value) {
53175344
if (dc->getSelfProtocolDecl())
53185345
return std::nullopt;
53195346

5320-
// Prevent isolation inference from requirements if the conforming type
5321-
// has an explicit `nonisolated` attribute.
5322-
if (auto *NTD = dc->getSelfNominalTypeDecl()) {
5323-
if (NTD->getAttrs().hasAttribute<NonisolatedAttr>())
5324-
return std::nullopt;
5347+
if (explicitNonisolatedIsSpecial(dc)) {
5348+
// Prevent isolation inference from requirements if the conforming type
5349+
// has an explicit `nonisolated` attribute.
5350+
if (auto *NTD = dc->getSelfNominalTypeDecl()) {
5351+
if (NTD->getAttrs().hasAttribute<NonisolatedAttr>())
5352+
return std::nullopt;
5353+
}
53255354
}
53265355

53275356
// Walk through each of the conformances in this context, collecting any
@@ -5379,8 +5408,13 @@ getIsolationFromWitnessedRequirements(ValueDecl *value) {
53795408
}
53805409

53815410
case ActorIsolation::GlobalActor:
5411+
break;
5412+
53825413
case ActorIsolation::Nonisolated:
53835414
case ActorIsolation::NonisolatedUnsafe:
5415+
if (!explicitNonisolatedIsSpecial(requirement->getDeclContext()))
5416+
continue;
5417+
53845418
break;
53855419
}
53865420

@@ -5488,7 +5522,8 @@ getIsolationFromConformances(NominalTypeDecl *nominal) {
54885522
case ActorIsolation::NonisolatedUnsafe:
54895523
break;
54905524
case ActorIsolation::Nonisolated:
5491-
if (inferredIsolation.source.kind == IsolationSource::Kind::Explicit) {
5525+
if (inferredIsolation.source.kind == IsolationSource::Kind::Explicit &&
5526+
explicitNonisolatedIsSpecial(nominal)) {
54925527
if (!foundIsolation) {
54935528
// We found an explicitly 'nonisolated' protocol.
54945529
foundIsolation = {
@@ -6152,12 +6187,27 @@ computeDefaultInferredActorIsolation(ValueDecl *value) {
61526187
return {};
61536188

61546189
if (dc != dyn_cast<DeclContext>(value)) {
6190+
// If the nominal type is global-actor-isolated, there's nothing
6191+
// more to look for.
61556192
if (getActorIsolation(nominal).isMainActor())
61566193
break;
61576194

6158-
return {};
6195+
// If this is an extension of a nonisolated type, its isolation
6196+
// is independent of the type.
6197+
if (auto ext = dyn_cast<ExtensionDecl>(dc)) {
6198+
// If there were isolation attributes on the extension, respect
6199+
// them.
6200+
if (getIsolationFromAttributes(ext).has_value())
6201+
return {};
6202+
6203+
// Keep looking.
6204+
} else {
6205+
// The type is nonisolated, so its members are nonisolated.
6206+
return {};
6207+
}
61596208
}
61606209
}
6210+
61616211
dc = dc->getParent();
61626212
}
61636213

@@ -6187,12 +6237,8 @@ computeDefaultInferredActorIsolation(ValueDecl *value) {
61876237
return {};
61886238
};
61896239

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-
6240+
DefaultIsolation defaultIsolation =
6241+
getDefaultIsolationForContext(value->getDeclContext());
61966242
// If we are required to use main actor... just use that.
61976243
if (defaultIsolation == DefaultIsolation::MainActor)
61986244
if (auto result =
@@ -6269,6 +6315,36 @@ computeDefaultInferredActorIsolation(ValueDecl *value) {
62696315
return {{ActorIsolation::forUnspecified(), {}}, nullptr, {}};
62706316
}
62716317

6318+
/// Determines when the given "self" isolation should override default
6319+
/// isolation.
6320+
static bool shouldSelfIsolationOverrideDefault(
6321+
ASTContext &ctx, const DeclContext *dc,
6322+
const ActorIsolation &selfIsolation) {
6323+
switch (selfIsolation) {
6324+
case ActorIsolation::ActorInstance:
6325+
case ActorIsolation::Erased:
6326+
case ActorIsolation::GlobalActor:
6327+
// Actor isolation always overrides.
6328+
return true;
6329+
6330+
case ActorIsolation::Unspecified:
6331+
// Unspecified isolation never overrides.
6332+
return false;
6333+
6334+
case ActorIsolation::Nonisolated:
6335+
case ActorIsolation::NonisolatedUnsafe:
6336+
case ActorIsolation::CallerIsolationInheriting:
6337+
// Explicit nonisolated used to overwrite default isolation all the time,
6338+
// but under NoExplicitNonIsolated it doesn't affect extensions.
6339+
if (isa<NominalTypeDecl>(dc))
6340+
return true;
6341+
6342+
/// Only allow explicit nonisolated to override the default when it's
6343+
/// treated as special.
6344+
return explicitNonisolatedIsSpecial(dc);
6345+
}
6346+
}
6347+
62726348
static InferredActorIsolation computeActorIsolation(Evaluator &evaluator,
62736349
ValueDecl *value) {
62746350
// If this declaration has actor-isolated "self", it's isolated to that
@@ -6601,7 +6677,8 @@ static InferredActorIsolation computeActorIsolation(Evaluator &evaluator,
66016677
// has isolation, use that.
66026678
if (auto selfTypeDecl = value->getDeclContext()->getSelfNominalTypeDecl()) {
66036679
auto selfTypeIsolation = getInferredActorIsolation(selfTypeDecl);
6604-
if (selfTypeIsolation.isolation) {
6680+
if (shouldSelfIsolationOverrideDefault(
6681+
ctx, value->getDeclContext(), selfTypeIsolation.isolation)) {
66056682
auto isolation = selfTypeIsolation.isolation;
66066683

66076684
if (ctx.LangOpts.hasFeature(Feature::NonisolatedNonsendingByDefault) &&
@@ -8439,6 +8516,19 @@ ActorIsolation swift::inferConformanceIsolation(
84398516
// isolated to a global actor, we may use the conforming type's isolation.
84408517
auto nominalIsolation = getActorIsolation(nominal);
84418518
if (!nominalIsolation.isGlobalActor()) {
8519+
// If we are in an extension of the type, we might still infer an
8520+
// isolated conformance depending on default isolation and on the extension
8521+
// itself.
8522+
if (auto ext = dyn_cast<ExtensionDecl>(dc)) {
8523+
// If there's an isolation-related attribute on the extension, use it.
8524+
if (auto attrIsolation = getIsolationFromAttributes(ext))
8525+
return *attrIsolation;
8526+
8527+
// If we're defaulting to main-actor isolation, use that.
8528+
if (getDefaultIsolationForContext(dc) == DefaultIsolation::MainActor)
8529+
return ActorIsolation::forMainActor(ctx);
8530+
}
8531+
84428532
return ActorIsolation::forNonisolated(false);
84438533
}
84448534

test/Concurrency/assume_mainactor_typechecker_errors.swift

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,70 @@ func testTaskDetached() async {
7979
await k.doSomething()
8080
}
8181
}
82+
83+
// @MainActor
84+
extension Int {
85+
func memberOfInt() { } // expected-note 2{{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+
}
103+
104+
nonisolated struct MyOtherStruct { }
105+
106+
extension MyOtherStruct {
107+
func f() {
108+
17.memberOfInt() // okay, on main actor
109+
}
110+
}
111+
112+
nonisolated
113+
extension MyOtherStruct {
114+
func g() {
115+
17.memberOfInt() // expected-swift5-warning{{call to main actor-isolated instance method 'memberOfInt()' in a synchronous nonisolated context}}
116+
// expected-swift6-error@-1{{call to main actor-isolated instance method 'memberOfInt()' in a synchronous nonisolated context}}
117+
}
118+
}
119+
120+
nonisolated protocol P {
121+
func g()
122+
}
123+
124+
struct MyP: P {
125+
func g() {
126+
17.memberOfInt() // okay, on main actor
127+
}
128+
}
129+
130+
// https://github.com/swiftlang/swift/issues/82168 -
131+
nonisolated protocol OtherP {
132+
associatedtype AT
133+
static var at: AT { get }
134+
}
135+
136+
nonisolated struct KP<R: OtherP, V> {
137+
init(keyPath: KeyPath<R, V>) {}
138+
}
139+
140+
struct S: OtherP {
141+
let p: Int
142+
struct AT {
143+
let kp = KP(keyPath: \S.p)
144+
}
145+
146+
// FIXME: This should not be an error.
147+
static let at = AT() // expected-swift6-error{{'S.AT' cannot be constructed because it has no accessible initializers}}
148+
}

test/Concurrency/isolated_conformance_default_actor.swift

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
// REQUIRES: concurrency
44

5+
@MainActor func onMain() { }
6+
57
nonisolated
68
protocol P {
79
func f()
@@ -12,8 +14,14 @@ protocol Q {
1214
func g()
1315
}
1416

17+
// expected-note@+4{{turn data races into runtime errors with '@preconcurrency'}}
18+
// expected-note@+3{{isolate this conformance to the main actor with '@MainActor'}}
19+
// expected-note@+2{{mark all declarations used in the conformance 'nonisolated'}}
20+
// expected-error@+1{{conformance of 'CImplicitMainActorNonisolatedConformance' to protocol 'P' crosses into main actor-isolated code and can cause data races}}
1521
class CImplicitMainActorNonisolatedConformance: nonisolated P {
16-
func f() { } // error: explicitly nonisolated conformance
22+
func f() { // expected-note{{main actor-isolated instance method 'f()' cannot satisfy nonisolated requirement}}
23+
onMain() // okay, f is on @MainActor
24+
}
1725
}
1826

1927

@@ -73,9 +81,6 @@ func acceptSendablePMeta<T: Sendable & P>(_: T.Type) { }
7381
func acceptSendableQMeta<T: Sendable & Q>(_: T.Type) { }
7482

7583
nonisolated func testConformancesFromNonisolated() {
76-
let _: any P = CExplicitMainActor() // okay
77-
let _: any P = CImplicitMainActor() // okay
78-
7984
let _: any P = CNonIsolated()
8085
let _: any P = CImplicitMainActorNonisolatedConformance()
8186

@@ -84,6 +89,10 @@ nonisolated func testConformancesFromNonisolated() {
8489
let _: any Q = CImplicitMainActor()
8590

8691
// Error, these are main-actor-isolated conformances
92+
let _: any P = CExplicitMainActor() // expected-error{{main actor-isolated conformance of 'CExplicitMainActor' to 'P' cannot be used in nonisolated context}}
93+
let _: any P = CImplicitMainActor() // expected-error{{main actor-isolated conformance of 'CImplicitMainActor' to 'P' cannot be used in nonisolated context}}
94+
95+
8796
let _: any Equatable.Type = EquatableStruct.self // expected-error{{main actor-isolated conformance of 'EquatableStruct' to 'Equatable' cannot be used in nonisolated context}}
8897
let _: any Hashable.Type = HashableStruct.self // expected-error{{main actor-isolated conformance of 'HashableStruct' to 'Hashable' cannot be used in nonisolated context}}
8998
let _: any RawRepresentable.Type = RawRepresentableEnum.self

0 commit comments

Comments
 (0)