Skip to content

Commit ad394f8

Browse files
committed
[Concurrency] Implement an experimental feature for setting default
actor isolation per file using a typealias. This feature is gated behind the `DefaultIsolationTypealias` experimental feature flag.
1 parent 7ae040e commit ad394f8

File tree

11 files changed

+182
-11
lines changed

11 files changed

+182
-11
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5993,6 +5993,13 @@ ERROR(global_actor_not_usable_from_inline,none,
59935993
NOTE(move_global_actor_attr_to_storage_decl,none,
59945994
"move global actor attribute to %kind0", (const ValueDecl *))
59955995

5996+
ERROR(default_isolation_internal,none,
5997+
"default isolation can only be set per file",
5998+
())
5999+
ERROR(default_isolation_custom,none,
6000+
"default isolation can only be set to 'MainActor' or 'nonisolated'",
6001+
())
6002+
59966003
ERROR(actor_isolation_multiple_attr_2,none,
59976004
"%kind0 has multiple actor-isolation attributes ('%1' and '%2')",
59986005
(const Decl *, StringRef, StringRef))

include/swift/AST/KnownIdentifiers.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ IDENTIFIER(decodeIfPresent)
7474
IDENTIFIER(Decoder)
7575
IDENTIFIER(decoder)
7676
IDENTIFIER(DefaultDistributedActorSystem)
77+
IDENTIFIER(DefaultIsolation)
7778
IDENTIFIER_(Differentiation)
7879
IDENTIFIER_WITH_NAME(PatternMatchVar, "$match")
7980
IDENTIFIER(dynamicallyCall)

include/swift/AST/KnownSDKTypes.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ KNOWN_SDK_TYPE_DECL(ObjectiveC, ObjCBool, StructDecl, 0)
4040
KNOWN_SDK_TYPE_DECL(Concurrency, CheckedContinuation, NominalTypeDecl, 2)
4141
KNOWN_SDK_TYPE_DECL(Concurrency, UnsafeContinuation, NominalTypeDecl, 2)
4242
KNOWN_SDK_TYPE_DECL(Concurrency, MainActor, NominalTypeDecl, 0)
43+
KNOWN_SDK_TYPE_DECL(Concurrency, nonisolated, NominalTypeDecl, 0)
4344
KNOWN_SDK_TYPE_DECL(Concurrency, Job, StructDecl, 0) // legacy type; prefer ExecutorJob
4445
KNOWN_SDK_TYPE_DECL(Concurrency, ExecutorJob, StructDecl, 0)
4546
KNOWN_SDK_TYPE_DECL(Concurrency, UnownedJob, StructDecl, 0)

include/swift/AST/TypeCheckRequests.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "swift/AST/SourceFile.h"
3434
#include "swift/AST/Type.h"
3535
#include "swift/AST/TypeResolutionStage.h"
36+
#include "swift/Basic/LangOptions.h"
3637
#include "swift/Basic/Statistic.h"
3738
#include "swift/Basic/TaggedUnion.h"
3839
#include "swift/Basic/TypeID.h"
@@ -1596,6 +1597,25 @@ class ActorIsolationRequest :
15961597
bool isCached() const { return true; }
15971598
};
15981599

