Skip to content

Commit 3c8405a

Browse files
authored
Merge pull request #42041 from DougGregor/derive-nonisolated-witnesses
2 parents 53617f0 + 0c7707f commit 3c8405a

11 files changed

+94
-9
lines changed

lib/Sema/CodeSynthesis.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl,
331331

332332
if (ICK == ImplicitConstructorKind::Memberwise) {
333333
ctor->setIsMemberwiseInitializer();
334+
addNonIsolatedToSynthesized(decl, ctor);
334335
}
335336

336337
// If we are defining a default initializer for a class that has a superclass,
@@ -1526,3 +1527,12 @@ void swift::addFixedLayoutAttr(NominalTypeDecl *nominal) {
15261527
// Add `@_fixed_layout` to the nominal.
15271528
nominal->getAttrs().add(new (C) FixedLayoutAttr(/*Implicit*/ true));
15281529
}
1530+
1531+
void swift::addNonIsolatedToSynthesized(
1532+
NominalTypeDecl *nominal, ValueDecl *value) {
1533+
if (!getActorIsolation(nominal).isActorIsolated())
1534+
return;
1535+
1536+
ASTContext &ctx = nominal->getASTContext();
1537+
value->getAttrs().add(new (ctx) NonisolatedAttr(/*isImplicit=*/true));
1538+
}

lib/Sema/CodeSynthesis.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ bool hasLetStoredPropertyWithInitialValue(NominalTypeDecl *nominal);
7777
/// Add `@_fixed_layout` attribute to the nominal type, if possible.
7878
void addFixedLayoutAttr(NominalTypeDecl *nominal);
7979

80+
/// Add 'nonisolated' to the synthesized declaration when needed.
81+
void addNonIsolatedToSynthesized(NominalTypeDecl *nominal, ValueDecl *value);
82+
8083
} // end namespace swift
8184

8285
#endif

lib/Sema/DerivedConformanceCodable.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
//
1616
//===----------------------------------------------------------------------===//
1717

18+
#include "CodeSynthesis.h"
1819
#include "TypeChecker.h"
1920
#include "llvm/ADT/STLExtras.h"
2021
#include "swift/AST/Decl.h"
@@ -1247,6 +1248,8 @@ static FuncDecl *deriveEncodable_encode(DerivedConformance &derived) {
12471248
encodeDecl->getAttrs().add(attr);
12481249
}
12491250

1251+
addNonIsolatedToSynthesized(derived.Nominal, encodeDecl);
1252+
12501253
encodeDecl->copyFormalAccessFrom(derived.Nominal,
12511254
/*sourceIsParentContext*/ true);
12521255

@@ -1866,6 +1869,8 @@ static ValueDecl *deriveDecodable_init(DerivedConformance &derived) {
18661869
initDecl->getAttrs().add(reqAttr);
18671870
}
18681871

1872+
addNonIsolatedToSynthesized(derived.Nominal, initDecl);
1873+
18691874
initDecl->copyFormalAccessFrom(derived.Nominal,
18701875
/*sourceIsParentContext*/ true);
18711876

@@ -2042,6 +2047,12 @@ static bool canDeriveCodable(NominalTypeDecl *NTD,
20422047
return false;
20432048
}
20442049

2050+
// Actor-isolated structs and classes cannot derive encodable/decodable
2051+
// unless all of their stored properties are immutable.
2052+
if ((isa<StructDecl>(NTD) || isa<ClassDecl>(NTD)) &&
2053+
memberwiseAccessorsRequireActorIsolation(NTD))
2054+
return false;
2055+
20452056
return true;
20462057
}
20472058

lib/Sema/DerivedConformanceComparable.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
//
1616
//===----------------------------------------------------------------------===//
1717

18+
#include "CodeSynthesis.h"
1819
#include "TypeChecker.h"
1920
#include "swift/AST/Decl.h"
2021
#include "swift/AST/Stmt.h"
@@ -275,6 +276,8 @@ deriveComparable_lt(
275276
return nullptr;
276277
}
277278

279+
addNonIsolatedToSynthesized(derived.Nominal, comparableDecl);
280+
278281
comparableDecl->setBodySynthesizer(bodySynthesizer);
279282

