Skip to content

Commit 2f83882

Browse files
authored
Merge pull request #82590 from DougGregor/no-main-actor-when-conforming-to-sendable-6.2
[6.2] [SE-0466 experimental amendment] Don't infer @mainactor on types conforming to Sendable
2 parents 997a252 + 09165e9 commit 2f83882

File tree

6 files changed

+134
-15
lines changed

6 files changed

+134
-15
lines changed

include/swift/Basic/Features.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,10 @@ EXPERIMENTAL_FEATURE(DefaultIsolationPerFile, false)
530530
/// Enable @_lifetime attribute
531531
SUPPRESSIBLE_EXPERIMENTAL_FEATURE(Lifetimes, true)
532532

533+
/// Disable @MainActor inference when the primary definition of a type conforms
534+
/// to SendableMetatype (or Sendable).
535+
EXPERIMENTAL_FEATURE(SendableProhibitsMainActorInference, true)
536+
533537
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
534538
#undef EXPERIMENTAL_FEATURE
535539
#undef UPCOMING_FEATURE

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(SendableProhibitsMainActorInference)
130131

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

lib/Sema/DerivedConformance/DerivedConformanceCodable.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -155,14 +155,16 @@ addImplicitCodingKeys(NominalTypeDecl *target,
155155
enumDecl->setSynthesized();
156156
enumDecl->setAccess(AccessLevel::Private);
157157

158-
switch (C.LangOpts.DefaultIsolationBehavior) {
159-
case DefaultIsolation::MainActor:
160-
enumDecl->getAttrs().add(NonisolatedAttr::createImplicit(C));
161-
break;
162-
163-
case DefaultIsolation::Nonisolated:
164-
// Nothing to do.
165-
break;
158+
if (!C.LangOpts.hasFeature(Feature::SendableProhibitsMainActorInference)) {
159+
switch (C.LangOpts.DefaultIsolationBehavior) {
160+
case DefaultIsolation::MainActor:
161+
enumDecl->getAttrs().add(NonisolatedAttr::createImplicit(C));
162+
break;
163+
164+
case DefaultIsolation::Nonisolated:
165+
// Nothing to do.
166+
break;
167+
}
166168
}
167169

168170
// For classes which inherit from something Encodable or Decodable, we

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 76 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5956,6 +5956,65 @@ static void addAttributesForActorIsolation(ValueDecl *value,
59565956
}
59575957
}
59585958

5959+
/// Determine whether there is a SendableMetatype conformance that requires that the nominal type
5960+
/// be nonisolated (preventing @MainActor inference).
5961+
static bool sendableConformanceRequiresNonisolated(NominalTypeDecl *nominal) {
5962+
ASTContext &ctx = nominal->getASTContext();
5963+
if (!ctx.LangOpts.hasFeature(Feature::SendableProhibitsMainActorInference))
5964+
return false;
5965+
5966+
if (isa<ProtocolDecl>(nominal))
5967+
return false;
5968+
5969+
auto sendable = ctx.getProtocol(KnownProtocolKind::Sendable);
5970+
auto sendableMetatype = ctx.getProtocol(KnownProtocolKind::SendableMetatype);
5971+
if (!sendableMetatype)
5972+
return false;
5973+
5974+
// Check whether any of the explicit conformances is to a
5975+
// SendableMetatype-inheriting protocol. We exclude direct conformance to
5976+
// Sendable here, because a global-actor-isolated type is implicitly Sendable,
5977+
// and writing Sendable explicitly
5978+
InvertibleProtocolSet inverses;
5979+
bool anyObject = false;
5980+
auto inherited = getDirectlyInheritedNominalTypeDecls(
5981+
nominal, inverses, anyObject);
5982+
for (const auto &entry : inherited) {
5983+
auto proto = dyn_cast<ProtocolDecl>(entry.Item);
5984+
if (proto && proto != sendable && proto->inheritsFrom(sendableMetatype))
5985+
return true;
5986+
}
5987+
5988+
// Check for member or extension macros that define conformances to
5989+
// SendableMetatype-inheriting protocols.
5990+
bool requiresNonisolated = false;
5991+
auto checkMacro = [&](MacroRole role, MacroDecl *macro) {
5992+
if (!macro || requiresNonisolated)
5993+
return;
5994+
5995+
SmallVector<ProtocolDecl *, 2> conformances;
5996+
macro->getIntroducedConformances(nominal, role, conformances);
5997+
for (auto proto : conformances) {
5998+
if (proto == sendableMetatype || proto->inheritsFrom(sendableMetatype)) {
5999+
requiresNonisolated = true;
6000+
break;
6001+
}
6002+
}
6003+
};
6004+
6005+
nominal->forEachAttachedMacro(
6006+
MacroRole::Member,
6007+
[&](CustomAttr * attr, MacroDecl *macro) {
6008+
checkMacro(MacroRole::Member, macro);
6009+
});
6010+
nominal->forEachAttachedMacro(
6011+
MacroRole::Extension,
6012+
[&](CustomAttr * attr, MacroDecl *macro) {
6013+
checkMacro(MacroRole::Extension, macro);
6014+
});
6015+
return requiresNonisolated;
6016+
}
6017+
59596018
/// Determine the default isolation and isolation source for this declaration,
59606019
/// which may still be overridden by other inference rules.
59616020
static std::tuple<InferredActorIsolation, ValueDecl *,
@@ -5975,24 +6034,34 @@ computeDefaultInferredActorIsolation(ValueDecl *value) {
59756034
auto *dc = value->getInnermostDeclContext();
59766035
while (dc && !inActorContext) {
59776036
if (auto *nominal = dc->getSelfNominalTypeDecl()) {
5978-
inActorContext = nominal->isAnyActor();
6037+
if (nominal->isAnyActor())
6038+
return {};
59796039
}
59806040
dc = dc->getParent();
59816041
}
59826042

5983-
if (!inActorContext) {
5984-
// FIXME: deinit should be implicitly MainActor too.
5985-
if (isa<TypeDecl>(value) || isa<ExtensionDecl>(value) ||
5986-
isa<AbstractStorageDecl>(value) || isa<FuncDecl>(value) ||
5987-
isa<ConstructorDecl>(value)) {
6043+
// If this is or is a non-type member of a nominal type that conforms to a
6044+
// SendableMetatype-inheriting protocol in its primary definition, disable
6045+
// @MainActor inference.
6046+
auto nominalTypeDecl = dyn_cast<NominalTypeDecl>(value);
6047+
if (!nominalTypeDecl && !isa<TypeDecl>(value)) {
6048+
nominalTypeDecl = value->getDeclContext()->getSelfNominalTypeDecl();
6049+
}
6050+
if (nominalTypeDecl &&
6051+
sendableConformanceRequiresNonisolated(nominalTypeDecl))
6052+
return { };
6053+
6054+
// FIXME: deinit should be implicitly MainActor too.
6055+
if (isa<TypeDecl>(value) || isa<ExtensionDecl>(value) ||
6056+
isa<AbstractStorageDecl>(value) || isa<FuncDecl>(value) ||
6057+
isa<ConstructorDecl>(value)) {
59886058
// Preconcurrency here is used to stage the diagnostics
59896059
// when users select `@MainActor` default isolation with
59906060
// non-strict concurrency modes (pre Swift 6).
59916061
auto isolation =
59926062
ActorIsolation::forGlobalActor(globalActor)
59936063
.withPreconcurrency(!ctx.LangOpts.isSwiftVersionAtLeast(6));
59946064
return {{{isolation, {}}, nullptr, {}}};
5995-
}
59966065
}
59976066

59986067
return {};
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %target-swift-frontend -swift-version 5 -emit-sil -default-isolation MainActor %s -verify -verify-additional-prefix swift5- -enable-experimental-feature SendableProhibitsMainActorInference
2+
// RUN: %target-swift-frontend -swift-version 6 -emit-sil -default-isolation MainActor %s -verify -verify-additional-prefix swift6- -enable-experimental-feature SendableProhibitsMainActorInference
3+
4+
// REQUIRES: swift_feature_SendableProhibitsMainActorInference
5+
6+
// Ensure that a Sendable-conforming protocol suppresses @MainActor inference
7+
// for a type.
8+
enum CK: CodingKey {
9+
case one
10+
11+
func f() { }
12+
}
13+
14+
nonisolated func testCK(x: CK) {
15+
x.f() // okay, because CK and CK.f are not @MainActor.
16+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// REQUIRES: swift_swift_parser, executable_test
2+
3+
// RUN: %empty-directory(%t)
4+
// RUN: %host-build-swift -swift-version 5 -emit-library -o %t/%target-library-name(MacroDefinition) -module-name=MacroDefinition %S/Inputs/syntax_macro_definitions.swift -g -no-toolchain-stdlib-rpath
5+
6+
// Check for errors
7+
// RUN: %target-swift-frontend -swift-version 5 -typecheck -load-plugin-library %t/%target-library-name(MacroDefinition) %s -I %t -disable-availability-checking -swift-version 6 -default-isolation MainActor -enable-experimental-feature SendableProhibitsMainActorInference
8+
9+
// REQUIRES: swift_feature_SendableProhibitsMainActorInference
10+
11+
@attached(extension, conformances: Sendable)
12+
macro AddSendable() = #externalMacro(module: "MacroDefinition", type: "SendableMacro")
13+
14+
@AddSendable
15+
final class SendableClass {
16+
var property: Int { 0 }
17+
18+
func f() { }
19+
}
20+
21+
nonisolated func acceptSendable<T: Sendable>(_: T) { }
22+
23+
@concurrent func test(sc: SendableClass) async {
24+
acceptSendable(sc) // SendableClass is Sendable
25+
acceptSendable(\SendableClass.property) // so is its property
26+
sc.f() // not on the main actor, so this is okay
27+
}

0 commit comments

Comments
 (0)