Skip to content

Commit 1e489d6

Browse files
committed
[Distributed] Force the order of properties in AST
1 parent 3619472 commit 1e489d6

File tree

7 files changed

+106
-99
lines changed

7 files changed

+106
-99
lines changed

lib/Sema/CodeSynthesisDistributedActor.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,17 @@ static VarDecl *addImplicitDistributedActorIDProperty(
117117
propDecl->getAttrs().add(
118118
new (C) CompilerInitializedAttr(/*IsImplicit=*/true));
119119

120-
nominal->addMember(propDecl);
121-
nominal->addMember(pbDecl);
122-
120+
// IMPORTANT: The `id` MUST be the first field of any distributed actor,
121+
// because when we allocate remote proxy instances, we don't allocate memory
122+
// for anything except the first two fields: id and actorSystem, so they
123+
// MUST be those fields.
124+
//
125+
// Their specific order also matters, because it is enforced this way in IRGen
126+
// and how we emit them in AST MUST match what IRGen expects or cross-module
127+
// things could be using wrong offsets and manifest as reading trash memory on
128+
// id or system accesses.
129+
nominal->addMember(propDecl, /*hint=*/nullptr, /*insertAtHead=*/true);
130+
nominal->addMember(pbDecl, /*hint=*/nullptr, /*insertAtHead=*/true);
123131
return propDecl;
124132
}
125133

lib/Sema/DerivedConformanceDistributedActor.cpp

Lines changed: 15 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -426,32 +426,6 @@ static FuncDecl *deriveDistributedActorSystem_invokeHandlerOnReturn(
426426
/******************************* PROPERTIES ***********************************/
427427
/******************************************************************************/
428428

429-
// TODO(distributed): make use of this after all, but FORCE it?
430-
static ValueDecl *deriveDistributedActor_id(DerivedConformance &derived) {
431-
assert(derived.Nominal->isDistributedActor());
432-
auto &C = derived.Context;
433-
434-
// ```
435-
// nonisolated
436-
// let id: Self.ID // Self.ActorSystem.ActorID
437-
// ```
438-
auto propertyType = getDistributedActorIDType(derived.Nominal);
439-
440-
VarDecl *propDecl;
441-
PatternBindingDecl *pbDecl;
442-
std::tie(propDecl, pbDecl) = derived.declareDerivedProperty(
443-
DerivedConformance::SynthesizedIntroducer::Let, C.Id_id, propertyType,
444-
propertyType,
445-
/*isStatic=*/false, /*isFinal=*/true);
446-
447-
// mark as nonisolated, allowing access to it from everywhere
448-
propDecl->getAttrs().add(
449-
new (C) NonisolatedAttr(/*IsImplicit=*/true));
450-
451-
derived.addMembersToConformanceContext({ propDecl, pbDecl });
452-
return propDecl;
453-
}
454-
455429
static ValueDecl *deriveDistributedActor_actorSystem(
456430
DerivedConformance &derived) {
457431
auto &C = derived.Context;
@@ -460,8 +434,7 @@ static ValueDecl *deriveDistributedActor_actorSystem(
460434
assert(classDecl && derived.Nominal->isDistributedActor());
461435

462436
// ```
463-
// nonisolated
464-
// let actorSystem: ActorSystem
437+
// nonisolated let actorSystem: ActorSystem
465438
// ```
466439
// (no need for @actorIndependent because it is an immutable let)
467440
auto propertyType = getDistributedActorSystemType(classDecl);
@@ -477,7 +450,14 @@ static ValueDecl *deriveDistributedActor_actorSystem(
477450
propDecl->getAttrs().add(
478451
new (C) NonisolatedAttr(/*IsImplicit=*/true));
479452

480-
derived.addMembersToConformanceContext({ propDecl, pbDecl });
453+
// IMPORTANT: `id` MUST be the first field of a distributed actor, and
454+
// `actorSystem` MUST be the second field, because for a remote instance
455+
// we don't allocate memory after those two fields, so their order is very
456+
// important. The `hint` below makes sure the system is inserted right after.
457+
auto id = derived.Nominal->getDistributedActorIDProperty();
458+
derived.addMemberToConformanceContext(pbDecl, /*hint=*/id);
459+
derived.addMemberToConformanceContext(propDecl, /*hint=*/id);
460+
481461
return propDecl;
482462
}
483463

@@ -571,11 +551,14 @@ deriveDistributedActorType_SerializationRequirement(
571551

572552
ValueDecl *DerivedConformance::deriveDistributedActor(ValueDecl *requirement) {
573553
if (auto var = dyn_cast<VarDecl>(requirement)) {
574-
if (var->getName() == Context.Id_id)
575-
return deriveDistributedActor_id(*this);
576-
577554
if (var->getName() == Context.Id_actorSystem)
578555
return deriveDistributedActor_actorSystem(*this);
556+
557+
if (var->getName() == Context.Id_id)
558+
llvm_unreachable("DistributedActor.id MUST be synthesized earlier, "
559+
"because it is forced by the Identifiable conformance. "
560+
"If we attempted to do synthesis here, the earlier phase "
561+
"failed and something is wrong: please report a bug.");
579562
}
580563

581564
if (auto func = dyn_cast<FuncDecl>(requirement)) {

lib/Sema/DerivedConformances.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,18 @@ void DerivedConformance::addMembersToConformanceContext(
5151
IDC->addMember(child);
5252
}
5353

54+
void DerivedConformance::addMemberToConformanceContext(
55+
Decl *member, Decl *hint) {
56+
auto IDC = cast<IterableDeclContext>(ConformanceDecl);
57+
IDC->addMember(member, hint, /*insertAtHead=*/false);
58+
}
59+
60+
void DerivedConformance::addMemberToConformanceContext(
61+
Decl *member, bool insertAtHead) {
62+
auto IDC = cast<IterableDeclContext>(ConformanceDecl);
63+
IDC->addMember(member, /*hint=*/nullptr, insertAtHead);
64+
}
65+
5466
Type DerivedConformance::getProtocolType() const {
5567
return Protocol->getDeclaredInterfaceType();
5668
}
@@ -325,10 +337,6 @@ ValueDecl *DerivedConformance::getDerivableRequirement(NominalTypeDecl *nominal,
325337
if (name.isSimpleName(ctx.Id_unownedExecutor))
326338
return getRequirement(KnownProtocolKind::Actor);
327339

328-
// DistributedActor.id
329-
if(name.isSimpleName(ctx.Id_id))
330-
return getRequirement(KnownProtocolKind::DistributedActor);
331-
332340
// DistributedActor.actorSystem
333341
if(name.isSimpleName(ctx.Id_actorSystem))
334342
return getRequirement(KnownProtocolKind::DistributedActor);

lib/Sema/DerivedConformances.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ class DerivedConformance {
7070

7171
/// Add \c children as members of the context that declares the conformance.
7272
void addMembersToConformanceContext(ArrayRef<Decl *> children);
73+
/// Add \c member right after the \c hint member which may be the tail
74+
void addMemberToConformanceContext(Decl *member, Decl* hint);
75+
/// Add \c member in front of any other existing members
76+
void addMemberToConformanceContext(Decl *member, bool insertAtHead);
7377

7478
/// Get the declared type of the protocol that this is conformance is for.
7579
Type getProtocolType() const;

stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift

Lines changed: 62 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
import Swift
14-
import _Concurrency
1514

1615
#if canImport(Darwin)
1716
import Darwin
@@ -27,33 +26,25 @@ import WinSDK
2726
/// for learning about `distributed actor` isolation, as well as early
2827
/// prototyping stages of development where a real system is not necessary yet.
2928
@available(SwiftStdlib 5.7, *)
30-
public struct LocalTestingDistributedActorSystem: DistributedActorSystem, @unchecked Sendable {
29+
public final class LocalTestingDistributedActorSystem: DistributedActorSystem, @unchecked Sendable {
3130
public typealias ActorID = LocalTestingActorAddress
3231
public typealias ResultHandler = LocalTestingInvocationResultHandler
3332
public typealias InvocationEncoder = LocalTestingInvocationEncoder
3433
public typealias InvocationDecoder = LocalTestingInvocationDecoder
35-
public typealias SerializationRequirement = any Codable
36-
37-
38-
@usableFromInline
39-
final class _Storage {
34+
public typealias SerializationRequirement = Codable
4035

41-
var activeActors: [ActorID: any DistributedActor] = [:]
42-
let activeActorsLock = _Lock()
36+
private var activeActors: [ActorID: any DistributedActor] = [:]
37+
private let activeActorsLock = _Lock()
4338

44-
var assignedIDs: Set<ActorID> = []
45-
let assignedIDsLock = _Lock()
46-
}
47-
let storage: _Storage
48-
var idProvider: ActorIDProvider = ActorIDProvider()
39+
private var idProvider: ActorIDProvider = ActorIDProvider()
40+
private var assignedIDs: Set<ActorID> = []
41+
private let assignedIDsLock = _Lock()
4942

50-
public init() {
51-
storage = .init()
52-
}
43+
public init() {}
5344

5445
public func resolve<Act>(id: ActorID, as actorType: Act.Type)
55-
throws -> Act? where Act: DistributedActor, Act.ID == ActorID {
56-
guard let anyActor = storage.activeActorsLock.withLock({ self.storage.activeActors[id] }) else {
46+
throws -> Act? where Act: DistributedActor {
47+
guard let anyActor = self.activeActorsLock.withLock({ self.activeActors[id] }) else {
5748
throw LocalTestingDistributedActorSystemError(message: "Unable to locate id '\(id)' locally")
5849
}
5950
guard let actor = anyActor as? Act else {
@@ -63,25 +54,28 @@ public struct LocalTestingDistributedActorSystem: DistributedActorSystem, @unche
6354
}
6455

6556
public func assignID<Act>(_ actorType: Act.Type) -> ActorID
66-
where Act: DistributedActor, Act.ID == ActorID {
57+
where Act: DistributedActor {
6758
let id = self.idProvider.next()
68-
storage.assignedIDsLock.withLock {
69-
self.storage.assignedIDs.insert(id)
59+
self.assignedIDsLock.withLock {
60+
self.assignedIDs.insert(id)
7061
}
7162
return id
7263
}
7364

7465
public func actorReady<Act>(_ actor: Act)
75-
where Act: DistributedActor, Act.ID == ActorID {
76-
guard storage.assignedIDsLock.withLock({ self.storage.assignedIDs.contains(actor.id) }) else {
66+
where Act: DistributedActor,
67+
Act.ID == ActorID {
68+
guard self.assignedIDsLock.withLock({ self.assignedIDs.contains(actor.id) }) else {
7769
fatalError("Attempted to mark an unknown actor '\(actor.id)' ready")
7870
}
79-
self.storage.activeActors[actor.id] = actor
71+
self.activeActorsLock.withLock {
72+
self.activeActors[actor.id] = actor
73+
}
8074
}
8175

8276
public func resignID(_ id: ActorID) {
83-
storage.activeActorsLock.withLock {
84-
self.storage.activeActors.removeValue(forKey: id)
77+
self.activeActorsLock.withLock {
78+
self.activeActors.removeValue(forKey: id)
8579
}
8680
}
8781

@@ -99,7 +93,7 @@ public struct LocalTestingDistributedActorSystem: DistributedActorSystem, @unche
9993
where Act: DistributedActor,
10094
Act.ID == ActorID,
10195
Err: Error,
102-
Res: Codable {
96+
Res: SerializationRequirement {
10397
fatalError("Attempted to make remote call to \(target) on actor \(actor) using a local-only actor system")
10498
}
10599

@@ -115,40 +109,51 @@ public struct LocalTestingDistributedActorSystem: DistributedActorSystem, @unche
115109
fatalError("Attempted to make remote call to \(target) on actor \(actor) using a local-only actor system")
116110
}
117111

118-
@usableFromInline
119-
final class ActorIDProvider {
112+
private struct ActorIDProvider {
120113
private var counter: Int = 0
121114
private let counterLock = _Lock()
122115

123-
@usableFromInline
124116
init() {}
125117

126-
func next() -> LocalTestingActorAddress {
118+
mutating func next() -> LocalTestingActorAddress {
127119
let id: Int = self.counterLock.withLock {
128120
self.counter += 1
129121
return self.counter
130122
}
131-
return LocalTestingActorAddress(id)
123+
return LocalTestingActorAddress(parse: "\(id)")
132124
}
133125
}
134126
}
135127

136128
@available(SwiftStdlib 5.7, *)
137-
public struct LocalTestingActorAddress: Hashable, Sendable, Codable {
138-
public let uuid: String
129+
@available(*, deprecated, renamed: "LocalTestingActorID")
130+
public typealias LocalTestingActorAddress = LocalTestingActorID
131+
132+
@available(SwiftStdlib 5.7, *)
133+
public struct LocalTestingActorID: Hashable, Sendable, Codable {
134+
@available(*, deprecated, renamed: "id")
135+
public var address: String {
136+
self.id
137+
}
138+
public let id: String
139+
140+
@available(*, deprecated, renamed: "init(id:)")
141+
public init(parse id: String) {
142+
self.id = id
143+
}
139144

140-
public init(_ uuid: Int) {
141-
self.uuid = "\(uuid)"
145+
public init(id: String) {
146+
self.id = id
142147
}
143148

144149
public init(from decoder: Decoder) throws {
145150
let container = try decoder.singleValueContainer()
146-
self.uuid = try container.decode(String.self)
151+
self.id = try container.decode(String.self)
147152
}
148153

149154
public func encode(to encoder: Encoder) throws {
150155
var container = encoder.singleValueContainer()
151-
try container.encode(self.uuid)
156+
try container.encode(self.id)
152157
}
153158
}
154159

@@ -228,7 +233,7 @@ public struct LocalTestingDistributedActorSystemError: DistributedActorSystemErr
228233
// === lock ----------------------------------------------------------------
229234

230235
@available(SwiftStdlib 5.7, *)
231-
internal class _Lock {
236+
fileprivate class _Lock {
232237
#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS)
233238
private let underlying: UnsafeMutablePointer<os_unfair_lock>
234239
#elseif os(Windows)
@@ -241,27 +246,10 @@ internal class _Lock {
241246
private let underlying: UnsafeMutablePointer<pthread_mutex_t>
242247
#endif
243248

244-
init() {
245-
#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS)
246-
self.underlying = UnsafeMutablePointer.allocate(capacity: 1)
247-
self.underlying.initialize(to: os_unfair_lock())
248-
#elseif os(Windows)
249-
self.underlying = UnsafeMutablePointer.allocate(capacity: 1)
250-
InitializeSRWLock(self.underlying)
251-
#elseif os(WASI)
252-
// WASI environment has only a single thread
253-
#else
254-
self.underlying = UnsafeMutablePointer.allocate(capacity: 1)
255-
guard pthread_mutex_init(self.underlying, nil) == 0 else {
256-
fatalError("pthread_mutex_init failed")
257-
}
258-
#endif
259-
}
260-
261249
deinit {
262250
#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS)
263251
// `os_unfair_lock`s do not need to be explicitly destroyed
264-
#elseif os(Windows)
252+
#elseif os(Windows)
265253
// `SRWLOCK`s do not need to be explicitly destroyed
266254
#elseif os(WASI)
267255
// WASI environment has only a single thread
@@ -277,6 +265,22 @@ internal class _Lock {
277265
#endif
278266
}
279267

268+
init() {
269+
#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS)
270+
self.underlying = UnsafeMutablePointer.allocate(capacity: 1)
271+
#elseif os(Windows)
272+
self.underlying = UnsafeMutablePointer.allocate(capacity: 1)
273+
InitializeSRWLock(self.underlying)
274+
#elseif os(WASI)
275+
// WASI environment has only a single thread
276+
#else
277+
self.underlying = UnsafeMutablePointer.allocate(capacity: 1)
278+
guard pthread_mutex_init(self.underlying, nil) == 0 else {
279+
fatalError("pthread_mutex_init failed")
280+
}
281+
#endif
282+
}
283+
280284
@discardableResult
281285
func withLock<T>(_ body: () -> T) -> T {
282286
#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS)

test/Distributed/Runtime/distributed_actor_init_local.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ func test() async {
286286

287287
let localDA = LocalTestingDA_Int()
288288
print("localDA = \(localDA.id)")
289-
// CHECK: localDA = LocalTestingActorAddress(uuid: "1")
289+
// CHECK: localDA = LocalTestingActorID(id: "1")
290290

291291
// the following tests fail to initialize the actor's identity.
292292
print("-- start of no-assign tests --")

test/IRGen/distributed_actor.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import Distributed
77

88
// Type descriptor.
9-
// CHECK-LABEL: @"$s17distributed_actor7MyActorC2id11Distributed012LocalTestingD7AddressVvpWvd"
9+
// CHECK-LABEL: @"$s17distributed_actor7MyActorC2id11Distributed012LocalTestingD2IDVvpWvd"
1010
@available(SwiftStdlib 5.6, *)
1111
public distributed actor MyActor {
1212
public typealias ActorSystem = LocalTestingDistributedActorSystem

0 commit comments

Comments
 (0)