Is it possible to for an extension macro to extend an already-declared type?

Usually extension macros are attached macros, e.g.

// Declaration:
@attached(extension, conformances: CustomDebugStringConvertible)
@attached(member, names: named(debugDescription))
public macro OptionSetDebugDescription() = #externalMacro(module: "MyMacros", type: "OptionSetDebugDescriptionMacro")

// Usage:
@OptionSetDebugDescription
struct OpenFlags: OptionSet { ... }

My macro works well if I'm defining a new type like that. However, I'd like to use it on pre-existing types, typically defined in C using NS_OPTIONS, e.g.:

typedef NS_OPTIONS(NSUInteger, OpenFlags) {
	OpenFlagsReadable = FREAD,
	OpenFlagsWritable = FWRITE,
};

Neither of these work:

import struct MyCLibrary.OpenFlags

@OptionSetDebugDescription
struct OpenFlags: OptionSet {} // This defines a new type, unrelated to `MyCLibrary`

@OptionSetDebugDescription
extension OpenFlags {} // 'extension' macro cannot be attached to extension

And a freestanding macro also doesn't work. Even if you get the expansion to work correctly:

@freestanding(declaration, conformances: CustomDebugStringConvertible)
public macro OptionSetDebugDescription<T: OptionSet>(to _: T.Type) =
	#externalMacro(module: "MyMacros", type: "OptionSetDebugDescriptionMacro")

#OptionSetDebugDescription(to: OpenFlags.self)

// Expands to:
// extension OpenFlags: CustomDebugStringConvertible {
//    var debugDescription: String { ... }
//}

... the compiler will block it:

Macro expansion cannot introduce extension

This intentional limitation is why conformance macros were added in the first place, but they seem limited to new type declarations only.

Am I missing something?

2 Likes

Ah so here's one workaround I figured out. I can make the extension myself, and use a freestanding macro to only declare the var debugDescription: String member within:

@freestanding(declaration, names: named(debugDescription))
public macro SynthesizeDebugDescription() = #externalMacro(module: "MyMacroMacros", type: "SynthesizeDebugDescriptionMacro")

extension OpenFlags: CustomDebugStringConvertible {
	#SynthesizeDebugDescription
}
2 Likes

I believe you can put attached conformance macros on an extension as well You can similarly write the protocol explicitly but still use a member macro for the implementation

@OptionSetDebugDescription
extension OpenFlags: CustomDebugStringConvertible {}
2 Likes