Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type generated by peer macro can't conform to Equatable #68683

Open
KeithBauerANZ opened this issue Sep 21, 2023 · 1 comment
Open

Type generated by peer macro can't conform to Equatable #68683

KeithBauerANZ opened this issue Sep 21, 2023 · 1 comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. triage needed This issue needs more specific labels

Comments

@KeithBauerANZ
Copy link

Description

I want to use a peer macro to create a new type that conforms to the same protocols as the type it's attached to. This seems to work for Hashable, Encodable, Decodable, but when I try to implement Equatable, I get compiler errors. Here's a reduction of the problem:

struct Inequatable {}

@Duplicate
struct S: Equatable {
    let preventSynthesis = Inequatable()
    static func == (lhs: Self, rhs: Self) -> Bool {
        true
    }
}

And this is what the Duplicate macro expands to:

struct S_Duplicate: Equatable {
    let preventSynthesis = Inequatable()
    static func == (lhs: Self, rhs: Self) -> Bool {
        true
    }
}

I get: error: type 'S_Duplicate' does not conform to protocol 'Equatable'

For completeness, here's my stupid macro:

public struct DuplicateMacro: PeerMacro {
    public static func expansion(
        of node: AttributeSyntax,
        providingPeersOf declaration: some DeclSyntaxProtocol,
        in context: some MacroExpansionContext
    ) throws -> [DeclSyntax] {
        var dup = declaration.as(StructDeclSyntax.self)!
        dup.attributes = [] // remove the macro to prevent recursion
        // dup.inheritanceClause = nil // remove the Equatable conformance (everything will compile, but S_Duplicate won't actually be Equatable)
        dup.name = TokenSyntax(.identifier("\(dup.name.text)_Duplicate"), presence: .present)
        return [dup.as(DeclSyntax.self)!]
    }
}

@main
struct MacroEquatablePlugin: CompilerPlugin {
    let providingMacros: [Macro.Type] = [
        DuplicateMacro.self,
    ]
}

and its declaration:

@attached(peer, names: suffixed(_Duplicate))
public macro Duplicate() = #externalMacro(module: "MacroEquatableMacros", type: "DuplicateMacro")

Steps to reproduce

Use Xcode's "new package" to create a macro package. Delete the unit tests.

Replace the macro declaration, macro implementation, and client code with the above snippets.

Compile (or rather, fail to). Full error output:

@__swiftmacro_20MacroEquatableClient1S9DuplicatefMp_.swift:1:8: error: type 'S_Duplicate' does not conform to protocol 'Equatable'
struct S_Duplicate: Equatable {
       ^
/Users/<me>/Source/SwiftReductions/MacroEquatable/Sources/MacroEquatableClient/main.swift:6:1: note: in expansion of macro 'Duplicate' here
struct S: Equatable {
^~~~~~~~~~~~~~~~~~~~~
/Users/<me>/Source/SwiftReductions/MacroEquatable/Sources/MacroEquatableClient/main.swift:6:1: note: in expansion of macro 'Duplicate' here
struct S: Equatable {
^~~~~~~~~~~~~~~~~~~~~
@__swiftmacro_20MacroEquatableClient1S9DuplicatefMp_.swift:2:9: note: stored property type 'Inequatable' does not conform to protocol 'Equatable', preventing synthesized conformance of 'S_Duplicate' to 'Equatable'
    let preventSynthesis = Inequatable()
        ^
/Users/<me>/Source/SwiftReductions/MacroEquatable/Sources/MacroEquatableClient/main.swift:6:1: note: in expansion of macro 'Duplicate' here
struct S: Equatable {
^~~~~~~~~~~~~~~~~~~~~
/Users/<me>/Source/SwiftReductions/MacroEquatable/Sources/MacroEquatableClient/main.swift:6:1: note: in expansion of macro 'Duplicate' here
struct S: Equatable {
^~~~~~~~~~~~~~~~~~~~~
Swift.==:1:24: note: candidate would match if 'S_Duplicate' conformed to 'RawRepresentable'
@inlinable public func == <T>(lhs: T, rhs: T) -> Bool where T : RawRepresentable, T.RawValue : Equatable
                       ^
Swift.FloatingPoint:2:24: note: candidate would match if 'S_Duplicate' conformed to 'FloatingPoint'
    public static func == (lhs: Self, rhs: Self) -> Bool
                       ^
Swift.BinaryInteger:2:24: note: candidate would match if 'S_Duplicate' conformed to 'BinaryInteger'
    public static func == <Other>(lhs: Self, rhs: Other) -> Bool where Other : BinaryInteger
                       ^
Swift._Pointer:2:24: note: candidate would match if 'S_Duplicate' conformed to '_Pointer'
    public static func == (lhs: Self, rhs: Self) -> Bool
                       ^
Swift._Pointer:3:35: note: candidate would match if 'S_Duplicate' conformed to '_Pointer'
    @inlinable public static func == <Other>(lhs: Self, rhs: Other) -> Bool where Other : _Pointer
                                  ^
Swift.Strideable:3:35: note: candidate would match if 'S_Duplicate' conformed to 'Strideable'
    @inlinable public static func == (x: Self, y: Self) -> Bool
                                  ^
Swift.StringProtocol:2:35: note: candidate would match if 'S_Duplicate' conformed to 'StringProtocol'
    @inlinable public static func == <RHS>(lhs: Self, rhs: RHS) -> Bool where RHS : StringProtocol
                                  ^
Swift.SIMD:4:24: note: candidate would match if 'S_Duplicate' conformed to 'SIMD'
    public static func == (a: Self, b: Self) -> Bool
                       ^
@__swiftmacro_20MacroEquatableClient1S9DuplicatefMp_.swift:1:8: note: do you want to add protocol stubs?
struct S_Duplicate: Equatable {
       ^
/Users/<me>/Source/SwiftReductions/MacroEquatable/Sources/MacroEquatableClient/main.swift:6:1: note: in expansion of macro 'Duplicate' here
struct S: Equatable {
^~~~~~~~~~~~~~~~~~~~~
/Users/<me>/Source/SwiftReductions/MacroEquatable/Sources/MacroEquatableClient/main.swift:6:1: note: in expansion of macro 'Duplicate' here
struct S: Equatable {
^~~~~~~~~~~~~~~~~~~~~

Expected behavior

I expect my macro-generated struct to be able to conform to Equatable

Environment
$ swiftc --version

swift-driver version: 1.87.1 Apple Swift version 5.9 (swiftlang-5.9.0.128.108 clang-1500.0.40.1)
Target: arm64-apple-macosx13.0

$ xcodebuild -version

Xcode 15.0
Build version 15A240d

@KeithBauerANZ KeithBauerANZ added bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. triage needed This issue needs more specific labels labels Sep 21, 2023
@KeithBauerANZ
Copy link
Author

This may be related to, or a duplicate of, #68515

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. triage needed This issue needs more specific labels
Projects
None yet
Development

No branches or pull requests

1 participant