Skip to content

add section on macro instantiation and parameters #1861

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Sep 28, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 62 additions & 6 deletions working/macros/feature-specification.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,62 @@ introspection APIs.
These macros can fully introspect on any type reachable from the declarations
they annotate, including introspecting on members of classes, etc.

## Macro Instantiation

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This kind of drops the reader in the deep end. How about something like:


To apply a macro, a Dart compiler constructs an instance of the applied macro's class, and then invokes methods on it that implement the macro API. The arguments on the metadata annotation for the macro application get passed to the macro as constructor parameters. For example, on a macro application like:

@myMacro(1, 'a string')
class SomeClass {}

The compiler constructs an instance of the myMacro class and passes 1 and 'a string' to it. We want a Dart compiler to be able to construct and call methods on these macros while they are potentially running in a separate isolate or process. That in turn means that the constructor arguments need to be values that are easily serialized and sent to an isolate.

Macro constructor arguments can only be one of these types:

  • The primitive types bool, double, Null, num, and String
  • [Code][] and its subtypes
  • Uri
  • DateTime

Also, what about lists, sets, and maps?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took that suggestion but s/arguments/parameters (the arguments can be anything, but the macro will get them as Code if they aren't these types).

Also, what about lists, sets, and maps?

I added those as well - but require that they are passed as literals (and all arguments are literals).

To apply a macro, a Dart compiler constructs an instance of the applied macro's
class, and then invokes methods on it that implement the macro API. The
arguments on the metadata annotation for the macro application get passed to the
macro as constructor parameters.

The macro can choose whether each parameter should be passed by its value, or as
a [Code][] object (or any subtype of [Code][]). It chooses this based on the
parameter type that is used - any parameter with a subtype of [Code][] is
automatically coerced into an instance of that type. The user simply writes
normal Dart code in their macro application. For arguments that are passed by
value, only literal expressions may be used as arguments, which limits the types
of values that can be passed in this way.

For example, given a macro definition like this:

```dart
class AddMacro implements FunctionDefinitionMacro {
const AddMacro(int a, Expression b);

void visitFunctionDefinition(_, FunctionDefinitionBuilder builder) {
// Takes the literal value for `a`, and adds the expression `b` to it.
builder.implement(FunctionBody.fromParts(['=> $a + ', b, ';']));
}
}
```

You could apply the macro like this:

```dart
int get a => 1;
const b = 2;

class SomeClass {
@AddMacro(1, a + b)
int addThem(); // Generates: => 1 + a + b;
}
```

The compiler constructs an instance of the `AddMacro` class and passes `1` and
an Expression object representing the original `a + b` expression, resolved in
the scope of the original macro application. The macro then uses each of those
to construct a function body and implement the function.

**Note**: Direct instantiations of macros from code (typically during macro
composition) must provide the actual [Code][] instance directly, and no
automatic coercion happens. This coercion only takes place for macro
applications.

**TODO**: What parser ambiguities does this introduce? Can/should we limit this
to the `Expression` type, or can we also allow statements?

**TODO**: Should we support const expressions as values, if they can be
evaluated prior to macro expansion?

## APIs

The specific APIs for macros are being prototyped currently, and the docs are
Expand Down Expand Up @@ -455,7 +511,7 @@ class Resource {

/// Synchronously reads this resource as text using [encoding].
String readAsStringSync({Encoding encoding = utf8});

/// Synchronously reads the resource as lines of text using [encoding].
List<String> readAsLinesSync({Encoding encoding = utf8});
}
Expand Down Expand Up @@ -543,7 +599,7 @@ Declaration phase:
@macroB
class X {
@macroC // Added by `@macroA`, runs after both `@macroB` and `@macroA`
int? a;
int? a;

// Generated by `@macroC`, not visible to `@macroB`.
int? b;
Expand Down Expand Up @@ -613,12 +669,12 @@ any user written code is always clear. Consider the following example:
```dart
int get x => 1;

@generateX
@generateX
class Bar {
// Generated: int get x => 2;

// Should this return the top level `x`, or the generated instance getter?
int get y => x;
int get y => x;
}
```

Expand Down Expand Up @@ -676,7 +732,7 @@ given library. This will be allowed through the library introspection class,
which is available from the introspection APIs on all declarations via a
`library` getter.

TODO: Fully define the library introspection API for each phase.
TODO: Fully define the library introspection API for each phase.

### API Versioning

Expand Down Expand Up @@ -723,4 +779,4 @@ lot of flexibility with the API going forward.
[Macro]: https://jakemac53.github.io/macro_prototype/doc/api/definition/Macro-class.html
[typeDeclarationOf]: https://jakemac53.github.io/macro_prototype/doc/api/definition/DeclarationBuilder/typeDeclarationOf.html
[TypeMacro]: https://jakemac53.github.io/macro_prototype/doc/api/definition/TypeMacro-class.html
[Uri]: https://api.dart.dev/stable/2.13.4/dart-core/Uri-class.html
[Uri]: https://api.dart.dev/stable/2.13.4/dart-core/Uri-class.html