"Macro expansion cannot introduce extension"?

Is there a way for an attached macro to extend a library type? I tried defining an extension in a peer macro but quickly hit the error:

:stop_sign: Macro expansion cannot introduce extension

An example of what would be nice to be possible:

// In library:
public protocol Foo { … }

// In user land:
@Fooable struct Bar { … }

// Generates conformance:
extension Bar: Foo { … }

// Generates peer:
extension Foo where Self == Bar {  // 🛑 Not possible?
  static let bar = Self(…)

Or even more simply:

// In library:
public struct Foo<A> { … }

// In user land:
@Fooable struct Bar { … }

// Generates peer:
extension Foo<Bar> { … }

Is this error just in how I'm holding things? A limitation of macros? If it's a limitation, is it temporary and could the limitation be lifted?


In the pitch for macros, it says that extensions are not allowed, so I think this was an element of the design.

Ah yeah, from here:

extension declarations can never be produced by a macro. The effect of an extension declaration is wide-ranging, with the ability to add conformances, members, and so on. These capabilities are meant to be introduced in a more fine-grained manner.

I think it's understandable to prohibit arbitrary extensions, but it seems perfectly reasonable to allow extensions for predefined types that ship with the same library that ships the macro. Does another macro provide the functionality for adding members to a library type? Should there be one?


You could make a macro and then apply it to an extension.

extension Int {}

extension Double {}

Seems like an unfortunate end-user experience to require two steps:

@Fooable struct Bar {}

@AddFoo(Bar.self) extension Foo {}

An important consideration for macros is that their effect on name lookup is constrained, so we don't need to run all of the macros in the program in order to serve code completion requests, track incremental compilation updates, resolve member references, and so on. When a macro can only attach members to the type it's applied to, then we only have to run the macro if we're looking for members of the annotated type. If we were going to lift that restriction, I think there'd still have to be a way for the macro declaration to specify what other types it adds members to.


That seems to be completely reasonable to me. In this case the package provides both the macro and the type it wants to extend, so it would be easy to provide this type to the macro.

Something else I've noticed is that default arguments don't appear to be provided to macros...is this a bug?

macro stringify<T>(_ value: T, valueType: T.Type = T.self) -> (T, String)

#stringify(1 + 2)  // macro isn't passed `valueType: Int.self`

It's discussed over at [SE-0382] Default arguments for macros and macro implementations.



This sounds like a good solution!

I'm wondering if there are any temporary workarounds for adding functions from one type to another via an extension.

For example, I'd like to take the functions from a protocol and add them to an existing type (provided by my library) as such:

protocol Foo {
    func one()
    func two() -> String
    func three() -> Int

extension Provider {
    // `@Provides<Foo>` would synthesize default implementations for `one()`, `two()`, and `three()`.

However, in the macro expansion, I can't access the actual functions on Foo, since they are defined outside the macro context (though correct me if I'm wrong here!).

Ideally I could just add the macro to the protocol itself, however that would require generating extension Provider.

Another option would be to generate a new type struct FooProvider for each protocol, but I'd like to try to keep the logic isolated to that single, library-provided type Provider.

1 Like

Just wanted to mention that I'm hitting this limitation as well and hope that it gets lifted in the future. My use case is basically this, where each implementation of a protocol needs a corresponding ID declaration:

struct SomeAsset: Asset {
	// implementation

extension AssetID {
	static let someAsset = AssetID()

I'd love to be able to attach a macro to each Asset implementation to generate the ID automatically.


I am running this issue too. If the macro can generate extension, my issues here Compilation issue for code generated by Swift Macro for Equatable could be resolved easily. But now I cannot figure out a solution for it.