Skip to content

Commit

Permalink
reproduce witness issue with peer+extension macros
Browse files Browse the repository at this point in the history
  • Loading branch information
ktoso committed Feb 19, 2024
1 parent 9eb9a2e commit 28136f6
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 79 deletions.
32 changes: 5 additions & 27 deletions lib/Macros/Sources/SwiftMacros/DistributedProtocolMacro.swift
Expand Up @@ -16,7 +16,8 @@ import SwiftOperators
import SwiftSyntaxBuilder

/// Introduces:
/// - `distributed actor $MyDistributedActor<ActorSystem>`
/// - `distributed actor $MyDistributedActor<ActorSystem>: $MyDistributedActor, _DistributedActorStub where ...`
/// - `extension MyDistributedActor where Self: _DistributedActorStub {}`
public struct DistributedProtocolMacro: ExtensionMacro, PeerMacro {
public static func expansion(
of node: AttributeSyntax,
Expand Down Expand Up @@ -48,7 +49,7 @@ public struct DistributedProtocolMacro: ExtensionMacro, PeerMacro {

let extensionDecl: DeclSyntax =
"""
extension \(proto.name) {
extension \(proto.name.trimmed) where Self: Distributed._DistributedActorStub {
\(raw: requirementStubs)
}
"""
Expand All @@ -68,33 +69,10 @@ public struct DistributedProtocolMacro: ExtensionMacro, PeerMacro {
let serializationRequirementType =
"Codable"

let requirements =
proto.memberBlock.members.map { member in
member.trimmed
}
let requirementStubs = requirements
.map { req in
"""
\(req) {
if #available(SwiftStdlib 5.11, *) {
Distributed._distributedStubFatalError()
} else {
fatalError()
}
}
"""
}.joined(separator: "\n ")

let extensionDecl: DeclSyntax =
"""
extension \(proto.name) where Self: _DistributedActorStub {
\(raw: requirementStubs)
}
"""

let stubActorDecl: DeclSyntax =
"""
distributed actor $\(proto.name)<ActorSystem>: \(proto.name), _DistributedActorStub
distributed actor $\(proto.name.trimmed)<ActorSystem>: \(proto.name.trimmed),
Distributed._DistributedActorStub
where ActorSystem: DistributedActorSystem<any \(raw: serializationRequirementType)>,
ActorSystem.ActorID: \(raw: serializationRequirementType)
{ }
Expand Down
1 change: 1 addition & 0 deletions stdlib/public/Distributed/DistributedMacros.swift
Expand Up @@ -17,6 +17,7 @@ import _Concurrency

#if $Macros

// FIXME: the fact tha this is peer and extension makes the extension's methods not found as witnesses
@attached(peer, names: prefixed(`$`)) // provides $Greeter concrete stub type
@attached(extension, names: arbitrary) // provides extension for Greeter & _DistributedActorStub
public macro _DistributedProtocol() =
Expand Down
Expand Up @@ -7,10 +7,7 @@
// RUN: %empty-directory(%t)
// RUN: %empty-directory(%t-scratch)

// RUN: %target-swift-frontend -typecheck -verify -disable-availability-checking -plugin-path %swift-plugin-dir -I %t -dump-macro-expansions %s -dump-macro-expansions 2>&1 | %FileCheck %s

// FIXME: inheritance tests limited because cannot refer to any generated macro from the same module...
// XFAIL: *
// RUN: %target-swift-frontend -typecheck -verify -disable-availability-checking -plugin-path %swift-plugin-dir -I %t -dump-macro-expansions %s 2>&1 | %FileCheck %s

import Distributed

Expand All @@ -19,62 +16,48 @@ typealias System = LocalTestingDistributedActorSystem
@_DistributedProtocol
protocol EmptyBase {}

// TODO: allow this?
//@_DistributedProtocol
//extension EmptyBase {}

// @_DistributedProtocol ->
//
// CHECK: @freestanding(declaration)
// CHECK: macro _distributed_stubs_EmptyBase() =
// CHECK: #distributedStubs(
// CHECK: module: "main", protocolName: "EmptyBase",
// CHECK: stubProtocols: []
// CHECK: )
//
// CHECK: // distributed actor $EmptyBase <ActorSystem>: EmptyBase where SerializationRequirement == any Codable {
// CHECK: distributed actor $EmptyBase : EmptyBase {
// CHECK: typealias ActorSystem = LocalTestingDistributedActorSystem // FIXME: remove this
//
// CHECK: #distributedStubs(
// CHECK: module: "main", protocolName: "EmptyBase",
// CHECK: stubProtocols: []
// CHECK: )
// CHECK: distributed actor $EmptyBase<ActorSystem>: EmptyBase,
// CHECK: Distributed._DistributedActorStub
// CHECK: where ActorSystem: DistributedActorSystem<any Codable>,
// CHECK: ActorSystem.ActorID: Codable
// CHECK: {
// CHECK: }

// CHECK: extension EmptyBase where Self: Distributed._DistributedActorStub {
// CHECK: }

// ==== ------------------------------------------------------------------------

@_DistributedProtocol
protocol G3: DistributedActor, EmptyBase where SerializationRequirement == any Codable {
protocol G3<ActorSystem>: DistributedActor, EmptyBase where ActorSystem: DistributedActorSystem<any Codable> {
distributed func get() -> String
distributed func greet(name: String) -> String
}

// @_DistributedProtocol ->
//
// Since we have also the EmptyBase we don't know what names it will introduce,
// so this stubs macro must be "names: arbitrary":
// CHECK: @freestanding(declaration, names: arbitrary)
// CHECK: macro _distributed_stubs_G3() =
// CHECK: #distributedStubs(
// CHECK: module: "main", protocolName: "G3",
// CHECK: stubProtocols: ["EmptyBase"],
// CHECK: "distributed func get() -> String",
// CHECK: "distributed func greet(name: String) -> String"
// CHECK: )
//
// TODO: distributed actor $G3<ActorSystem>: Greeter where SerializationRequirement == any Codable {
// CHECK: distributed actor $G3: G3, EmptyBase {
// TODO: Preferably, we could refer to our own macro like this: #_distributed_stubs_G3
// WORKAROUND:
// CHECK: #distributedStubs(
// CHECK: module: "main", protocolName: "G3",
// CHECK: stubProtocols: ["EmptyBase"],
// CHECK: "distributed func get() -> String",
// CHECK: "distributed func greet(name: String) -> String"
// CHECK: )
// CHECK:
// FIXME: the below cannot find the macro because it's form the same module
// CHECK: // stub inherited members
// CHECK: #_distributed_stubs_EmptyBase
// CHECK: distributed actor $G3<ActorSystem>: G3
// CHECK: Distributed._DistributedActorStub
// CHECK: where ActorSystem: DistributedActorSystem<any Codable>,
// CHECK: ActorSystem.ActorID: Codable
// CHECK: {
// CHECK: }

// CHECK: extension G3 where Self: Distributed._DistributedActorStub {
// CHECK: func get() -> String {
// CHECK: if #available (SwiftStdlib 5.11, *) {
// CHECK: Distributed._distributedStubFatalError()
// CHECK: } else {
// CHECK: fatalError()
// CHECK: }
// CHECK: }
// CHECK: distributed func greet(name: String) -> String {
// CHECK: if #available (SwiftStdlib 5.11, *) {
// CHECK: Distributed._distributedStubFatalError()
// CHECK: } else {
// CHECK: fatalError()
// CHECK: }
// CHECK: }
// CHECK: }

// ==== ------------------------------------------------------------------------
50 changes: 49 additions & 1 deletion test/Macros/Inputs/syntax_macro_definitions.swift
Expand Up @@ -2199,7 +2199,55 @@ public struct SingleMemberStubMacro: DeclarationMacro {
}
}

public struct FakeCodeItemMacro: DeclarationMacro, PeerMacro {
public struct GenerateStubsForProtocolRequirementsMacro: PeerMacro, ExtensionMacro {
public static func expansion(
of node: AttributeSyntax,
attachedTo declaration: some DeclGroupSyntax,
providingExtensionsOf type: some TypeSyntaxProtocol,
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [ExtensionDeclSyntax] {
guard let proto = declaration.as(ProtocolDeclSyntax.self) else {
return []
}

let requirements =
proto.memberBlock.members.map { member in member.trimmed }
let requirementStubs = requirements
.map { req in
"\(req) { fatalError() }"
}
.joined(separator: "\n ")

let extensionDecl: DeclSyntax =
"""
extension \(proto.name) where Self: _TestStub {
\(raw: requirementStubs)
}
"""
return [extensionDecl.cast(ExtensionDeclSyntax.self)]
}

public static func expansion(
of node: AttributeSyntax,
providingPeersOf declaration: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
guard let proto = declaration.as(ProtocolDeclSyntax.self) else {
return []
}

return [
"""
struct __\(proto.name): \(proto.name), _TestStub {
init() {}
}
"""
]
}
}

public struct FakeCodeItemMacro: DeclarationMacro, PeerMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
Expand Down
17 changes: 17 additions & 0 deletions test/Macros/macro_expand_synthesized_members.swift
Expand Up @@ -157,3 +157,20 @@ struct NestedMacroExpansion {}
func callNestedExpansionMember() {
NestedMacroExpansion.member()
}

@attached(peer, names: arbitrary) // introduces `__GenerateStubsForProtocolRequirements
@attached(extension, names: arbitrary) // introduces `extension GenerateStubsForProtocolRequirements`
macro GenerateStubsForProtocolRequirements() = #externalMacro(module: "MacroDefinition", type: "GenerateStubsForProtocolRequirementsMacro")
protocol _TestStub {} // used by 'GenerateStubsForProtocolRequirements'

@GenerateStubsForProtocolRequirements
protocol MacroExpansionRequirements {
func hello(name: String) -> String
}
// struct __MacroExpansionRequirements: _TestStub where ...
// extension MacroExpansionRequirements where Self: _TestStub ...

func testWitnessStub() {
let stub: any MacroExpansionRequirements = __MacroExpansionRequirements()
_ = stub.hello(name: "Caplin")
}

0 comments on commit 28136f6

Please sign in to comment.