SE-0382: Expression Macros

Very cool!

I can certainly see how it would be valuable. Opaque result types as they exist today for functions/properties/subscripts have a notion of identity that is tied to the function/property/subscript returning them, where the identity of the opaque type is determined by that function/property/subscript and any generic arguments to it. That notion of identity doesn't work for macros, because macros get the source code of the arguments and everything that you can access via the macro expansion context, so there's no way to establish identity. That's the logic I followed in banning opaque result types for macros.

However, we could say that every use of a macro that has an opaque result type produces a completely unique opaque type, similar to what happens when we open an existential. From a compiler perspective, we know the opaque result type after macro expansion.

That's all a very long-winded way to say that I think we can lift the restriction, and allow opaque result types for macros. @Joe_Groff does the above seem plausible to you?

I think this is my preferred solution, which we've talked about at various times as being a "join" of the types of each of the returns.

The compiler model here does have some of this information, but it's not being surfaced well to users. When a macro is expanded, the compiler creates an internal buffer containing the source code produced by the macro implementation. If something goes wrong when type-checking that code, the compiler will print an error message pointing into that buffer, with a follow-up note showing the location where the macro expansion was triggered. The result looks something like this (extracted from a compiler test case):

macro:addBlocker:/swift/test/Macros/macro_expand.swift:81:7-81:27:1:4: error: binary operator '-' cannot be applied to two 'OnlyAdds' operands
oa - oa
~~ ^ ~~
/swift/test/Macros/macro_expand.swift:81:7: note: in expansion of macro 'addBlocker' here
  _ = #addBlocker(oa + oa)
      ^~~~~~~~~~~~~~~~~~~~

The addBlocker macro is my silly example which replaces + with -, so the oa - oa code is the result of macro expansion of #addBlocker(oa + oa). That weird macro:addBlocker: file name is the name of the internal buffer, showing that the macro-expanded source code is there in a compiler, but it's only showing one line of it. We can do better here, for example by leveraging the nifty formatter in swift-syntax to show more of the macro expansion buffer in the compiler's output. (This idea just came to me; I haven't tried it yet)

If you're using Xcode, you'll have to look into the build log to see the compiler output. Clang and Swift use a different serialized format for diagnostics when talking to IDEs, so all of this information is lost right now. I have an RFC on the LLVM Discourse to extend that serialized format to include macro-expansion buffers. Swift's compiler already implements it, but IDEs would need to pick up the new APIs to provide a good experience here.

That looks like a bug in the compiler implementation, rather than a limitation of the design.

It's a limitation of the current implementation, thanks!

Thank you for diving deep into result builders-via-macros! It's great to see what can be achieved with macros, what is still out of reach, and where the pain points are.

Sorry 'bout the delay. Answers above.

Nope! With the advent of the new Swift parser, the swift-syntax package is all-Swift and fully standalone.

Doug

10 Likes