[Pitch #2] Expression macros

The struct label here brings up a minor nit with the examples: We don't need to instantiate instances of the macro types, so they ought to be written as uninhabited enums, right? And if so, we probably don't want to use struct as the label—we'd want type or maybe enum instead.

For what it's worth, the vision document originally included the macro kind(s) and various other information in a long argument list on a macro modifier; I asked if we could remove or reformat that information for better readability, and one of the changes Doug made in response was to get the macro kinds from the conformances on the implementation, since it already had to be written there.

From what I can see, we could write the macro kind on the decl, but (a) it would be entirely redundant and (b) we would have to decide if it makes sense to have more than one kind and how that should be written. I'll let Doug chime in on those issues.


On the gripping hand, ExpressionMacro only has one function in it anyway. Do we anticipate that other kinds of macros will require several functions? If not, perhaps we should drop the type entirely, define the macro expansions as free functions, and write the macro kind only in the macro decl, removing the redundancy in the other direction.

// module declaring the macro
expression macro stringify<T>(_: T) -> (T, String) = #externalMacro(module: "ExampleMacros", func: "expandStringify(of:in:)")

// module defining the expansion
public func expandStringify(
  of node: MacroExpansionExprSyntax, in context: inout MacroExpansionContext
) -> ExprSyntax {
  guard let argument = node.argumentList.first?.expression else {
    fatalError("compiler bug: the macro does not have any arguments")
  }

  return "(\(argument), \(literal: argument.description))"
}
5 Likes