From 9f683f8315c711e84eb6f67b84f2c10457f904c3 Mon Sep 17 00:00:00 2001 From: Jake Macdonald Date: Fri, 17 Sep 2021 09:26:47 -0700 Subject: [PATCH 1/4] add section on macro instantiation and parameters --- working/macros/feature-specification.md | 42 +++++++++++++++++++++---- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/working/macros/feature-specification.md b/working/macros/feature-specification.md index 1890a71dd6..5425b82a10 100644 --- a/working/macros/feature-specification.md +++ b/working/macros/feature-specification.md @@ -246,6 +246,36 @@ 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 + +Macros should be able to be able to be instantiated using only the libraries +that are available to the macro definition library. This allows us to compile an +app (or spawn an isolate) that can run macro applications for that macro using +only its own dependencies. + +### Macro Constructor Parameters + +In order to facilitate this, parameters to Macro constructors are limited to +a specific set of allowable types: + +- [Code][] or its subtypes +- String +- int, double, num +- bool +- null (really this just allows nullable types) +- Uri +- DateTime + +### Macro Constructor Arguments + +When [Code][] is used as a parameter type, the corresponding arguments in macro +_applications_ are provided as normal Dart code by the user. This code is then +parsed and converted to the corresponding [Code][] instance. + +Direct instantiations of macros from code (typically during macro composition) +must provide the actual [Code][] instance directly, and no automatic conversion +happens. + ## APIs The specific APIs for macros are being prototyped currently, and the docs are @@ -455,7 +485,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 readAsLinesSync({Encoding encoding = utf8}); } @@ -543,7 +573,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; @@ -613,12 +643,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; } ``` @@ -676,7 +706,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 @@ -723,4 +753,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 \ No newline at end of file +[Uri]: https://api.dart.dev/stable/2.13.4/dart-core/Uri-class.html From 37b9cba024aaa9c3313c0d99b5ff37624e60d329 Mon Sep 17 00:00:00 2001 From: Jake Macdonald Date: Fri, 17 Sep 2021 09:31:52 -0700 Subject: [PATCH 2/4] add wording about object literals only --- working/macros/feature-specification.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/working/macros/feature-specification.md b/working/macros/feature-specification.md index 5425b82a10..7580fee9be 100644 --- a/working/macros/feature-specification.md +++ b/working/macros/feature-specification.md @@ -268,6 +268,11 @@ a specific set of allowable types: ### Macro Constructor Arguments +When any type other than [Code][] is used, arguments are only allowed to be +object literals. Or in the case of `Uri` and `DateTime` they must be direct +invocations of one of their constructors, which are only passed literals as +arguments. + When [Code][] is used as a parameter type, the corresponding arguments in macro _applications_ are provided as normal Dart code by the user. This code is then parsed and converted to the corresponding [Code][] instance. From 5da15393d7ba30ecda0ec93657bf1f65457b9fa9 Mon Sep 17 00:00:00 2001 From: Jake Macdonald Date: Mon, 27 Sep 2021 14:28:06 -0700 Subject: [PATCH 3/4] code review updates --- working/macros/feature-specification.md | 85 +++++++++++++++---------- 1 file changed, 53 insertions(+), 32 deletions(-) diff --git a/working/macros/feature-specification.md b/working/macros/feature-specification.md index 7580fee9be..15108358fa 100644 --- a/working/macros/feature-specification.md +++ b/working/macros/feature-specification.md @@ -248,38 +248,59 @@ they annotate, including introspecting on members of classes, etc. ## Macro Instantiation -Macros should be able to be able to be instantiated using only the libraries -that are available to the macro definition library. This allows us to compile an -app (or spawn an isolate) that can run macro applications for that macro using -only its own dependencies. - -### Macro Constructor Parameters - -In order to facilitate this, parameters to Macro constructors are limited to -a specific set of allowable types: - -- [Code][] or its subtypes -- String -- int, double, num -- bool -- null (really this just allows nullable types) -- Uri -- DateTime - -### Macro Constructor Arguments - -When any type other than [Code][] is used, arguments are only allowed to be -object literals. Or in the case of `Uri` and `DateTime` they must be direct -invocations of one of their constructors, which are only passed literals as -arguments. - -When [Code][] is used as a parameter type, the corresponding arguments in macro -_applications_ are provided as normal Dart code by the user. This code is then -parsed and converted to the corresponding [Code][] instance. - -Direct instantiations of macros from code (typically during macro composition) -must provide the actual [Code][] instance directly, and no automatic conversion -happens. +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 object literals 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 From b1296ee439c4723b6f8b1d5a8017993729c49b6b Mon Sep 17 00:00:00 2001 From: Jake Macdonald Date: Tue, 28 Sep 2021 09:20:13 -0700 Subject: [PATCH 4/4] object literals => literal expressions --- working/macros/feature-specification.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/working/macros/feature-specification.md b/working/macros/feature-specification.md index 15108358fa..375a493ebe 100644 --- a/working/macros/feature-specification.md +++ b/working/macros/feature-specification.md @@ -258,7 +258,7 @@ 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 object literals may be used as arguments, which limits the types +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: