@@ -130,7 +130,7 @@ extension DistributedResolvableMacro {
130130 return [ ]
131131 }
132132
133- var isGenericStub = false
133+ var isGenericOverActorSystem = false
134134 var specificActorSystemRequirement : TypeSyntax ?
135135
136136 let accessModifiers = proto. accessControlModifiers
@@ -140,15 +140,15 @@ extension DistributedResolvableMacro {
140140 case . conformanceRequirement( let conformanceReq)
141141 where conformanceReq. leftType. isActorSystem:
142142 specificActorSystemRequirement = conformanceReq. rightType. trimmed
143- isGenericStub = true
143+ isGenericOverActorSystem = true
144144
145145 case . sameTypeRequirement( let sameTypeReq) :
146146 switch sameTypeReq. leftType {
147147 case . type( let type) where type. isActorSystem:
148148 switch sameTypeReq. rightType. trimmed {
149149 case . type( let rightType) :
150150 specificActorSystemRequirement = rightType
151- isGenericStub = false
151+ isGenericOverActorSystem = false
152152
153153 case . expr:
154154 throw DiagnosticsError (
@@ -167,41 +167,78 @@ extension DistributedResolvableMacro {
167167 }
168168 }
169169
170- if isGenericStub, let specificActorSystemRequirement {
171- return [
172- """
173- \( proto. modifiers) distributed actor $ \( proto. name. trimmed) <ActorSystem>: \( proto. name. trimmed) ,
174- Distributed._DistributedActorStub
175- where ActorSystem: \( specificActorSystemRequirement)
176- { }
177- """
178- ]
179- } else if let specificActorSystemRequirement {
180- return [
181- """
182- \( proto. modifiers) distributed actor $ \( proto. name. trimmed) : \( proto. name. trimmed) ,
183- Distributed._DistributedActorStub
184- {
185- \( typealiasActorSystem ( access: accessModifiers, proto, specificActorSystemRequirement) )
186- }
187- """
188- ]
170+ var primaryAssociatedTypes : [ PrimaryAssociatedTypeSyntax ] = [ ]
171+ if let primaryTypes = proto. primaryAssociatedTypeClause? . primaryAssociatedTypes {
172+ primaryAssociatedTypes. append ( contentsOf: primaryTypes)
173+ }
174+
175+ // The $Stub is always generic over the actor system: $Stub<ActorSystem>
176+ var primaryTypeParams : [ String ] = primaryAssociatedTypes. map {
177+ $0. as ( PrimaryAssociatedTypeSyntax . self) !. name. trimmed. text
178+ }
179+
180+ // Don't duplicate the ActorSystem type parameter if it already was declared
181+ // on the protocol as a primary associated type;
182+ // otherwise, add it as first primary associated type.
183+ let actorSystemTypeParam : [ String ]
184+ if primaryTypeParams. contains ( " ActorSystem " ) {
185+ actorSystemTypeParam = [ ]
186+ } else if isGenericOverActorSystem {
187+ actorSystemTypeParam = [ " ActorSystem " ]
189188 } else {
189+ actorSystemTypeParam = [ ]
190+ }
191+
192+ // Prepend the actor system type parameter, as we want it to be the first one
193+ primaryTypeParams = actorSystemTypeParam + primaryTypeParams
194+ let typeParamsClause =
195+ primaryTypeParams. isEmpty ? " " : " < " + primaryTypeParams. joined ( separator: " , " ) + " > "
196+
197+ var whereClause : String = " "
198+ do {
199+ let associatedTypeDecls = proto. associatedTypeDecls
200+ var typeParamConstraints : [ String ] = [ ]
201+ for typeParamName in primaryTypeParams {
202+ if let decl = associatedTypeDecls [ typeParamName] {
203+ if let inheritanceClause = decl. inheritanceClause {
204+ typeParamConstraints. append ( " \( typeParamName) \( inheritanceClause) " )
205+ }
206+ }
207+ }
208+
209+ if isGenericOverActorSystem, let specificActorSystemRequirement {
210+ typeParamConstraints = [ " ActorSystem: \( specificActorSystemRequirement) " ] + typeParamConstraints
211+ }
212+
213+ if !typeParamConstraints. isEmpty {
214+ whereClause += " \n where " + typeParamConstraints. joined ( separator: " , \n " )
215+ }
216+ }
217+
218+ let stubActorBody : String
219+ if isGenericOverActorSystem {
190220 // there may be no `where` clause specifying an actor system,
191221 // but perhaps there is a typealias (or extension with a typealias),
192222 // specifying a concrete actor system so we let this synthesize
193223 // an empty `$Greeter` -- this may fail, or succeed depending on
194224 // surrounding code using a default distributed actor system,
195225 // or extensions providing it.
196- return [
197- """
198- \( proto. modifiers) distributed actor $ \( proto. name. trimmed) : \( proto. name. trimmed) ,
199- Distributed._DistributedActorStub
200- {
201- }
202- """
203- ]
226+ stubActorBody = " "
227+ } else if let specificActorSystemRequirement {
228+ stubActorBody = " \( typealiasActorSystem ( access: accessModifiers, proto, specificActorSystemRequirement) ) "
229+ } else {
230+ stubActorBody = " "
204231 }
232+
233+ return [
234+ """
235+ \( proto. modifiers) distributed actor $ \( proto. name. trimmed) \( raw: typeParamsClause) : \( proto. name. trimmed) ,
236+ Distributed._DistributedActorStub \( raw: whereClause)
237+ {
238+ \( raw: stubActorBody)
239+ }
240+ """
241+ ]
205242 }
206243
207244 private static func typealiasActorSystem( access: DeclModifierListSyntax ,
@@ -253,6 +290,23 @@ extension DeclModifierSyntax {
253290 }
254291}
255292
293+ extension ProtocolDeclSyntax {
294+ var associatedTypeDecls : [ String : AssociatedTypeDeclSyntax ] {
295+ let visitor = AssociatedTypeDeclVisitor ( viewMode: . all)
296+ visitor. walk ( self )
297+ return visitor. associatedTypeDecls
298+ }
299+
300+ final class AssociatedTypeDeclVisitor : SyntaxVisitor {
301+ var associatedTypeDecls : [ String : AssociatedTypeDeclSyntax ] = [ : ]
302+
303+ override func visit( _ node: AssociatedTypeDeclSyntax ) -> SyntaxVisitorContinueKind {
304+ associatedTypeDecls [ node. name. text] = node
305+ return . skipChildren
306+ }
307+ }
308+ }
309+
256310// ===== -----------------------------------------------------------------------
257311// MARK: @Distributed.Resolvable macro errors
258312
0 commit comments