-
Notifications
You must be signed in to change notification settings - Fork 228
Description
A difference that cropped up naturally in the dart_model exploration, and seems worth considering independently of that work.
Generalize over applications:
Consider having one macro "runtime instance" handle multiple applications.
There isn't any particular advantage to macro code executing in the context one of application: rather, all applications of a macro Foo are runs of the same code that in the worst case can certainly manage not to interfere with itself, and in the best case can benefit from sharing work.
For example, suppose that the Foo macro must introspect and make decisions about all the types used as a return type in the class Foo is applied to.
Then, there is a high chance this work will be duplicated across Foo applications, because different classes using Foo are very likely to have some of the same return types. Sharing the work across applications can lead to significantly better scalability.
@Foo()
class Bar1 {
final Baz baz;
}
@Foo()
class Bar2 {
final Baz baz; // Work related to `Baz` done by the `Foo` application to `Bar1` can be reused.
}This can also lead to opportunities to batch requests and responses for better serialization throughput.
Generalize over annotations:
Consider having once macro "instance" handle, if so configured, multiple annotations.
There is also no particular advantage to tying macros to a single annotation. One macro package might offer annotations Foo and Bar that care about each other: different aspects of the same generated code. Then, there is no particular reason to force every Foo to redo the work done by Bar, when it could simply be shared. So: support using the same macro code for both Foo and Bar and running it on all Foo and all Bar.
@Foo()
class Bar1 {
final Baz baz;
}
@Bar()
class Bar2 {
final Baz baz; // Work related to `Baz` done by the `Foo` application to `Bar1` can be reused here too.
}Merging Foo and Bar into a single macro in this way also simplifies the job of the host, because they are no longer separate macro applications each of which might produce output visible to the other; they share information internally and must produce one unified set of augmentations, leaving no more work for the host.
Generalize fully?
Probably not. To generalize fully in this direction would mean:
- Given a program, a macro arbitrarily decides whether it wants to run;
- It consumes any part of the program it likes as input;
- It generates augmentations anywhere in the program as output.
And this seems too much: #1 and #3 are too surprising.
A reasonable compromise seems to be to restrict #1 and #2 back to focusing on annotations:
- A macro is associated with one or more annotations Macro application annotation identification #3728;
- A macro runs if any of the annotations it is associated with is "applied" as defined in Macro application annotation identification #3728;
- It consumes any part of the program reachable from those annotation applications as input;
- It generates augmentations in any library containing those annotations applications as output.
What about modular builds?
The host should communicate which applications are "read only": i.e. the augmentation has already been written in an earlier build step; the macro does not have to produce the augmentation again but is free to introspect that application if it wants.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status