Skip to content

Commit

Permalink
clarify how and when macros should be constructed, remove ability to …
Browse files Browse the repository at this point in the history
…annotate with const references (#3205)
  • Loading branch information
jakemac53 authored Jul 13, 2023
1 parent c125ae1 commit b1b4828
Showing 1 changed file with 47 additions and 43 deletions.
90 changes: 47 additions & 43 deletions working/macros/feature-specification.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,19 +150,22 @@ Macros are applied to declarations using the existing metadata annotation
syntax. For example:

```dart
@myCoolMacro
@MyCoolMacro()
class MyClass {}
```

Here, if `myCoolMacro` resolves to an instance of a class implementing one or
more of the macro interfaces, then the annotation is treated as an application
of the `myCoolMacro` macro to the class MyClass.
Here, if the `MyCoolMacro` type is a `macro class`, then the annotation is
treated as an application of the `MyCoolMacro()` macro to the class MyClass.

Macro applications can also be passed arguments, either in the form of
[Code][] expressions, [TypeAnnotation][]s, or certain
types of literal values. See [Macro Arguments](#Macro-arguments) for more
information on how these arguments are handled when executing macros.

Macro applications must always be constructor invocations. It is an error to
annotate a declaration with a constant reference to a macro instance. In the
future we may explore a more concise macro application syntax.

### Code Arguments

Consider this example macro application:
Expand Down Expand Up @@ -257,25 +260,25 @@ order of macros:
example:

```dart
@third
@second
@first
@Third()
@Second()
@First()
class C {}
```

Here, the macros applied to C are run `first`, `second`, then `third`.
Here, the macros applied to C are run `First()`, `Second()`, then `Third()`.

* **Macros are applied to superclasses, mixins, and interfaces first, in**
**Phase 2** For example:

```dart
@third
@Third()
class B extends A with C implements D {}
@second
@Second()
class A implements C {}
@first
@First()
class C {}
@first
Expand Down Expand Up @@ -415,22 +418,22 @@ ordering problem to discuss. Imagine you have these two classes for tracking
pets and their humans:

```dart
@jsonSerializable
@JsonSerializable()
class Human {
final String name;
final Pet? pet; // Optional, might not have a pet.
}
@jsonSerializable
@JsonSerializable()
class Pet {
final String name;
final Owner? owner; // Optional, might be feral.
}
```

You want to be able to save these to the cloud, so you use a `@jsonSerializable`
macro that generates a `toJson()` method on each class the macro is applied to.
You want the methods to look like this:
You want to be able to save these to the cloud, so you use a
`@JsonSerializable()` macro that generates a `toJson()` method on each class the
macro is applied to. You want the methods to look like this:

```dart
class Human {
Expand All @@ -451,10 +454,10 @@ class Pet {
```

Note that the `pet` and `owner` fields are serialized by recursively calling
their `toJson()` methods. To generate that code, the `@jsonSerializable` macro
their `toJson()` methods. To generate that code, the `@JsonSerializable()` macro
needs to look at the type of each field to see if it declares a `toJson()`
method. The problem is that there is *no* order of macro application that will
give the right result. If we apply `@jsonSerializable` to Human first, then it
give the right result. If we apply `@JsonSerializable()` to Human first, then it
won't call `toJson()` on `pet` because Pet doesn't have a `toJson()` method yet.
We get the opposite problem if we apply the macro to Pet first.

Expand Down Expand Up @@ -621,7 +624,7 @@ With macros, many of those metadata annotations would instead either *become*
macros or be *read* by them. The latter means that macros also need to be able
to introspect over non-macro metadata annotations applied to declarations.

For example, a `@jsonSerialization` class macro might want to look for an
For example, a `@JsonSerialization()` class macro might want to look for an
`@unseralized` annotation on fields to exclude them from serialization.

**TODO**: The following subsections read more like a design discussion that a
Expand Down Expand Up @@ -788,7 +791,7 @@ user-written code is clear. Consider the following example:
```dart
int get x => 1;
@generateX
@GenerateX()
class Bar {
// Generated: int get x => 2;
Expand Down Expand Up @@ -912,19 +915,19 @@ Both of these mechanisms allow for normal macro ordering to be circumvented.
Consider the following example, where all macros run in the Declaration phase:

```dart
@macroA
@macroB
@MacroA()
@MacroB()
class X {
@macroC // Added by `@macroA`, runs after both `@macroB` and `@macroA`
@MacroC() // Added by `@MacroA()`, runs after both `@MacroB()` and `@MacroA()`
int? a;
// Generated by `@macroC`, not visible to `@macroB`.
// Generated by `@MacroC()`, not visible to `@MacroB()`.
int? b;
}
```

Normally, macros always run "inside-out". But in this case `@macroC` runs after
both `@macroB` and `@macroA` which were applied to the class.
Normally, macros always run "inside-out". But in this case `@MacroC()` runs after
both `@MacroB()` and `@MacroA()` which were applied to the class.

We still allow this because it doesn't cause any ambiguity in ordering, even
though it violates the normal rules. We could instead only allow adding macros
Expand Down Expand Up @@ -998,23 +1001,24 @@ their libraries. At this point, you have a set of mutually interdependent
libraries. They may contain references to declarations that don't exist because
macros have yet to produce them.

Collect all the metadata annotations whose names can be resolved and that
resolve to macro classes. Report an error if any application refers to a macro
declared in this cycle.
Collect all the metadata annotations which are constructor invocations of
macro classes. Report an error if any application refers to a macro declared in
this cycle, or if any annotation is a reference to a const instance of a macro
class (they must be explicit constructor invocations).

**TODO**: The above resolution rules may change based on
**NOTE**: We may in the future allow constant references to macros in
annotations, or something similar to that, see discussion in
https://github.com/dart-lang/language/issues/1890.

#### 3. Apply macros

In a sandbox environment or isolate, create an instance of the corresponding
macro class for each macro application. Pass in any macro application arguments
to the macro's constructor. If a parameter's type is `Code` or a subclass,
convert the argument expression to a `Code` object. Any bare identifiers in the
argument expression are converted to `Identifier` (see
[Identifier Scope](#Identifier-Scope) for scoping information).
In a sandbox environment, likely a separate isolate or process, create an
instance of the corresponding macro class for each macro application. See
[Executing macros](#Executing-macros) for more explanation of how macros are
constructed and how their arguments are handled.

Run all of the macros in phase order:
Run all of the macros in phase order (see also
[Application order](#Application-order) for ordering within each phase):

1. Invoke the corresponding visit method for all macros that implement phase 1
APIs.
Expand Down Expand Up @@ -1095,14 +1099,14 @@ are ready to be loaded and executed when applied in libraries in later cycles.
## Executing macros

To apply a macro, a Dart compiler constructs an instance of the applied macro's
class and then invokes methods that implement macro API interfaces. The macro is
a full-featured Dart program with complete access to the entire Dart language.
Macros are Turing-complete.
class and then invokes methods that implement macro API interfaces. Then it
disposes of the macro instance. Typically this is all done in a separate isolate
or process from the compiler itself.

### Macro arguments
Macros are full-featured Dart programs with complete access to the entire Dart
language (but limited access to core libraries). Macros are Turing-complete.

**TODO**: How are metadata annotations that refer to constant objects handled
(#1890)?
### Macro arguments

Each argument in the metadata annotation for the macro application is converted
to a form that the corresponding constructor on the macro class expects, which
Expand Down

0 comments on commit b1b4828

Please sign in to comment.