Skip to content

Commit c16e93f

Browse files
authored
Merge pull request #81420 from DougGregor/se-0470-synthesized-conformances-main-actor
[SE-0470] Fix synthesized conformances with default main actor isolation
2 parents ff5b821 + 9d493db commit c16e93f

File tree

6 files changed

+91
-2
lines changed

6 files changed

+91
-2
lines changed

include/swift/AST/ProtocolConformance.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,9 @@ class NormalProtocolConformance : public RootProtocolConformance,
713713
return ExplicitSafety::Unspecified;
714714
}
715715

716+
/// Whether this conformance has explicitly-specified global actor isolation.
717+
bool hasExplicitGlobalActorIsolation() const;
718+
716719
/// Determine whether we've lazily computed the associated conformance array
717720
/// already.
718721
bool hasComputedAssociatedConformances() const {

lib/AST/ProtocolConformance.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,10 @@ TypeExpr *NormalProtocolConformance::getExplicitGlobalActorIsolation() const {
515515
return ctx.getGlobalCache().conformanceExplicitGlobalActorIsolation[this];
516516
}
517517

518+
bool NormalProtocolConformance::hasExplicitGlobalActorIsolation() const {
519+
return Bits.NormalProtocolConformance.HasExplicitGlobalActor;
520+
}
521+
518522
void
519523
NormalProtocolConformance::setExplicitGlobalActorIsolation(TypeExpr *typeExpr) {
520524
if (!typeExpr) {

lib/Sema/CodeSynthesis.cpp

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1723,12 +1723,52 @@ bool swift::hasLetStoredPropertyWithInitialValue(NominalTypeDecl *nominal) {
17231723
});
17241724
}
17251725

1726+
/// Determine whether a synth
1727+
static bool synthesizedRequirementIsNonIsolated(
1728+
const NormalProtocolConformance *conformance) {
1729+
// @preconcurrency suppresses this.
1730+
if (conformance->isPreconcurrency())
1731+
return false;
1732+
1733+
// Explicit global actor isolation suppresses this.
1734+
if (conformance->hasExplicitGlobalActorIsolation())
1735+
return false;
1736+
1737+
// Explicit nonisolated forces this.
1738+
if (conformance->getOptions()
1739+
.contains(ProtocolConformanceFlags::Nonisolated))
1740+
return true;
1741+
1742+
// When we are inferring conformance isolation, only add nonisolated if
1743+
// either
1744+
// (1) the protocol inherits from SendableMetatype, or
1745+
// (2) the conforming type is nonisolated.
1746+
ASTContext &ctx = conformance->getDeclContext()->getASTContext();
1747+
if (!ctx.LangOpts.hasFeature(Feature::InferIsolatedConformances))
1748+
return true;
1749+
1750+
// Check inheritance from SendableMetatype, which implies that the conformance
1751+
// will be nonisolated.
1752+
auto sendableMetatypeProto =
1753+
ctx.getProtocol(KnownProtocolKind::SendableMetatype);
1754+
if (sendableMetatypeProto &&
1755+
conformance->getProtocol()->inheritsFrom(sendableMetatypeProto))
1756+
return true;
1757+
1758+
auto nominalType = conformance->getType()->getAnyNominal();
1759+
if (!nominalType)
1760+
return true;
1761+
1762+
return !getActorIsolation(nominalType).isMainActor();
1763+
}
1764+
17261765
bool swift::addNonIsolatedToSynthesized(DerivedConformance &derived,
17271766
ValueDecl *value) {
17281767
if (auto *conformance = derived.Conformance) {
1729-
if (conformance && conformance->isPreconcurrency())
1768+
if (!synthesizedRequirementIsNonIsolated(conformance))
17301769
return false;
17311770
}
1771+
17321772
return addNonIsolatedToSynthesized(derived.Nominal, value);
17331773
}
17341774

lib/Sema/DerivedConformance/DerivedConformanceCodable.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +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;
166+
}
167+
158168
// For classes which inherit from something Encodable or Decodable, we
159169
// provide case `super` as the first key (to be used in encoding super).
160170
auto *classDecl = dyn_cast<ClassDecl>(target);

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7944,16 +7944,24 @@ ConformanceIsolationRequest::evaluate(Evaluator &evaluator, ProtocolConformance
79447944

79457945
auto dc = rootNormal->getDeclContext();
79467946
ASTContext &ctx = dc->getASTContext();
7947+
auto proto = rootNormal->getProtocol();
79477948

79487949
// If the protocol itself is isolated, don't infer isolation for the
79497950
// conformance.
7950-
if (getActorIsolation(rootNormal->getProtocol()).isActorIsolated())
7951+
if (getActorIsolation(proto).isActorIsolated())
7952+
return ActorIsolation::forNonisolated(false);
7953+
7954+
// SendableMetatype disables isolation inference.
7955+
auto sendableMetatypeProto =
7956+
ctx.getProtocol(KnownProtocolKind::SendableMetatype);
7957+
if (sendableMetatypeProto && proto->inheritsFrom(sendableMetatypeProto))
79517958
return ActorIsolation::forNonisolated(false);
79527959

79537960
// @preconcurrency disables isolation inference.
79547961
if (rootNormal->isPreconcurrency())
79557962
return ActorIsolation::forNonisolated(false);
79567963

7964+
79577965
// Isolation inference rules follow. If we aren't inferring isolated conformances,
79587966
// we're done.
79597967
if (!ctx.LangOpts.hasFeature(Feature::InferIsolatedConformances))

test/Concurrency/isolated_conformance_default_actor.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,24 @@ nonisolated class CNonIsolated: P {
4242
@MainActor func f() { } // expected-note{{main actor-isolated instance method 'f()' cannot satisfy nonisolated requirement}}
4343
}
4444

45+
// Synthesized conformances
46+
struct EquatableStruct: Equatable {
47+
var state: Int = 0
48+
}
49+
50+
struct HashableStruct: Hashable {
51+
var state: Int = 0
52+
}
53+
54+
enum RawRepresentableEnum: Int {
55+
case one
56+
case two
57+
}
58+
59+
class CodableClass: Codable {
60+
var state: Int = 0
61+
}
62+
4563
func acceptSendablePMeta<T: Sendable & P>(_: T.Type) { }
4664
func acceptSendableQMeta<T: Sendable & Q>(_: T.Type) { }
4765

@@ -55,4 +73,10 @@ nonisolated func testConformancesFromNonisolated() {
5573
// Okay, these are nonisolated conformances.
5674
let _: any Q = CExplicitMainActor()
5775
let _: any Q = CImplicitMainActor()
76+
77+
// Error, these are main-actor-isolated conformances
78+
let _: any Equatable.Type = EquatableStruct.self // expected-error{{main actor-isolated conformance of 'EquatableStruct' to 'Equatable' cannot be used in nonisolated context}}
79+
let _: any Hashable.Type = HashableStruct.self // expected-error{{main actor-isolated conformance of 'HashableStruct' to 'Hashable' cannot be used in nonisolated context}}
80+
let _: any RawRepresentable.Type = RawRepresentableEnum.self
81+
let _: any Encodable.Type = CodableClass.self // expected-error{{main actor-isolated conformance of 'CodableClass' to 'Encodable' cannot be used in nonisolated context}}
5882
}

0 commit comments

Comments
 (0)