Files
Henrik G. Olsson d74850c69e [Macros] add a compiler internal macro kind
This adds a new kind of macro to the macro infrastructure. This macro
kind is internal to the compiler, in both implementation and use: the
macros can only be implemented as part of the compiler, and they can
only by invoked by the compiler.

It is intended solely as an implementation detail for synthesizing decls
in the compiler, and in most cases won't be visible to the user at all.
Other methods of synthesizing decls exist in the compiler, but unlike
macros, which generate a string to be treated as a virtual file and
parsed, these methods generate AST nodes directly. This has the downside
of preventing source locations, since source locations are a thin
abstraction over a `char*` into the source buffer: there's no way to
provide a source location for something that wasn't parsed from a
string. For less trivial synthesized decls it's beneficial to be less
"magical". Macro expansions can be stepped through in the debugger,
which is important for decls that may trap or otherwise contain control
flow. They can also be expanded in IDEs, allowing users to jump to
definition and see where the synthesized decl originated and figure out
why it exists, and how it works.

Since macros already have all the infrastructure set up for dealing with
this, both in the compiler and in SourceKit, we may as well reuse that
for these compiler synthesized decls.

`@_SwiftifyImport` used a macro approach, but used plain Swift macros
placed in the standard library.  This has the unfortunate side-effect of
allowing the compiler and the macro implementation fall out of sync,
adding friction to updating the macro implementation since it must be
both backward and forward compatible with newer and older compilers. By
statically linking the macro implementation in the compiler, and
synthesizing the macro declaration node, the compiler can fully control
implementation, interface and invocation.

Another lesson learnt from `@_SwiftifyImport` was the limitations of not
being able to access semantic information (most importantly, types),
since Swift macros operate purely on syntax. ClangImporter would have to
pre-compute information, generate a string parameter, and pass that to
the macro, for every piece of semantic information required by the
macro. This is unwieldy, splits implementation between the compiler and
the macro more than necessary, and requires eagerly computing the info
regardless of whether the macro ends up expanded or not. One such
example is `counted_by`'s count expression, which ClangImporter converts
from a C expression to a string in Swift syntax, and passes that string
along to the macro, even though generating Swift syntax is the macro's
whole job. By making the macro implementation part of the compiler
itself, we can simply pass the AST node to the macro implementation, and
it can do all the semantic lookups it wants. It can also reuse utilities
in the compiler, e.g. for determining whether a variable name requires
escaping, or how to merge availability attributes, instead of
reimplementing it purely based on syntactic information.

While `@_SwiftifyImport` probably ought to be rewritten as an internal
macro one day, it currently works and is already in use. Instead the
first adopter will be a new macro, which will act as the opposite of
`@_SwiftifyImport`. This will be added in a follow-up PR.

Since this is not exposed to anything outside the compiler, this initial
implementation is catered only to what is needed for the first macro. It
only supports peer macros for now, and all the macro gets passed is a
pointer to the node it's attached to. If future macros need something
more we can easily add more and update all internal macro
implementations (even though I expect the number to be quite low) in
sync without breaking anything.
2026-10-02 16:28:09 +02:00
..