280283
comparableDecl->copyFormalAccessFrom(derived.Nominal, /*sourceIsParentContext*/ true);

lib/Sema/DerivedConformanceEquatableHashable.cpp

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
//
1616
//===----------------------------------------------------------------------===//
1717

18+
#include "CodeSynthesis.h"
1819
#include "TypeChecker.h"
1920
#include "swift/AST/Decl.h"
2021
#include "swift/AST/Stmt.h"
@@ -45,9 +46,16 @@ static bool canDeriveConformance(DeclContext *DC,
4546
if (auto structDecl = dyn_cast<StructDecl>(target)) {
4647
// All stored properties of the struct must conform to the protocol. If
4748
// there are no stored properties, we will vaccously return true.
48-
return DerivedConformance::storedPropertiesNotConformingToProtocol(
49-
DC, structDecl, protocol)
50-
.empty();
49+
if (!DerivedConformance::storedPropertiesNotConformingToProtocol(
50+
DC, structDecl, protocol).empty())
51+
return false;
52+
53+
// If the struct is actor-isolated, we cannot derive Equatable/Hashable
54+
// conformance if any of the stored properties are mutable.
55+
if (memberwiseAccessorsRequireActorIsolation(structDecl))
56+
return false;
57+
58+
return true;
5159
}
5260

5361
return false;
@@ -426,6 +434,8 @@ deriveEquatable_eq(
426434
return nullptr;
427435
}
428436

437+
addNonIsolatedToSynthesized(derived.Nominal, eqDecl);
438+
429439
eqDecl->setBodySynthesizer(bodySynthesizer);
430440

431441
eqDecl->copyFormalAccessFrom(derived.Nominal, /*sourceIsParentContext*/ true);
@@ -544,7 +554,7 @@ deriveHashable_hashInto(
544554
/*Throws=*/false,
545555
/*GenericParams=*/nullptr, params, returnType, parentDC);
546556
hashDecl->setBodySynthesizer(bodySynthesizer);
547-
557+
addNonIsolatedToSynthesized(derived.Nominal, hashDecl);
548558
hashDecl->copyFormalAccessFrom(derived.Nominal,
549559
/*sourceIsParentContext=*/true);
550560

@@ -881,6 +891,7 @@ static ValueDecl *deriveHashable_hashValue(DerivedConformance &derived) {
881891
SourceLoc(), C.Id_hashValue, parentDC);
882892
hashValueDecl->setInterfaceType(intType);
883893
hashValueDecl->setSynthesized();
894+
addNonIsolatedToSynthesized(derived.Nominal, hashValueDecl);
884895

885896
ParameterList *params = ParameterList::createEmpty(C);
886897

@@ -896,7 +907,6 @@ static ValueDecl *deriveHashable_hashValue(DerivedConformance &derived) {
896907
getterDecl->setBodySynthesizer(&deriveBodyHashable_hashValue);
897908
getterDecl->setSynthesized();
898909
getterDecl->setIsTransparent(false);
899-
900910
getterDecl->copyFormalAccessFrom(derived.Nominal,
901911
/*sourceIsParentContext*/ true);
902912

lib/Sema/DerivedConformanceError.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
//
1616
//===----------------------------------------------------------------------===//
1717

18+
#include "CodeSynthesis.h"
1819
#include "TypeChecker.h"
1920
#include "DerivedConformances.h"
2021
#include "swift/AST/Decl.h"
@@ -100,6 +101,7 @@ deriveBridgedNSError_enum_nsErrorDomain(
100101
DerivedConformance::SynthesizedIntroducer::Var,
101102
derived.Context.Id_nsErrorDomain, stringTy, stringTy, /*isStatic=*/true,
102103
/*isFinal=*/true);
104+
addNonIsolatedToSynthesized(derived.Nominal, propDecl);
103105

104106
// Define the getter.
105107
auto getterDecl = derived.addGetterToReadOnlyDerivedProperty(

lib/Sema/DerivedConformanceRawRepresentable.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
//
1616
//===----------------------------------------------------------------------===//
1717

18+
#include "CodeSynthesis.h"
1819
#include "TypeChecker.h"
1920
#include "swift/AST/Decl.h"
2021
#include "swift/AST/Stmt.h"
@@ -168,6 +169,7 @@ static VarDecl *deriveRawRepresentable_raw(DerivedConformance &derived) {
168169
DerivedConformance::SynthesizedIntroducer::Var, C.Id_rawValue,
169170
rawInterfaceType, rawType, /*isStatic=*/false,
170171
/*isFinal=*/false);
172+
addNonIsolatedToSynthesized(enumDecl, propDecl);
171173

172174
// Define the getter.
173175
auto getterDecl = DerivedConformance::addGetterToReadOnlyDerivedProperty(
@@ -432,7 +434,7 @@ deriveRawRepresentable_init(DerivedConformance &derived) {
432434

433435
initDecl->setImplicit();
434436
initDecl->setBodySynthesizer(&deriveBodyRawRepresentable_init);
435-
437+
addNonIsolatedToSynthesized(enumDecl, initDecl);
436438
initDecl->copyFormalAccessFrom(enumDecl, /*sourceIsParentContext*/true);
437439

438440
// If the containing module is not resilient, make sure clients can construct

lib/Sema/DerivedConformances.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,6 @@ DerivedConformance::declareDerivedPropertyGetter(VarDecl *property,
502502
property->getInterfaceType(), parentDC);
503503
getterDecl->setImplicit();
504504
getterDecl->setIsTransparent(false);
505-
506505
getterDecl->copyFormalAccessFrom(property);
507506

508507

@@ -887,3 +886,18 @@ VarDecl *DerivedConformance::indexedVarDecl(char prefixChar, int index, Type typ
887886
varDecl->setInterfaceType(type);
888887
return varDecl;
889888
}
889+
890+
bool swift::memberwiseAccessorsRequireActorIsolation(NominalTypeDecl *nominal) {
891+
if (!getActorIsolation(nominal).isActorIsolated())
892+
return false;
893+
894+
for (auto property : nominal->getStoredProperties()) {
895+
if (!property->isUserAccessible())
896+
continue;
897+
898+
if (!property->isLet())
899+
return true;
900+
}
901+
902+
return false;
903+
}

lib/Sema/DerivedConformances.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,12 @@ class DerivedConformance {
442442
static VarDecl *indexedVarDecl(char prefixChar, int index, Type type,
443443
DeclContext *varContext);
444444
};
445+
446+
/// Determine whether any "memberwise" accessors, which walk through the
447+
/// stored properties of the given nominal type, require actor isolation
448+
/// because they involve mutable state.
449+
bool memberwiseAccessorsRequireActorIsolation(NominalTypeDecl *nominal);
450+
445451
} // namespace swift
446452

447453
#endif

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3554,11 +3554,11 @@ static Optional<MemberIsolationPropagation> getMemberIsolationPropagation(
35543554
case DeclKind::Param:
35553555
case DeclKind::Module:
35563556
case DeclKind::Destructor:
3557+
case DeclKind::EnumCase:
3558+
case DeclKind::EnumElement:
35573559
return None;
35583560

35593561
case DeclKind::PatternBinding:
3560-
case DeclKind::EnumCase:
3561-
case DeclKind::EnumElement:
35623562
return MemberIsolationPropagation::GlobalActor;
35633563

35643564
case DeclKind::Constructor:
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: %target-typecheck-verify-swift -disable-availability-checking -warn-concurrency -parse-as-library
2+
// REQUIRES: concurrency
3+
4+
@MainActor
5+
struct X1: Equatable, Hashable, Codable {
6+
let x: Int
7+
let y: String
8+
}
9+
10+
// expected-error@+5{{type 'X2' does not conform to protocol 'Encodable'}}
11+
// expected-error@+4{{type 'X2' does not conform to protocol 'Decodable'}}
12+
// expected-error@+3{{type 'X2' does not conform to protocol 'Equatable'}}
13+
// expected-error@+2{{type 'X2' does not conform to protocol 'Hashable'}}
14+
@MainActor
15+
struct X2: Equatable, Hashable, Codable {
16+
let x: Int
17+
var y: String
18+
}
19+
20+
@MainActor
21+
enum X3: Hashable, Comparable, Codable {
22+
case a
23+
case b(Int)
24+
}

0 commit comments

Comments
 (0)