Creating a macro for Hashable/Equatable conformance

Hi!

I've been excited to use macros in Swift and started by looking at the examples repo. I used @MyOptionSet as a baseline because it adds conformance to a protocol and @CustomCodable to learn how to find properties decorated with another macro.

With this information I created CustomHashable/CustomHashable.swift at main · JosephDuffy/CustomHashable · GitHub. Running swift test -Xswiftc -Xfrontend -Xswiftc -dump-macro-expansions shows it outputting the generated code I would expect:

@__swiftmacro_19CustomHashableTests0aB26StructWithExcludedProperty0aB0fMm_.swift
------------------------------


func hash(into hasher: inout Hasher) {
    hasher.combine(firstProperty)
    hasher.combine(secondProperty)
}

static func ==(lhs: CustomHashableStructWithExcludedProperty, rhs: CustomHashableStructWithExcludedProperty) -> Bool {
    lhs.firstProperty == rhs.firstProperty
        && lhs.secondProperty == rhs.secondProperty
}

------------------------------

However when I run a test to ensure the excluded property is not included it fails, seemingly because Swift is using the synthesised implementation of Hashable.

If I add a non-hashable property or try to use this macro with a class it fails to compile, so it doesn't seem to be picking up these functions when checking if the type conforms to the protocol.

If the explicit conformance is removed and the type is moved to another module the tests don't compile with the error: global function 'XCTAssertEqual(_:_:_:file:line:)' requires that 'CustomHashableStructWithMultipleProperties' conform to 'Equatable' XCTAssertEqual(value1, value1).

Updating the macro to add a different functions (e.g. static func isEqual) and calling that directly does work, so the new functions are being added to the type.

Is this something that's expected to work? Or maybe there's something silly I've overlooked. I've tried this with swift-DEVELOPMENT-SNAPSHOT-2023-04-27-a and swift-DEVELOPMENT-SNAPSHOT-2023-04-25-a with the same results.

This seems to be improving. With recent snapshots, e.g. swift-DEVELOPMENT-SNAPSHOT-2023-05-14-a, I have been able to use hashValue when Hashable conformance and the hash(into:) function are added by a macro.

However it still will not compile when the implementation of == is added via a macro.

With the release of the Xcode 15 beta I have also created an issue on the Swift repo for this: `==` implementation added in a macro is not considered when checking for `Equatable` conformance · Issue #66348 · apple/swift · GitHub

I believe I just fixed this via Macro operators without global operator lookup 5.9 by DougGregor · Pull Request #66325 · apple/swift · GitHub.

Doug

2 Likes

I have checked this with the swift-5.9-DEVELOPMENT-SNAPSHOT-2023-06-17-a toolchain and I'm still getting the errors, although I'm not sure if those changes were in that specific snapshot.

I'll try it again when the Xcode 15 beta 2 is released.

Thanks for all your work and example around macros, I'm loving it so far!

I'm seeing the same errors in Xcode 15 beta 2 so I think this may still not be working.