1600+
/// Determine the default actor isolation for the given source file.
1601+
class DefaultActorIsolationRequest :
1602+
public SimpleRequest<DefaultActorIsolationRequest,
1603+
std::optional<DefaultIsolation>(SourceFile *),
1604+
RequestFlags::Cached> {
1605+
public:
1606+
using SimpleRequest::SimpleRequest;
1607+
1608+
private:
1609+
friend SimpleRequest;
1610+
1611+
std::optional<DefaultIsolation>
1612+
evaluate(Evaluator &evaluator, SourceFile *file) const;
1613+
1614+
public:
1615+
// Caching
1616+
bool isCached() const { return true; }
1617+
};
1618+
15991619
/// Determine whether the given function should have an isolated 'self'.
16001620
class HasIsolatedSelfRequest :
16011621
public SimpleRequest<HasIsolatedSelfRequest,

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,9 @@ SWIFT_REQUEST(TypeChecker, GlobalActorAttributeRequest,
182182
SWIFT_REQUEST(TypeChecker, ActorIsolationRequest,
183183
ActorIsolationState(ValueDecl *),
184184
Cached, NoLocationInfo)
185+
SWIFT_REQUEST(TypeChecker, DefaultActorIsolationRequest,
186+
std::optional<DefaultIsolation>(SourceFile *),
187+
Cached, NoLocationInfo)
185188
SWIFT_REQUEST(TypeChecker, HasIsolatedSelfRequest,
186189
bool(ValueDecl *),
187190
Uncached, NoLocationInfo)

include/swift/Basic/Features.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,9 @@ EXPERIMENTAL_FEATURE(InferIsolatedConformances, true)
505505
/// Allow SwiftSettings
506506
EXPERIMENTAL_FEATURE(SwiftSettings, false)
507507

508+
/// Enable setting default isolation per file via typealias.
509+
EXPERIMENTAL_FEATURE(DefaultIsolationTypealias, true)
510+
508511
/// Syntax sugar features for concurrency.
509512
EXPERIMENTAL_FEATURE(ConcurrencySyntaxSugar, true)
510513

lib/AST/FeatureSet.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,7 @@ UNINTERESTING_FEATURE(SafeInteropWrappers)
404404
UNINTERESTING_FEATURE(AssumeResilientCxxTypes)
405405
UNINTERESTING_FEATURE(ImportNonPublicCxxMembers)
406406
UNINTERESTING_FEATURE(CoroutineAccessorsUnwindOnCallerError)
407+
UNINTERESTING_FEATURE(DefaultIsolationTypealias)
407408

408409
static bool usesFeatureSwiftSettings(const Decl *decl) {
409410
// We just need to guard `#SwiftSettings`.

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 73 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5746,6 +5746,73 @@ static void addAttributesForActorIsolation(ValueDecl *value,
57465746
}
57475747
}
57485748

5749+
std::optional<DefaultIsolation>
5750+
DefaultActorIsolationRequest::evaluate(Evaluator &evaluator,
5751+
SourceFile *sourceFile) const {
5752+
auto &ctx = sourceFile->getASTContext();
5753+
5754+
auto defaultModuleIsolaton = ctx.LangOpts.DefaultIsolationBehavior;
5755+
auto mainActorType = ctx.getMainActorType();
5756+
if (!mainActorType)
5757+
return defaultModuleIsolaton;
5758+
5759+
if (ctx.LangOpts.hasFeature(Feature::SwiftSettings)) {
5760+
auto options = sourceFile->getLanguageOptions();
5761+
if (auto isolation = options.defaultIsolation) {
5762+
return *isolation;
5763+
}
5764+
}
5765+
5766+
if (ctx.LangOpts.hasFeature(Feature::DefaultIsolationTypealias)) {
5767+
auto nonisolatedType = ctx.getnonisolatedType();
5768+
5769+
auto decls = sourceFile->getTopLevelDecls();
5770+
if (decls.empty())
5771+
return defaultModuleIsolaton;
5772+
5773+
auto locInFile = decls.front()->getStartLoc();
5774+
auto defaultIsolationResult = TypeChecker::lookupUnqualified(
5775+
sourceFile->getModuleScopeContext(),
5776+
DeclNameRef(ctx.Id_DefaultIsolation),
5777+
locInFile);
5778+
for (auto found : defaultIsolationResult) {
5779+
auto *decl = found.getValueDecl();
5780+
if (!decl)
5781+
continue;
5782+
5783+
auto *typealias = dyn_cast<TypeAliasDecl>(decl);
5784+
if (!typealias ||
5785+
!typealias->getDeclContext()->isModuleScopeContext())
5786+
continue;
5787+
5788+
// We have a top-level 'DefaultIsolation' typealias. We can assume
5789+
// it's the only one, because multiple top-level typealiases with
5790+
// the same name is a redeclaration error, and default isolation
5791+
// cannot be set by an imported library.
5792+
5793+
// The typealias can only be used to set default isolation per file.
5794+
if (typealias->getFormalAccess() >= AccessLevel::Internal) {
5795+
typealias->diagnose(diag::default_isolation_internal);
5796+
break;
5797+
}
5798+
5799+
auto type = typealias->getUnderlyingType();
5800+
if (type->isEqual(mainActorType))
5801+
return DefaultIsolation::MainActor;
5802+
5803+
if (type->isEqual(nonisolatedType))
5804+
return DefaultIsolation::Nonisolated;
5805+
5806+
// The underlying type of the typealias must either be 'MainActor'
5807+
// or 'nonisolated'.
5808+
typealias->diagnose(diag::default_isolation_custom);
5809+
break;
5810+
}
5811+
}
5812+
5813+
return defaultModuleIsolaton;
5814+
}
5815+
57495816
/// Determine the default isolation and isolation source for this declaration,
57505817
/// which may still be overridden by other inference rules.
57515818
static std::tuple<InferredActorIsolation, ValueDecl *,
@@ -5755,17 +5822,13 @@ computeDefaultInferredActorIsolation(ValueDecl *value) {
57555822

57565823
// Determine whether default isolation is set to MainActor, either for
57575824
// the entire module or in this specific file.
5758-
if (value->getModuleContext() == ctx.MainModule) {
5825+
auto mainActorType = ctx.getMainActorType();
5826+
if (mainActorType && value->getModuleContext() == ctx.MainModule) {
57595827
// See if we have one specified by our file unit.
5760-
auto defaultIsolation = ctx.LangOpts.DefaultIsolationBehavior;
5761-
if (ctx.LangOpts.hasFeature(Feature::SwiftSettings)) {
5762-
if (auto *sourceFile = value->getDeclContext()->getParentSourceFile()) {
5763-
auto options = sourceFile->getLanguageOptions();
5764-
if (auto isolation = options.defaultIsolation) {
5765-
defaultIsolation = *isolation;
5766-
}
5767-
}
5768-
}
5828+
auto *sourceFile = value->getDeclContext()->getParentSourceFile();
5829+
auto defaultIsolation = evaluateOrDefault(
5830+
ctx.evaluator, DefaultActorIsolationRequest{sourceFile},
5831+
ctx.LangOpts.DefaultIsolationBehavior);
57695832

57705833
if (defaultIsolation == DefaultIsolation::MainActor) {
57715834
// Default global actor isolation does not apply to any declarations
@@ -5779,7 +5842,6 @@ computeDefaultInferredActorIsolation(ValueDecl *value) {
57795842
dc = dc->getParent();
57805843
}
57815844

5782-
auto mainActorType = ctx.getMainActorType()->mapTypeOutOfContext();
57835845
if (!inActorContext) {
57845846
// FIXME: deinit should be implicitly MainActor too.
57855847
if (isa<TypeDecl>(value) || isa<ExtensionDecl>(value) ||

stdlib/public/Concurrency/Actor.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,3 +140,6 @@ public func extractIsolation<each Arg, Result>(
140140
return Builtin.extractFunctionIsolation(fn)
141141
}
142142
#endif
143+
144+
@available(SwiftStdlib 6.2, *)
145+
public struct nonisolated {}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: split-file %s %t
3+
4+
// REQUIRES: concurrency
5+
// REQUIRES: swift_feature_DefaultIsolationTypealias
6+
7+
// RUN: %target-swift-frontend -enable-experimental-feature DefaultIsolationTypealias -emit-sil -swift-version 6 -disable-availability-checking %t/main.swift %t/concurrent.swift | %FileCheck %s
8+
9+
//--- main.swift
10+
11+
private typealias DefaultIsolation = MainActor
12+
13+
class C {
14+
// CHECK: // static C.shared.getter
15+
// CHECK-NEXT: // Isolation: global_actor. type: MainActor
16+
static let shared = C()
17+
18+
// CHECK: // C.init()
19+
// CHECK-NEXT: // Isolation: global_actor. type: MainActor
20+
init() {}
21+
}
22+
23+
// CHECK: // test()
24+
// CHECK-NEXT: // Isolation: global_actor. type: MainActor
25+
func test() {
26+
// CHECK: // closure #1 in test()
27+
// CHECK-NEXT: // Isolation: nonisolated
28+
Task.detached {
29+
let s = S(value: 0)
30+
}
31+
}
32+
33+
34+
//--- concurrent.swift
35+
36+
private typealias DefaultIsolation = nonisolated
37+
38+
// CHECK: // S.init(value:)
39+
// CHECK-NEXT: // Isolation: unspecified
40+
struct S {
41+
// CHECK: // S.value.getter
42+
// CHECK-NEXT: // Isolation: unspecified
43+
var value: Int
44+
}

0 commit comments

Comments
 (0)