Skip to content

Commit f03ddf7

Browse files
ktosoxedin
andauthored
[Macros] Reproduce issue with peer+extension macro extension's methods not being checked as witnesses (#71717)
Co-authored-by: Pavel Yaskevich <[email protected]>
1 parent b835009 commit f03ddf7

File tree

6 files changed

+147
-113
lines changed

6 files changed

+147
-113
lines changed

lib/AST/NameLookup.cpp

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1844,13 +1844,18 @@ PotentialMacroExpansions PotentialMacroExpansionsInContextRequest::evaluate(
18441844
auto containerDecl = container.getAsDecl();
18451845
forEachPotentialAttachedMacro(containerDecl, MacroRole::Member, nameTracker);
18461846

1847-
// If the container is an extension that was created from an extension macro,
1848-
// look at the nominal declaration to find any extension macros.
1849-
if (auto ext = dyn_cast<ExtensionDecl>(containerDecl)) {
1850-
if (auto nominal = nominalForExpandedExtensionDecl(ext)) {
1851-
forEachPotentialAttachedMacro(
1852-
nominal, MacroRole::Extension, nameTracker);
1853-
}
1847+
// Extension macros on the type or extension.
1848+
{
1849+
NominalTypeDecl *nominal = nullptr;
1850+
// If the container is an extension that was created from an extension
1851+
// macro, look at the nominal declaration to find any extension macros.
1852+
if (auto ext = dyn_cast<ExtensionDecl>(containerDecl))
1853+
nominal = nominalForExpandedExtensionDecl(ext);
1854+
else
1855+
nominal = container.getBaseNominal();
1856+
1857+
if (nominal)
1858+
forEachPotentialAttachedMacro(nominal, MacroRole::Extension, nameTracker);
18541859
}
18551860

18561861
// Peer and freestanding declaration macros.
@@ -1911,15 +1916,20 @@ populateLookupTableEntryFromMacroExpansions(ASTContext &ctx,
19111916
// names match.
19121917
{
19131918
MacroIntroducedNameTracker nameTracker;
1914-
if (auto ext = dyn_cast<ExtensionDecl>(container.getAsDecl())) {
1915-
if (auto nominal = nominalForExpandedExtensionDecl(ext)) {
1916-
forEachPotentialAttachedMacro(nominal, MacroRole::Extension, nameTracker);
1917-
if (nameTracker.shouldExpandForName(name)) {
1918-
(void)evaluateOrDefault(
1919-
ctx.evaluator,
1920-
ExpandExtensionMacros{nominal},
1921-
false);
1922-
}
1919+
NominalTypeDecl *nominal = nullptr;
1920+
// If the container is an extension that was created from an extension
1921+
// macro, look at the nominal declaration to find any extension macros.
1922+
if (auto ext = dyn_cast<ExtensionDecl>(container.getAsDecl()))
1923+
nominal = nominalForExpandedExtensionDecl(ext);
1924+
else
1925+
nominal = container.getBaseNominal();
1926+
1927+
if (nominal) {
1928+
forEachPotentialAttachedMacro(nominal,
1929+
MacroRole::Extension, nameTracker);
1930+
if (nameTracker.shouldExpandForName(name)) {
1931+
(void)evaluateOrDefault(ctx.evaluator, ExpandExtensionMacros{nominal},
1932+
false);
19231933
}
19241934
}
19251935
}

lib/Macros/Sources/SwiftMacros/DistributedProtocolMacro.swift

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ import SwiftOperators
1616
import SwiftSyntaxBuilder
1717

1818
/// Introduces:
19-
/// - `distributed actor $MyDistributedActor<ActorSystem>`
19+
/// - `distributed actor $MyDistributedActor<ActorSystem>: $MyDistributedActor, _DistributedActorStub where ...`
20+
/// - `extension MyDistributedActor where Self: _DistributedActorStub {}`
2021
public struct DistributedProtocolMacro: ExtensionMacro, PeerMacro {
2122
public static func expansion(
2223
of node: AttributeSyntax,
@@ -48,7 +49,7 @@ public struct DistributedProtocolMacro: ExtensionMacro, PeerMacro {
4849

4950
let extensionDecl: DeclSyntax =
5051
"""
51-
extension \(proto.name) {
52+
extension \(proto.name.trimmed) where Self: Distributed._DistributedActorStub {
5253
\(raw: requirementStubs)
5354
}
5455
"""
@@ -68,33 +69,10 @@ public struct DistributedProtocolMacro: ExtensionMacro, PeerMacro {
6869
let serializationRequirementType =
6970
"Codable"
7071

71-
let requirements =
72-
proto.memberBlock.members.map { member in
73-
member.trimmed
74-
}
75-
let requirementStubs = requirements
76-
.map { req in
77-
"""
78-
\(req) {
79-
if #available(SwiftStdlib 5.11, *) {
80-
Distributed._distributedStubFatalError()
81-
} else {
82-
fatalError()
83-
}
84-
}
85-
"""
86-
}.joined(separator: "\n ")
87-
88-
let extensionDecl: DeclSyntax =
89-
"""
90-
extension \(proto.name) where Self: _DistributedActorStub {
91-
\(raw: requirementStubs)
92-
}
93-
"""
94-
9572
let stubActorDecl: DeclSyntax =
9673
"""
97-
distributed actor $\(proto.name)<ActorSystem>: \(proto.name), _DistributedActorStub
74+
distributed actor $\(proto.name.trimmed)<ActorSystem>: \(proto.name.trimmed),
75+
Distributed._DistributedActorStub
9876
where ActorSystem: DistributedActorSystem<any \(raw: serializationRequirementType)>,
9977
ActorSystem.ActorID: \(raw: serializationRequirementType)
10078
{ }

test/Distributed/Macros/distributed_macro_expansion_DistributedProtocol_inheritance.swift

Lines changed: 34 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,7 @@
77
// RUN: %empty-directory(%t)
88
// RUN: %empty-directory(%t-scratch)
99

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

1512
import Distributed
1613

@@ -19,62 +16,48 @@ typealias System = LocalTestingDistributedActorSystem
1916
@_DistributedProtocol
2017
protocol EmptyBase {}
2118

22-
// TODO: allow this?
23-
//@_DistributedProtocol
24-
//extension EmptyBase {}
25-
2619
// @_DistributedProtocol ->
2720
//
28-
// CHECK: @freestanding(declaration)
29-
// CHECK: macro _distributed_stubs_EmptyBase() =
30-
// CHECK: #distributedStubs(
31-
// CHECK: module: "main", protocolName: "EmptyBase",
32-
// CHECK: stubProtocols: []
33-
// CHECK: )
34-
//
35-
// CHECK: // distributed actor $EmptyBase <ActorSystem>: EmptyBase where SerializationRequirement == any Codable {
36-
// CHECK: distributed actor $EmptyBase : EmptyBase {
37-
// CHECK: typealias ActorSystem = LocalTestingDistributedActorSystem // FIXME: remove this
38-
//
39-
// CHECK: #distributedStubs(
40-
// CHECK: module: "main", protocolName: "EmptyBase",
41-
// CHECK: stubProtocols: []
42-
// CHECK: )
21+
// CHECK: distributed actor $EmptyBase<ActorSystem>: EmptyBase,
22+
// CHECK: Distributed._DistributedActorStub
23+
// CHECK: where ActorSystem: DistributedActorSystem<any Codable>,
24+
// CHECK: ActorSystem.ActorID: Codable
25+
// CHECK: {
26+
// CHECK: }
27+
28+
// CHECK: extension EmptyBase where Self: Distributed._DistributedActorStub {
4329
// CHECK: }
4430

31+
// ==== ------------------------------------------------------------------------
32+
4533
@_DistributedProtocol
46-
protocol G3: DistributedActor, EmptyBase where SerializationRequirement == any Codable {
34+
protocol G3<ActorSystem>: DistributedActor, EmptyBase where ActorSystem: DistributedActorSystem<any Codable> {
4735
distributed func get() -> String
4836
distributed func greet(name: String) -> String
4937
}
5038

5139
// @_DistributedProtocol ->
52-
//
53-
// Since we have also the EmptyBase we don't know what names it will introduce,
54-
// so this stubs macro must be "names: arbitrary":
55-
// CHECK: @freestanding(declaration, names: arbitrary)
56-
// CHECK: macro _distributed_stubs_G3() =
57-
// CHECK: #distributedStubs(
58-
// CHECK: module: "main", protocolName: "G3",
59-
// CHECK: stubProtocols: ["EmptyBase"],
60-
// CHECK: "distributed func get() -> String",
61-
// CHECK: "distributed func greet(name: String) -> String"
62-
// CHECK: )
63-
//
64-
// TODO: distributed actor $G3<ActorSystem>: Greeter where SerializationRequirement == any Codable {
65-
// CHECK: distributed actor $G3: G3, EmptyBase {
66-
// TODO: Preferably, we could refer to our own macro like this: #_distributed_stubs_G3
67-
// WORKAROUND:
68-
// CHECK: #distributedStubs(
69-
// CHECK: module: "main", protocolName: "G3",
70-
// CHECK: stubProtocols: ["EmptyBase"],
71-
// CHECK: "distributed func get() -> String",
72-
// CHECK: "distributed func greet(name: String) -> String"
73-
// CHECK: )
74-
// CHECK:
75-
// FIXME: the below cannot find the macro because it's form the same module
76-
// CHECK: // stub inherited members
77-
// CHECK: #_distributed_stubs_EmptyBase
40+
// CHECK: distributed actor $G3<ActorSystem>: G3
41+
// CHECK: Distributed._DistributedActorStub
42+
// CHECK: where ActorSystem: DistributedActorSystem<any Codable>,
43+
// CHECK: ActorSystem.ActorID: Codable
44+
// CHECK: {
45+
// CHECK: }
46+
47+
// CHECK: extension G3 where Self: Distributed._DistributedActorStub {
48+
// CHECK: func get() -> String {
49+
// CHECK: if #available (SwiftStdlib 5.11, *) {
50+
// CHECK: Distributed._distributedStubFatalError()
51+
// CHECK: } else {
52+
// CHECK: fatalError()
53+
// CHECK: }
54+
// CHECK: }
55+
// CHECK: distributed func greet(name: String) -> String {
56+
// CHECK: if #available (SwiftStdlib 5.11, *) {
57+
// CHECK: Distributed._distributedStubFatalError()
58+
// CHECK: } else {
59+
// CHECK: fatalError()
60+
// CHECK: }
61+
// CHECK: }
7862
// CHECK: }
7963

80-
// ==== ------------------------------------------------------------------------

test/Distributed/Macros/distributed_macro_expansion_DistributedProtocol_simple.swift

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,29 +11,26 @@
1111

1212
import Distributed
1313

14-
// FIXME: the errors below are bugs: the added methods should be considered witnesses rdar://123012943
15-
// expected-note@+1{{in expansion of macro '_DistributedProtocol' on protocol 'Greeter' here}}
1614
@_DistributedProtocol
1715
protocol Greeter: DistributedActor where ActorSystem: DistributedActorSystem<any Codable> {
18-
// FIXME: this is a bug
19-
// expected-note@+1{{protocol requires function 'greet(name:)' with type '(String) -> String'}}
2016
distributed func greet(name: String) -> String
2117
}
2218

2319
// @_DistributedProtocol ->
2420

25-
// CHECK: distributed actor $Greeter<ActorSystem>: Greeter, _DistributedActorStub
26-
// CHECK: where ActorSystem: DistributedActorSystem<any Codable>,
27-
// CHECK: ActorSystem.ActorID: Codable
28-
// CHECK: {
29-
// CHECK: }
21+
// CHECK: distributed actor $Greeter<ActorSystem>: Greeter,
22+
// CHECK-NEXT: Distributed._DistributedActorStub
23+
// CHECK-NEXT: where ActorSystem: DistributedActorSystem<any Codable>,
24+
// CHECK-NEXT: ActorSystem.ActorID: Codable
25+
// CHECK-NEXT: {
26+
// CHECK-NEXT: }
3027

31-
// CHECK: extension Greeter {
32-
// CHECK: distributed func greet(name: String) -> String {
33-
// CHECK: if #available (SwiftStdlib 5.11, *) {
34-
// CHECK: Distributed._distributedStubFatalError()
35-
// CHECK: } else {
36-
// CHECK: fatalError()
37-
// CHECK: }
38-
// CHECK: }
39-
// CHECK: }
28+
// CHECK: extension Greeter where Self: Distributed._DistributedActorStub {
29+
// CHECK-NEXT: distributed func greet(name: String) -> String {
30+
// CHECK-NEXT: if #available (SwiftStdlib 5.11, *) {
31+
// CHECK-NEXT: Distributed._distributedStubFatalError()
32+
// CHECK-NEXT: } else {
33+
// CHECK-NEXT: fatalError()
34+
// CHECK-NEXT: }
35+
// CHECK-NEXT: }
36+
// CHECK-NEXT: }

test/Macros/Inputs/syntax_macro_definitions.swift

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2199,7 +2199,55 @@ public struct SingleMemberStubMacro: DeclarationMacro {
21992199
}
22002200
}
22012201

2202-
public struct FakeCodeItemMacro: DeclarationMacro, PeerMacro {
2202+
public struct GenerateStubsForProtocolRequirementsMacro: PeerMacro, ExtensionMacro {
2203+
public static func expansion(
2204+
of node: AttributeSyntax,
2205+
attachedTo declaration: some DeclGroupSyntax,
2206+
providingExtensionsOf type: some TypeSyntaxProtocol,
2207+
conformingTo protocols: [TypeSyntax],
2208+
in context: some MacroExpansionContext
2209+
) throws -> [ExtensionDeclSyntax] {
2210+
guard let proto = declaration.as(ProtocolDeclSyntax.self) else {
2211+
return []
2212+
}
2213+
2214+
let requirements =
2215+
proto.memberBlock.members.map { member in member.trimmed }
2216+
let requirementStubs = requirements
2217+
.map { req in
2218+
"\(req) { fatalError() }"
2219+
}
2220+
.joined(separator: "\n ")
2221+
2222+
let extensionDecl: DeclSyntax =
2223+
"""
2224+
extension \(proto.name) where Self: _TestStub {
2225+
\(raw: requirementStubs)
2226+
}
2227+
"""
2228+
return [extensionDecl.cast(ExtensionDeclSyntax.self)]
2229+
}
2230+
2231+
public static func expansion(
2232+
of node: AttributeSyntax,
2233+
providingPeersOf declaration: some DeclSyntaxProtocol,
2234+
in context: some MacroExpansionContext
2235+
) throws -> [DeclSyntax] {
2236+
guard let proto = declaration.as(ProtocolDeclSyntax.self) else {
2237+
return []
2238+
}
2239+
2240+
return [
2241+
"""
2242+
struct __\(proto.name): \(proto.name), _TestStub {
2243+
init() {}
2244+
}
2245+
"""
2246+
]
2247+
}
2248+
}
2249+
2250+
public struct FakeCodeItemMacro: DeclarationMacro, PeerMacro {
22032251
public static func expansion(
22042252
of node: some FreestandingMacroExpansionSyntax,
22052253
in context: some MacroExpansionContext

test/Macros/macro_expand_synthesized_members.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,21 @@ struct NestedMacroExpansion {}
157157
func callNestedExpansionMember() {
158158
NestedMacroExpansion.member()
159159
}
160+
161+
@attached(peer, names: prefixed(`__`)) // introduces `__GenerateStubsForProtocolRequirements
162+
@attached(extension, names: arbitrary) // introduces `extension GenerateStubsForProtocolRequirements`
163+
macro GenerateStubsForProtocolRequirements() = #externalMacro(module: "MacroDefinition", type: "GenerateStubsForProtocolRequirementsMacro")
164+
165+
protocol _TestStub {} // used by 'GenerateStubsForProtocolRequirements'
166+
167+
@GenerateStubsForProtocolRequirements
168+
protocol MacroExpansionRequirements {
169+
func hello(name: String) -> String
170+
}
171+
// struct __MacroExpansionRequirements: _TestStub where ...
172+
// extension MacroExpansionRequirements where Self: _TestStub ...
173+
174+
func testWitnessStub() {
175+
let stub: any MacroExpansionRequirements = __MacroExpansionRequirements()
176+
_ = stub.hello(name: "Caplin")
177+
}

0 commit comments

Comments
 (0)