mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[Macros] Reproduce issue with peer+extension macro extension's methods not being checked as witnesses (#71717)
Co-authored-by: Pavel Yaskevich <pyaskevich@apple.com>
This commit is contained in:
committed by
GitHub
parent
b835009744
commit
f03ddf728b
@@ -1844,13 +1844,18 @@ PotentialMacroExpansions PotentialMacroExpansionsInContextRequest::evaluate(
|
|||||||
auto containerDecl = container.getAsDecl();
|
auto containerDecl = container.getAsDecl();
|
||||||
forEachPotentialAttachedMacro(containerDecl, MacroRole::Member, nameTracker);
|
forEachPotentialAttachedMacro(containerDecl, MacroRole::Member, nameTracker);
|
||||||
|
|
||||||
// If the container is an extension that was created from an extension macro,
|
// Extension macros on the type or extension.
|
||||||
// look at the nominal declaration to find any extension macros.
|
{
|
||||||
if (auto ext = dyn_cast<ExtensionDecl>(containerDecl)) {
|
NominalTypeDecl *nominal = nullptr;
|
||||||
if (auto nominal = nominalForExpandedExtensionDecl(ext)) {
|
// If the container is an extension that was created from an extension
|
||||||
forEachPotentialAttachedMacro(
|
// macro, look at the nominal declaration to find any extension macros.
|
||||||
nominal, MacroRole::Extension, nameTracker);
|
if (auto ext = dyn_cast<ExtensionDecl>(containerDecl))
|
||||||
}
|
nominal = nominalForExpandedExtensionDecl(ext);
|
||||||
|
else
|
||||||
|
nominal = container.getBaseNominal();
|
||||||
|
|
||||||
|
if (nominal)
|
||||||
|
forEachPotentialAttachedMacro(nominal, MacroRole::Extension, nameTracker);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Peer and freestanding declaration macros.
|
// Peer and freestanding declaration macros.
|
||||||
@@ -1911,15 +1916,20 @@ populateLookupTableEntryFromMacroExpansions(ASTContext &ctx,
|
|||||||
// names match.
|
// names match.
|
||||||
{
|
{
|
||||||
MacroIntroducedNameTracker nameTracker;
|
MacroIntroducedNameTracker nameTracker;
|
||||||
if (auto ext = dyn_cast<ExtensionDecl>(container.getAsDecl())) {
|
NominalTypeDecl *nominal = nullptr;
|
||||||
if (auto nominal = nominalForExpandedExtensionDecl(ext)) {
|
// If the container is an extension that was created from an extension
|
||||||
forEachPotentialAttachedMacro(nominal, MacroRole::Extension, nameTracker);
|
// macro, look at the nominal declaration to find any extension macros.
|
||||||
if (nameTracker.shouldExpandForName(name)) {
|
if (auto ext = dyn_cast<ExtensionDecl>(container.getAsDecl()))
|
||||||
(void)evaluateOrDefault(
|
nominal = nominalForExpandedExtensionDecl(ext);
|
||||||
ctx.evaluator,
|
else
|
||||||
ExpandExtensionMacros{nominal},
|
nominal = container.getBaseNominal();
|
||||||
false);
|
|
||||||
}
|
if (nominal) {
|
||||||
|
forEachPotentialAttachedMacro(nominal,
|
||||||
|
MacroRole::Extension, nameTracker);
|
||||||
|
if (nameTracker.shouldExpandForName(name)) {
|
||||||
|
(void)evaluateOrDefault(ctx.evaluator, ExpandExtensionMacros{nominal},
|
||||||
|
false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ import SwiftOperators
|
|||||||
import SwiftSyntaxBuilder
|
import SwiftSyntaxBuilder
|
||||||
|
|
||||||
/// Introduces:
|
/// Introduces:
|
||||||
/// - `distributed actor $MyDistributedActor<ActorSystem>`
|
/// - `distributed actor $MyDistributedActor<ActorSystem>: $MyDistributedActor, _DistributedActorStub where ...`
|
||||||
|
/// - `extension MyDistributedActor where Self: _DistributedActorStub {}`
|
||||||
public struct DistributedProtocolMacro: ExtensionMacro, PeerMacro {
|
public struct DistributedProtocolMacro: ExtensionMacro, PeerMacro {
|
||||||
public static func expansion(
|
public static func expansion(
|
||||||
of node: AttributeSyntax,
|
of node: AttributeSyntax,
|
||||||
@@ -48,7 +49,7 @@ public struct DistributedProtocolMacro: ExtensionMacro, PeerMacro {
|
|||||||
|
|
||||||
let extensionDecl: DeclSyntax =
|
let extensionDecl: DeclSyntax =
|
||||||
"""
|
"""
|
||||||
extension \(proto.name) {
|
extension \(proto.name.trimmed) where Self: Distributed._DistributedActorStub {
|
||||||
\(raw: requirementStubs)
|
\(raw: requirementStubs)
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
@@ -68,33 +69,10 @@ public struct DistributedProtocolMacro: ExtensionMacro, PeerMacro {
|
|||||||
let serializationRequirementType =
|
let serializationRequirementType =
|
||||||
"Codable"
|
"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 =
|
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)>,
|
where ActorSystem: DistributedActorSystem<any \(raw: serializationRequirementType)>,
|
||||||
ActorSystem.ActorID: \(raw: serializationRequirementType)
|
ActorSystem.ActorID: \(raw: serializationRequirementType)
|
||||||
{ }
|
{ }
|
||||||
|
|||||||
@@ -7,10 +7,7 @@
|
|||||||
// RUN: %empty-directory(%t)
|
// RUN: %empty-directory(%t)
|
||||||
// RUN: %empty-directory(%t-scratch)
|
// 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
|
// RUN: %target-swift-frontend -typecheck -verify -disable-availability-checking -plugin-path %swift-plugin-dir -I %t -dump-macro-expansions %s 2>&1 | %FileCheck %s
|
||||||
|
|
||||||
// FIXME: inheritance tests limited because cannot refer to any generated macro from the same module...
|
|
||||||
// XFAIL: *
|
|
||||||
|
|
||||||
import Distributed
|
import Distributed
|
||||||
|
|
||||||
@@ -19,62 +16,48 @@ typealias System = LocalTestingDistributedActorSystem
|
|||||||
@_DistributedProtocol
|
@_DistributedProtocol
|
||||||
protocol EmptyBase {}
|
protocol EmptyBase {}
|
||||||
|
|
||||||
// TODO: allow this?
|
|
||||||
//@_DistributedProtocol
|
|
||||||
//extension EmptyBase {}
|
|
||||||
|
|
||||||
// @_DistributedProtocol ->
|
// @_DistributedProtocol ->
|
||||||
//
|
//
|
||||||
// CHECK: @freestanding(declaration)
|
// CHECK: distributed actor $EmptyBase<ActorSystem>: EmptyBase,
|
||||||
// CHECK: macro _distributed_stubs_EmptyBase() =
|
// CHECK: Distributed._DistributedActorStub
|
||||||
// CHECK: #distributedStubs(
|
// CHECK: where ActorSystem: DistributedActorSystem<any Codable>,
|
||||||
// CHECK: module: "main", protocolName: "EmptyBase",
|
// CHECK: ActorSystem.ActorID: Codable
|
||||||
// CHECK: stubProtocols: []
|
// CHECK: {
|
||||||
// CHECK: )
|
// CHECK: }
|
||||||
//
|
|
||||||
// CHECK: // distributed actor $EmptyBase <ActorSystem>: EmptyBase where SerializationRequirement == any Codable {
|
// CHECK: extension EmptyBase where Self: Distributed._DistributedActorStub {
|
||||||
// CHECK: distributed actor $EmptyBase : EmptyBase {
|
|
||||||
// CHECK: typealias ActorSystem = LocalTestingDistributedActorSystem // FIXME: remove this
|
|
||||||
//
|
|
||||||
// CHECK: #distributedStubs(
|
|
||||||
// CHECK: module: "main", protocolName: "EmptyBase",
|
|
||||||
// CHECK: stubProtocols: []
|
|
||||||
// CHECK: )
|
|
||||||
// CHECK: }
|
// CHECK: }
|
||||||
|
|
||||||
|
// ==== ------------------------------------------------------------------------
|
||||||
|
|
||||||
@_DistributedProtocol
|
@_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 get() -> String
|
||||||
distributed func greet(name: String) -> String
|
distributed func greet(name: String) -> String
|
||||||
}
|
}
|
||||||
|
|
||||||
// @_DistributedProtocol ->
|
// @_DistributedProtocol ->
|
||||||
//
|
// CHECK: distributed actor $G3<ActorSystem>: G3
|
||||||
// Since we have also the EmptyBase we don't know what names it will introduce,
|
// CHECK: Distributed._DistributedActorStub
|
||||||
// so this stubs macro must be "names: arbitrary":
|
// CHECK: where ActorSystem: DistributedActorSystem<any Codable>,
|
||||||
// CHECK: @freestanding(declaration, names: arbitrary)
|
// CHECK: ActorSystem.ActorID: Codable
|
||||||
// CHECK: macro _distributed_stubs_G3() =
|
// CHECK: {
|
||||||
// CHECK: #distributedStubs(
|
// CHECK: }
|
||||||
// CHECK: module: "main", protocolName: "G3",
|
|
||||||
// CHECK: stubProtocols: ["EmptyBase"],
|
// CHECK: extension G3 where Self: Distributed._DistributedActorStub {
|
||||||
// CHECK: "distributed func get() -> String",
|
// CHECK: func get() -> String {
|
||||||
// CHECK: "distributed func greet(name: String) -> String"
|
// CHECK: if #available (SwiftStdlib 5.11, *) {
|
||||||
// CHECK: )
|
// CHECK: Distributed._distributedStubFatalError()
|
||||||
//
|
// CHECK: } else {
|
||||||
// TODO: distributed actor $G3<ActorSystem>: Greeter where SerializationRequirement == any Codable {
|
// CHECK: fatalError()
|
||||||
// CHECK: distributed actor $G3: G3, EmptyBase {
|
// CHECK: }
|
||||||
// TODO: Preferably, we could refer to our own macro like this: #_distributed_stubs_G3
|
// CHECK: }
|
||||||
// WORKAROUND:
|
// CHECK: distributed func greet(name: String) -> String {
|
||||||
// CHECK: #distributedStubs(
|
// CHECK: if #available (SwiftStdlib 5.11, *) {
|
||||||
// CHECK: module: "main", protocolName: "G3",
|
// CHECK: Distributed._distributedStubFatalError()
|
||||||
// CHECK: stubProtocols: ["EmptyBase"],
|
// CHECK: } else {
|
||||||
// CHECK: "distributed func get() -> String",
|
// CHECK: fatalError()
|
||||||
// CHECK: "distributed func greet(name: String) -> String"
|
// CHECK: }
|
||||||
// CHECK: )
|
// 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: }
|
// CHECK: }
|
||||||
|
|
||||||
// ==== ------------------------------------------------------------------------
|
|
||||||
|
|||||||
@@ -11,29 +11,26 @@
|
|||||||
|
|
||||||
import Distributed
|
import Distributed
|
||||||
|
|
||||||
// FIXME: the errors below are bugs: the added methods should be considered witnesses rdar://123012943
|
|
||||||
// expected-note@+1{{in expansion of macro '_DistributedProtocol' on protocol 'Greeter' here}}
|
|
||||||
@_DistributedProtocol
|
@_DistributedProtocol
|
||||||
protocol Greeter: DistributedActor where ActorSystem: DistributedActorSystem<any Codable> {
|
protocol Greeter: DistributedActor where ActorSystem: DistributedActorSystem<any Codable> {
|
||||||
// FIXME: this is a bug
|
|
||||||
// expected-note@+1{{protocol requires function 'greet(name:)' with type '(String) -> String'}}
|
|
||||||
distributed func greet(name: String) -> String
|
distributed func greet(name: String) -> String
|
||||||
}
|
}
|
||||||
|
|
||||||
// @_DistributedProtocol ->
|
// @_DistributedProtocol ->
|
||||||
|
|
||||||
// CHECK: distributed actor $Greeter<ActorSystem>: Greeter, _DistributedActorStub
|
// CHECK: distributed actor $Greeter<ActorSystem>: Greeter,
|
||||||
// CHECK: where ActorSystem: DistributedActorSystem<any Codable>,
|
// CHECK-NEXT: Distributed._DistributedActorStub
|
||||||
// CHECK: ActorSystem.ActorID: Codable
|
// CHECK-NEXT: where ActorSystem: DistributedActorSystem<any Codable>,
|
||||||
// CHECK: {
|
// CHECK-NEXT: ActorSystem.ActorID: Codable
|
||||||
// CHECK: }
|
// CHECK-NEXT: {
|
||||||
|
// CHECK-NEXT: }
|
||||||
|
|
||||||
// CHECK: extension Greeter {
|
// CHECK: extension Greeter where Self: Distributed._DistributedActorStub {
|
||||||
// CHECK: distributed func greet(name: String) -> String {
|
// CHECK-NEXT: distributed func greet(name: String) -> String {
|
||||||
// CHECK: if #available (SwiftStdlib 5.11, *) {
|
// CHECK-NEXT: if #available (SwiftStdlib 5.11, *) {
|
||||||
// CHECK: Distributed._distributedStubFatalError()
|
// CHECK-NEXT: Distributed._distributedStubFatalError()
|
||||||
// CHECK: } else {
|
// CHECK-NEXT: } else {
|
||||||
// CHECK: fatalError()
|
// CHECK-NEXT: fatalError()
|
||||||
// CHECK: }
|
// CHECK-NEXT: }
|
||||||
// CHECK: }
|
// CHECK-NEXT: }
|
||||||
// CHECK: }
|
// CHECK-NEXT: }
|
||||||
|
|||||||
@@ -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(
|
public static func expansion(
|
||||||
of node: some FreestandingMacroExpansionSyntax,
|
of node: some FreestandingMacroExpansionSyntax,
|
||||||
in context: some MacroExpansionContext
|
in context: some MacroExpansionContext
|
||||||
|
|||||||
@@ -157,3 +157,21 @@ struct NestedMacroExpansion {}
|
|||||||
func callNestedExpansionMember() {
|
func callNestedExpansionMember() {
|
||||||
NestedMacroExpansion.member()
|
NestedMacroExpansion.member()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@attached(peer, names: prefixed(`__`)) // 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")
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user