From 5bf353bfc3a1acf671690a30597cad54cf7de4eb Mon Sep 17 00:00:00 2001 From: Robert Borghese <28355157+RobertBorghese@users.noreply.github.com> Date: Wed, 17 May 2023 07:22:09 -0400 Subject: [PATCH 01/12] Initial draft of typed metadata proposal --- proposals/0000-typed-metadata.md | 554 +++++++++++++++++++++++++++++++ 1 file changed, 554 insertions(+) create mode 100644 proposals/0000-typed-metadata.md diff --git a/proposals/0000-typed-metadata.md b/proposals/0000-typed-metadata.md new file mode 100644 index 0000000..4929b81 --- /dev/null +++ b/proposals/0000-typed-metadata.md @@ -0,0 +1,554 @@ +# ::enter feature name here:: + +* Proposal: [HXP-0000](0000-typed-metadata.md) +* Author: [Robert Borghese](https://github.com/RobertBorghese) + +## Introduction + +A typing system for Haxe metadata to validate a its arguments +and optionally provide compile-time transformations. + +```haxe +// ----------------------- +// mypack/Meta.hx + +/** + Define an author for a type definition. +**/ +@:metadata function author(name: String): haxe.macro.Expr.TypeDefinition; + +/** + Transform an `Expr` to execute only if the `input` + expression is not `null`. +**/ +@:metadata function ifSome(input: haxe.macro.Expr): haxe.macro.Expr { + final content = switch(Context.getDecoratorSubject().type) { + case DExpression(e): e; + case _: throw "Impossible"; + } + return macro { + final _t = $input; + if(_t != null) { + $content; + } + } +} + +// ----------------------- +// MyClass.hx + +import mypack.Meta; + +@author("Anonymous") +class MyClass { + public function printPlus3(a: Null): Int { + @ifSome(a) { + trace(a + 3); + } + } +} +``` + +## Motivation + +Metadata can be a little tedious to work with sometimes. + +When making a program involving Haxe metadata, it's simple to check for +a specific meta's existance; however, things become way more complicated +when working with its arguments. A good amount of boilerplate needs +to be written to check if an argument exists, if it's the desired type, +and then to convert the `Expr` object into the desired format. + +On the other hand using a Haxe library or framework that processes +metadata can become troublesome, as there is no guarentee the metadata +is documented properly. Not to mention there is no scoping control with +metadata, so there may be naming conflicts. + +Typing metadata using function declarations provides a better format +for finding, documenting, and error checking Haxe metadata. + +## Detailed design + +There's a lot to cover here. A table has been provided for your convenience: + +| Topic | Description | +| --- | --- | +| Basic Rules | The basic syntax and rules for a metadata "function" declaration. | +| Haxe API Changes | The changes to the Haxe API required. | +| New Metadata | List of new metadata used to configure metadata functions. | +| Allowed Argument Types | List of argument types allowed for a typed metadata. | +| Allowed Return Types | List of return types allowed for a typed metadata. | +| Decorators | The design of metadata that runs code from its function to modify its subject. | + +### Basic Rules + +A metadata can be declared using a function declaration with the `@:metadata` meta. + +Metadata functions are allowed to have no function body, though they can use one +if desired. This will be covered later (see "Decorators"). +```haxe +@:metadata function myMeta(): Any; +``` + +#### Metadata Function Restrictions + +The `@:metadata` meta may only be used on module-level or static functions. +Furthermore, the `macro`, `dynamic`, `extern`, and `inline` flags cannot be used with a metadata function. +```haxe +@:metadata var myVar: Int; // error: @:metadata can only be used on static functions. +@:metadata macro function myMeta(): Any; // error: Invalid access on metadata function. +``` + +Metadata functions cannot be called normally. Any attempt to call them should +result in an error: +```haxe +function main() { + myMeta(); // error: Function marked with @:metadata can only be used as metadata. +} +``` + +Type parameters are not allowed on metadata functions. +```haxe +@:metadata function myMeta(); // error: Type parameters disalloweed on metadata functions. +``` + +#### Metadata Scoping/Importing + +This metadata can be used on anything that allows metadata on it currently. +However, it follows the same scoping rules as Haxe functions. Meaning it must +either be used with its full path (packages.Module.funcName) or be imported: +```haxe +@mypack.MyModule.myMeta function doThing() { ... } +``` +```haxe +import mypack.MyModule; +@myMeta function doThing() { ... } +``` + +#### Untyped Metadata + +If the Haxe compiler encounters a metadata entry it cannot type, how it is +handled is an "Unresolved Question". + +For the time being, this proposal suggests throwing an error unless the +define `-D allow-untyped-meta` is defined. + +#### Setting Where the Meta can be Used + +The return type of the metadata function declaration dictates where this metadata +can be used. The `Any` type denotes a metadata can be used anywhere. Another example +is `haxe.macro.Expr` restricts a metadata's usage to expressions. + +A full list of allowed return types can be found at "Allowed Return Types". +Any return type besides the ones listed there are not allowed and should result in an error. + +```haxe +// use this anywhere +@:metadata function anyMeta(): Any; + +// only allowed on expressions +@:metadata function exprMeta(): haxe.macro.Expr; + +// error: Type `Int` is not valid type for metadata function. +@:metadata function intMeta(): Int; +``` +#### Basic Meta Arguments + +Arguments can be added to the metadata functions. Like with return types, there are only +certain types allowed. A full list can be found at "Allowed Argument Types". + +Besides the argument type restriction, there are no other restrictions for arguments. +Optional arguments, default arguments, and rest arguments should work as normal. +```haxe +@:metadata function oneNum(num: Int): Any; +@oneNum(123) function doThing() { ... } + +// --- + +@:metadata function maybeNum(num: Int = 0): Any; +@maybeNum function doThing() { ... } +@maybeNum(123) function doThing2() { ... } + +// --- + +@:metadata function numAndStr(?num: Int, str: String): Any; +@numAndStr(123, "test") function doThing() { ... } +@numAndStr("test") function doThing2() { ... } + +// --- + +@:metadata function numRest(...num: Int): Any; +@numRest function doThing() { ... } +@numRest(1) function doThing2() { ... } +@numRest(1, 2, 3) function doThing3() { ... } + +// --- + +// error: Type `haxe.Exception` is not valid argument type for metadata function. +@:metadata function invalidType(o: haxe.Exception): Any; +``` + +### Haxe API Changes + +A new optional field should be added to `haxe.macro.Expr.MetadataEntry`. + +If this metadata entry is typed, then this field `field` will be filled with a +reference to the `ClassField` of the metadata function. +```haxe +// Unresolved question: would it be possible to have this typed? +// Maybe this should be haxe.macro.Expr.Field instead? +var ?field:Ref; +``` + +#### Reading Arguments + +There needs to be a mechanism for reading the arguments of a metadata. +To provide this, new class should be added: `haxe.macro.MetadataEntryTools`. + +This class provides static extension functions for `MetadataEntry` that read +a specific argument type. For example, for the `Int` argument type, there should +be a `getInt(index: Int)` function that looks like this: + +```haxe +static function getInt(entry: MetadataEntry, index: Int): Null { + return if(entry.params != null && index < entry.params.length) { + switch(entry.params[index].expr) { + case EConst(CInt(v)): Std.parseInt(v); + case _: null; + } + } else { + null; + } +} +``` + +There should be one function for every possible argument type. + +These functions should not throw any errors, as that is the job of the Haxe compiler +on typed metadata. Instead `null` is returned if the argument doesn't exist or match +the desired type. Technically, these could also be used on untyped metadata. + +#### Function Type Struct + +The following anonymous structure should be added to the `haxe/macro/Expr.hx` module: +```haxe +typedef FunctionPath = { + > TypePath, + functionName: String; +}; +``` + +This is an untyped structure for storing type paths to functions. It is used as an +argument type for metadata. Long story short, it allows for type paths that end with +a lowercase identifier (`myFunc`, `Module.Sub.myFunc`). + +Technically, function paths could be stored in `TypePath`, but they shouldn't. + +### New Metadata + +There needs to be a way for metadata functions to configure a couple options: + * Whether the metadata can be used multiple times on the same subject. + * Whether the metadata is compile-time only, or if its data should persist as rtti. + * Whether the metadata is restricted to a specific platform (or list of platforms). + * Whether the metadata requires another metadata to function. + +While subject to change, this proposal recommends adding additional metadata to be used +in combination with `@:metadata` to configure these options: +```haxe +/** + Use on a metadata function. That meta will only exist at + compile-time (will not generate any rtti data). + + Unresolved question: Should this force this meta to + be prefixed with a colon? +**/ +@:metadata +@metadataCompileOnly +@metadataRequire(@:metadata _) +function metadataCompileOnly(): haxe.macro.Expr.Function; + +/** + Use on a metadata function. + + Generates an error if the metadata function is + used on a subject without a meta with the name `other`. + + For example: `@:metadataRequire(Meta.metadata)` only allows + this meta to be used in combination with `@:metadata`. +**/ +@:metadata +@metadataCompileOnly +@metadataRequire(@:metadata _) +function metadataRequire(...other: haxe.macro.Expr.MetadataEntry): haxe.macro.Expr.Function; + +/** + Register a function as a typed meta. + + `allowMultiple` defines whether this metadata can be + used multiple times on the same subject. +**/ +@:metadata +@metadataCompileOnly +@metadataRequire(@:metadata _) +function metadataAllowMulti(): haxe.macro.Expr.Function; + +/** + Use on a metadata function. + + Restricts the meta to only be used on specific platforms. + + If untyped metadata throw an error, this is unnecessary, as one can + use conditional compilation to only define this metadata if a target's + define is defined. +**/ +@:metadata +@metadataCompileOnly +@metadataRequire(@:metadata _) +function metadataPlatforms(...platformName: String): haxe.macro.Expr.Function; +``` + +### Allowed Argument Types + +The following is the full list of allowed argument types for metadata. + +* `Bool` +Allows either `true` or `false`. Argument must match: `EConst(CIdent("true" | "false"))`. +The identifier is "true", `true` is passed to the decorator function. Otherwise, `false` is passed. + +* `Int` +Allows an integer literal. Argument must match: `EConst(CInt(v))`. +`v` is what's passed to the decorator function. + +* `Float` +Allows a float literal. Argument must match: `EConst(CFloat(v))` or `EConst(CInt(v))`. +`v` is what's passed to the decorator function. + +* `String` +Allows a string literal. Argument must match: `EConst(CString(v, DoubleQuotes))`. +Let there be unique error message if `SingleQuotes` is used. +`v` is what's passed to the decorator function. + +* `EReg` +Allows a regular expression literal. Argument must match: `EConst(CRegexp(s, opt))`. +`new EReg(s, opt)` is what's passed to the decorator function. + +* `haxe.macro.Expr.Var` +Allows variable declaration expression. Argument must match: `EVars([v])`. +`v` is what's passed to the decorator function. + +* `haxe.macro.Expr` +Allows any expression. Argument can be any valid Haxe expression. +The expression object is passed directly. + +* `haxe.macro.Expr.TypePath` +Allows a type path. Argument must match: `EConst(CIdent(_))` or `EField(_, _)`. +Needs to be a valid expression, so type parameters cannot be used. +The expression will be converted to a `TypePath` manually by the Haxe compiler. +Furthermore, it's only valid if the type path follows Haxe package/module naming +rules (packages must be lowercase, module and sub names must start with uppercase). + +* `haxe.macro.Expr.FunctionPath` +Same as `TypePath`, but when converting/validating from an expression, this allows +the final identifier to start with a lowercase letter. + +* `haxe.macro.Expr.ComplexType` +Allows any type. Argument must match: `ECheckType({ expr: EConst(EIdent("\_")) }, complexType)` +`complexType` is what's passed to the decorator function. + +* `haxe.macro.Expr.MetadataEntry` +Allows for any metadata. Argument must match: `EMeta(metaEntry, { expr: EConst(EIdent("\_")) })` +`metaEntry` is what's passed to the decorator function. + +### Allowed Return Types + +The following is the full list of allowed return types for metadata. + +* `Any` +The metadata can be used anywhere. + +* `haxe.macro.Expr` +The metadata can only be used on an expression. + +* `haxe.macro.Expr.TypeDefinition` +The metadata can only be used on type definitions. +`@:metadataAllowedDefinitions` can restrict the type of type definition allowed. + +* `haxe.macro.Expr.Field` +The metadata can only be used on class fields. + +* `haxe.macro.Expr.TypeParamDecl` +The metadata can only be used on type parameters. + +### Decorators + +A typed metadata that has code in its function body is called a Decorator. +The code of a decorator is run for every entry of the typed metadata. + +#### Context.getDecoratorSubject() + +To retrieve information about the subject of the decorator, `Context.getDecoratorSubject` is +a new `Context` function that may be used. + +```haxe +class Context { + // ... + public static function getDecoratorSubject(): DecoratorSubject { ... } +} +``` + +`DecoratorSubject` is a new typedef from the `Context` module containing the entry that triggered +the metadata function call and the "target" it's being used on. + +`DecoratorSubjectTarget` is a new enum from the `Context` module containing all the possible +metadata targets and their equivalent untyped data structure. + +```haxe +import haxe.macro.Expr; + +typedef DecoratorSubject = { + entry: MetadataEntry, + type: DecoratorSubjectTarget +} + +// Prefix with "D" to prevent conflicts with `haxe.macro.` classes? +enum DecoratorSubjectTarget { + DExpression(e: Expr); + DTypeDefinition(td: TypeDefinition); + DField(f: Field); + DTypeParam(tp: TypeParamDecl); +} +``` + +#### Custom Decorator Validator + +Decorators do not need to return a value. If `null` is returned, the decorator will not affect +its subject. However, this can be helpful as this allows developers to write their own logic +for whether a metadata is being used correctly. + +If one's metadata should only be used on a SPECIFIC type of expression or a SPECIFIC type of field, +this is where that can be enforced. + +Note: when a throw statement is used in a metadata function, it should generate a compiler error +at the position of the metadata entry that triggered the function. +```haxe +// Only works on property fields +@:metadata function propMeta(): haxe.macro.Expr.Field { + switch(Context.getDecoratorSubject().type) { + case DField(f): { + switch(f.kind) { + case FProp(_, _, _, _): { /* don't do anything */ } + case _: throw "This metadata should only be used on properties."; + } + } + case _: throw "Impossible"; + } + + return null; +} +``` + +#### Subject-Modifying Decorator + +If a decorator's function returns an instance of the "target" subject, that instance +will replace the decorator's subject in the compiler. + +For example, this decorator replaces its expression subject with a `0`. + +```haxe +@:metadata function makeZero(): haxe.macro.Expr { + return macro 0; +} + +// --- + +trace(@makeZero "Hello!"); // Main.hx:1: 0 +``` + +```haxe +@:metadata function changeName(n: String): haxe.macro.Expr.TypeDefinition { + return switch(Context.getDecoratorSubject().type) { + case DTypeDefinition(td): { + td.name = n; + td; + } + case _: throw "Impossible"; + } +} + +// --- + +@changeName("YourClass") +class MyClass { + public function new() {} +} + +function main() { + final c = new YourClass(); +} +``` + +A metadata that works on any subject can be smart and perform different actions based +on the subject type. +```haxe +/** + Adds a meta to any subject. +**/ +@:metadata function markWithMeta(name: String): Any { + return switch(Context.getDecoratorSubject().type) { + case DExpression(e) { + { + expr: TMeta({ name: name, pos: e.pos }, e), + pos: e.pos + }; + } + case DTypeDefinition(td): { + if(td.meta == null) td.meta = []; + td.meta.push({ name: name, pos: td.pos }); + td; + } + case DField(f): { + if(f.meta == null) f.meta = []; + f.meta.push({ name: name, pos: f.pos }); + f; + } + case DTypeDefinition(tp): { + if(tp.meta == null) tp.meta = []; + tp.meta.push({ name: name, pos: Context.makePosition({min: 0, max: 0, file: ""}) }); + tp; + } + } +} +``` + +## Impact on existing code + +There will only be an impact on existing code if untyped metadata are decided to generate +errors. + +Otherwise, the API additions should not cause any breaking changes and there should be +no impact on existing code. + +## Drawbacks + +There might be of a performance penalty since all metadata must be type checked. + +## Alternatives + +Metadata can be typed checked manually, but requires a lot of unnecessary boilerplate. See "Motivation". + +Decorators on expressions, fields, and variables can be replicated using global `@:build` macros, +which are significantly slower and require writing boilerplate for checking all expressions/fields. + +There is currently no alternatives for decorators on type definitions. + +## Unresolved questions + +Should untyped metadata throw an error? Maybe a warning? Maybe errors can be enabled/disabled with +a define? + +How should colons be handled? If the current built-in Haxe metadata is going to be typed, there should +probably be a way to set a metadata to use a colon to ensure compatibility. However, it might be prefered +to encourage/enforce that users are only make typed metadata without a colon? For the time being, +`@metadataCompileOnly` answers this question by requiring a colon but not generating rtti. + +Should `MetadataEntry`s `field` field be `Ref` or `haxe.macro.Expr.Field`? From 969696c5484f8421aaba3ee36cb48b4a18440989 Mon Sep 17 00:00:00 2001 From: RoBBoR <28355157+RobertBorghese@users.noreply.github.com> Date: Wed, 17 May 2023 07:28:50 -0400 Subject: [PATCH 02/12] Add title + spacing between sections --- proposals/0000-typed-metadata.md | 84 +++++++++++++++++++++++--------- 1 file changed, 60 insertions(+), 24 deletions(-) diff --git a/proposals/0000-typed-metadata.md b/proposals/0000-typed-metadata.md index 4929b81..399d958 100644 --- a/proposals/0000-typed-metadata.md +++ b/proposals/0000-typed-metadata.md @@ -1,9 +1,12 @@ -# ::enter feature name here:: +# Typed Metadata * Proposal: [HXP-0000](0000-typed-metadata.md) * Author: [Robert Borghese](https://github.com/RobertBorghese) -## Introduction +  +  + +# Introduction A typing system for Haxe metadata to validate a its arguments and optionally provide compile-time transformations. @@ -49,7 +52,10 @@ class MyClass { } ``` -## Motivation +  +  + +# Motivation Metadata can be a little tedious to work with sometimes. @@ -67,7 +73,10 @@ metadata, so there may be naming conflicts. Typing metadata using function declarations provides a better format for finding, documenting, and error checking Haxe metadata. -## Detailed design +  +  + +# Detailed design There's a lot to cover here. A table has been provided for your convenience: @@ -80,7 +89,7 @@ There's a lot to cover here. A table has been provided for your convenience: | Allowed Return Types | List of return types allowed for a typed metadata. | | Decorators | The design of metadata that runs code from its function to modify its subject. | -### Basic Rules +## Basic Rules A metadata can be declared using a function declaration with the `@:metadata` meta. @@ -90,7 +99,7 @@ if desired. This will be covered later (see "Decorators"). @:metadata function myMeta(): Any; ``` -#### Metadata Function Restrictions +### Metadata Function Restrictions The `@:metadata` meta may only be used on module-level or static functions. Furthermore, the `macro`, `dynamic`, `extern`, and `inline` flags cannot be used with a metadata function. @@ -112,7 +121,7 @@ Type parameters are not allowed on metadata functions. @:metadata function myMeta(); // error: Type parameters disalloweed on metadata functions. ``` -#### Metadata Scoping/Importing +### Metadata Scoping/Importing This metadata can be used on anything that allows metadata on it currently. However, it follows the same scoping rules as Haxe functions. Meaning it must @@ -125,7 +134,7 @@ import mypack.MyModule; @myMeta function doThing() { ... } ``` -#### Untyped Metadata +### Untyped Metadata If the Haxe compiler encounters a metadata entry it cannot type, how it is handled is an "Unresolved Question". @@ -133,7 +142,7 @@ handled is an "Unresolved Question". For the time being, this proposal suggests throwing an error unless the define `-D allow-untyped-meta` is defined. -#### Setting Where the Meta can be Used +### Metadata Target The return type of the metadata function declaration dictates where this metadata can be used. The `Any` type denotes a metadata can be used anywhere. Another example @@ -152,7 +161,7 @@ Any return type besides the ones listed there are not allowed and should result // error: Type `Int` is not valid type for metadata function. @:metadata function intMeta(): Int; ``` -#### Basic Meta Arguments +### Basic Meta Arguments Arguments can be added to the metadata functions. Like with return types, there are only certain types allowed. A full list can be found at "Allowed Argument Types". @@ -188,7 +197,10 @@ Optional arguments, default arguments, and rest arguments should work as normal. @:metadata function invalidType(o: haxe.Exception): Any; ``` -### Haxe API Changes +  +  + +## Haxe API Changes A new optional field should be added to `haxe.macro.Expr.MetadataEntry`. @@ -200,7 +212,7 @@ reference to the `ClassField` of the metadata function. var ?field:Ref; ``` -#### Reading Arguments +### Reading Arguments There needs to be a mechanism for reading the arguments of a metadata. To provide this, new class should be added: `haxe.macro.MetadataEntryTools`. @@ -228,7 +240,7 @@ These functions should not throw any errors, as that is the job of the Haxe comp on typed metadata. Instead `null` is returned if the argument doesn't exist or match the desired type. Technically, these could also be used on untyped metadata. -#### Function Type Struct +### Function Type Struct The following anonymous structure should be added to the `haxe/macro/Expr.hx` module: ```haxe @@ -244,7 +256,10 @@ a lowercase identifier (`myFunc`, `Module.Sub.myFunc`). Technically, function paths could be stored in `TypePath`, but they shouldn't. -### New Metadata +  +  + +## New Metadata There needs to be a way for metadata functions to configure a couple options: * Whether the metadata can be used multiple times on the same subject. @@ -307,7 +322,10 @@ function metadataAllowMulti(): haxe.macro.Expr.Function; function metadataPlatforms(...platformName: String): haxe.macro.Expr.Function; ``` -### Allowed Argument Types +  +  + +## Allowed Argument Types The following is the full list of allowed argument types for metadata. @@ -359,7 +377,10 @@ Allows any type. Argument must match: `ECheckType({ expr: EConst(EIdent("\_")) } Allows for any metadata. Argument must match: `EMeta(metaEntry, { expr: EConst(EIdent("\_")) })` `metaEntry` is what's passed to the decorator function. -### Allowed Return Types +  +  + +## Allowed Return Types The following is the full list of allowed return types for metadata. @@ -379,12 +400,15 @@ The metadata can only be used on class fields. * `haxe.macro.Expr.TypeParamDecl` The metadata can only be used on type parameters. -### Decorators +  +  + +## Decorators A typed metadata that has code in its function body is called a Decorator. The code of a decorator is run for every entry of the typed metadata. -#### Context.getDecoratorSubject() +### Context.getDecoratorSubject() To retrieve information about the subject of the decorator, `Context.getDecoratorSubject` is a new `Context` function that may be used. @@ -419,7 +443,7 @@ enum DecoratorSubjectTarget { } ``` -#### Custom Decorator Validator +### Custom Decorator Validator Decorators do not need to return a value. If `null` is returned, the decorator will not affect its subject. However, this can be helpful as this allows developers to write their own logic @@ -447,7 +471,7 @@ at the position of the metadata entry that triggered the function. } ``` -#### Subject-Modifying Decorator +### Subject-Modifying Decorator If a decorator's function returns an instance of the "target" subject, that instance will replace the decorator's subject in the compiler. @@ -520,7 +544,10 @@ on the subject type. } ``` -## Impact on existing code +  +  + +# Impact on existing code There will only be an impact on existing code if untyped metadata are decided to generate errors. @@ -528,11 +555,17 @@ errors. Otherwise, the API additions should not cause any breaking changes and there should be no impact on existing code. -## Drawbacks +  +  + +# Drawbacks There might be of a performance penalty since all metadata must be type checked. -## Alternatives +  +  + +# Alternatives Metadata can be typed checked manually, but requires a lot of unnecessary boilerplate. See "Motivation". @@ -541,7 +574,10 @@ which are significantly slower and require writing boilerplate for checking all There is currently no alternatives for decorators on type definitions. -## Unresolved questions +  +  + +# Unresolved questions Should untyped metadata throw an error? Maybe a warning? Maybe errors can be enabled/disabled with a define? From 38ff249307c9fd0f7e75d18136aac98e4f2578dc Mon Sep 17 00:00:00 2001 From: RoBBoR <28355157+RobertBorghese@users.noreply.github.com> Date: Wed, 17 May 2023 07:42:59 -0400 Subject: [PATCH 03/12] Use tables for Allowed Argument/Return Types sections --- proposals/0000-typed-metadata.md | 82 ++++++++------------------------ 1 file changed, 20 insertions(+), 62 deletions(-) diff --git a/proposals/0000-typed-metadata.md b/proposals/0000-typed-metadata.md index 399d958..c509cd2 100644 --- a/proposals/0000-typed-metadata.md +++ b/proposals/0000-typed-metadata.md @@ -329,53 +329,19 @@ function metadataPlatforms(...platformName: String): haxe.macro.Expr.Function; The following is the full list of allowed argument types for metadata. -* `Bool` -Allows either `true` or `false`. Argument must match: `EConst(CIdent("true" | "false"))`. -The identifier is "true", `true` is passed to the decorator function. Otherwise, `false` is passed. - -* `Int` -Allows an integer literal. Argument must match: `EConst(CInt(v))`. -`v` is what's passed to the decorator function. - -* `Float` -Allows a float literal. Argument must match: `EConst(CFloat(v))` or `EConst(CInt(v))`. -`v` is what's passed to the decorator function. - -* `String` -Allows a string literal. Argument must match: `EConst(CString(v, DoubleQuotes))`. -Let there be unique error message if `SingleQuotes` is used. -`v` is what's passed to the decorator function. - -* `EReg` -Allows a regular expression literal. Argument must match: `EConst(CRegexp(s, opt))`. -`new EReg(s, opt)` is what's passed to the decorator function. - -* `haxe.macro.Expr.Var` -Allows variable declaration expression. Argument must match: `EVars([v])`. -`v` is what's passed to the decorator function. - -* `haxe.macro.Expr` -Allows any expression. Argument can be any valid Haxe expression. -The expression object is passed directly. - -* `haxe.macro.Expr.TypePath` -Allows a type path. Argument must match: `EConst(CIdent(_))` or `EField(_, _)`. -Needs to be a valid expression, so type parameters cannot be used. -The expression will be converted to a `TypePath` manually by the Haxe compiler. -Furthermore, it's only valid if the type path follows Haxe package/module naming -rules (packages must be lowercase, module and sub names must start with uppercase). - -* `haxe.macro.Expr.FunctionPath` -Same as `TypePath`, but when converting/validating from an expression, this allows -the final identifier to start with a lowercase letter. - -* `haxe.macro.Expr.ComplexType` -Allows any type. Argument must match: `ECheckType({ expr: EConst(EIdent("\_")) }, complexType)` -`complexType` is what's passed to the decorator function. - -* `haxe.macro.Expr.MetadataEntry` -Allows for any metadata. Argument must match: `EMeta(metaEntry, { expr: EConst(EIdent("\_")) })` -`metaEntry` is what's passed to the decorator function. +| Type | Expression Must Match | Decorator Argument Value | MetadataEntryTools Getter | Description | +| --- | --- | --- | --- | --- | +| `Bool` | `EConst(CIdent("true" \| "false"))` | `v == "true"` | `getBool` | Allows either `true` or `false`. | +| `Int` | `EConst(CInt(v))` | `Std.parseInt(v)` | `getInt` | Allows an integer literal. | +| `Float` | `EConst(CFloat(v))` or `EConst(CInt(v))` | `Std.parseFloat(v)` | `getFloat` | Allows an float literal. | +| `String` | `EConst(CString(v, DoubleQuotes))` | `v` | `getString` | Allows a string literal. Let there be unique error message if `SingleQuotes` is used. | +| `EReg` | `EConst(CRegexp(s, opt))` | `new EReg(s, opt)` | `getRegex` | Allows a regular expression literal. | +| `haxe.macro.Expr.Var` | `EVars([v])` | `v` | `getVarDecl` | Allows variable declaration expression. | +| `haxe.macro.Expr` | `e` | `e` | `getExpr` | Allows any expression. The expression object is passed directly. | +| `haxe.macro.Expr.TypePath` | `EConst(CIdent(_))` or `EField(_, _)` | ??? | `getTypePath` | Allows a type path. The expression will be converted to a `TypePath` manually by the Haxe compiler. Furthermore, it's only valid if the type path follows Haxe package/module naming rules (packages must be lowercase, module and sub names must start with uppercase). | +| `haxe.macro.Expr.FunctionPath` | `EConst(CIdent(_))` or `EField(_, _)` | ??? | `getFunctionPath` | Same as `TypePath`, but when converting/validating from an expression, this allows the final identifier to start with a lowercase letter. | +| `haxe.macro.Expr.ComplexType` | `ECheckType({ expr: EConst(EIdent("\_")) }, complexType)` | `complexType` | `getComplexType` | Allows any type. Must format as `_ : Type` to comply with expression parsing. | +| `haxe.macro.Expr.MetadataEntry` | `EMeta(metaEntry, { expr: EConst(EIdent("\_")) })` | `metaEntry` | `getMetadataEntry` | Allows any metadata. Must format as `@:meta _` to comply with expression parsing. |     @@ -384,21 +350,13 @@ Allows for any metadata. Argument must match: `EMeta(metaEntry, { expr: EConst(E The following is the full list of allowed return types for metadata. -* `Any` -The metadata can be used anywhere. - -* `haxe.macro.Expr` -The metadata can only be used on an expression. - -* `haxe.macro.Expr.TypeDefinition` -The metadata can only be used on type definitions. -`@:metadataAllowedDefinitions` can restrict the type of type definition allowed. - -* `haxe.macro.Expr.Field` -The metadata can only be used on class fields. - -* `haxe.macro.Expr.TypeParamDecl` -The metadata can only be used on type parameters. +| Type | DecoratorSubjectType Case | Description | +| --- | --- | --- | +| `Any` | N/A | The metadata can be used anywhere. | +| `haxe.macro.Expr` | `DExpression(e: Expr)` | The metadata can only be used on an expression. | +| `haxe.macro.Expr.TypeDefinition` | `DTypeDefinition(td: TypeDefinition)` | The metadata can only be used on type definitions. | +| `haxe.macro.Expr.Field` | `DField(f: Field)` | The metadata can only be used on class fields. | +| `haxe.macro.Expr.TypeParamDecl` | `DTypeParam(tp: TypeParamDecl)` | The metadata can only be used on type parameters. |     From 78788e4c1a526ecd95ef3ab3c9612f5c458ec7ed Mon Sep 17 00:00:00 2001 From: RoBBoR <28355157+RobertBorghese@users.noreply.github.com> Date: Wed, 17 May 2023 07:43:37 -0400 Subject: [PATCH 04/12] Test local links --- proposals/0000-typed-metadata.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0000-typed-metadata.md b/proposals/0000-typed-metadata.md index c509cd2..6325cfd 100644 --- a/proposals/0000-typed-metadata.md +++ b/proposals/0000-typed-metadata.md @@ -82,7 +82,7 @@ There's a lot to cover here. A table has been provided for your convenience: | Topic | Description | | --- | --- | -| Basic Rules | The basic syntax and rules for a metadata "function" declaration. | +| [Basic Rules](/0000-typed-metadata.md#basic-rules) | The basic syntax and rules for a metadata "function" declaration. | | Haxe API Changes | The changes to the Haxe API required. | | New Metadata | List of new metadata used to configure metadata functions. | | Allowed Argument Types | List of argument types allowed for a typed metadata. | From 8208864a9ac6e7edfdc53a88e9f2e3579d01dffe Mon Sep 17 00:00:00 2001 From: RoBBoR <28355157+RobertBorghese@users.noreply.github.com> Date: Wed, 17 May 2023 07:45:23 -0400 Subject: [PATCH 05/12] Test local links again --- proposals/0000-typed-metadata.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0000-typed-metadata.md b/proposals/0000-typed-metadata.md index 6325cfd..626fcd8 100644 --- a/proposals/0000-typed-metadata.md +++ b/proposals/0000-typed-metadata.md @@ -82,7 +82,7 @@ There's a lot to cover here. A table has been provided for your convenience: | Topic | Description | | --- | --- | -| [Basic Rules](/0000-typed-metadata.md#basic-rules) | The basic syntax and rules for a metadata "function" declaration. | +| [Basic Rules](0000-typed-metadata.md#basic-rules) | The basic syntax and rules for a metadata "function" declaration. | | Haxe API Changes | The changes to the Haxe API required. | | New Metadata | List of new metadata used to configure metadata functions. | | Allowed Argument Types | List of argument types allowed for a typed metadata. | From d0fa5fa936752d95a455d9b2b5fcd5920ad70623 Mon Sep 17 00:00:00 2001 From: RoBBoR <28355157+RobertBorghese@users.noreply.github.com> Date: Wed, 17 May 2023 07:49:12 -0400 Subject: [PATCH 06/12] Setup links --- proposals/0000-typed-metadata.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/proposals/0000-typed-metadata.md b/proposals/0000-typed-metadata.md index 626fcd8..4d2a207 100644 --- a/proposals/0000-typed-metadata.md +++ b/proposals/0000-typed-metadata.md @@ -82,19 +82,19 @@ There's a lot to cover here. A table has been provided for your convenience: | Topic | Description | | --- | --- | -| [Basic Rules](0000-typed-metadata.md#basic-rules) | The basic syntax and rules for a metadata "function" declaration. | -| Haxe API Changes | The changes to the Haxe API required. | -| New Metadata | List of new metadata used to configure metadata functions. | -| Allowed Argument Types | List of argument types allowed for a typed metadata. | -| Allowed Return Types | List of return types allowed for a typed metadata. | -| Decorators | The design of metadata that runs code from its function to modify its subject. | +| [Basic Rules](0000-typed-metadata.md#basic-rules) | The basic syntax and rules for a metadata "function" declaration. | +| [Haxe API Changes](0000-typed-metadata.md#haxe-api-changes) | The changes to the Haxe API required. | +| [New Metadata ](0000-typed-metadata.md#new-metadata) | List of new metadata used to configure metadata functions. | +| [Allowed Argument Types](0000-typed-metadata.md#allowed-argument-types) | List of argument types allowed for a typed metadata. | +| [Allowed Return Types](0000-typed-metadata.m#allowed-return-types) | List of return types allowed for a typed metadata. | +| [Decorators](0000-typed-metadata.md#decorators) | The design of metadata that runs code from its function to modify its subject. | ## Basic Rules A metadata can be declared using a function declaration with the `@:metadata` meta. Metadata functions are allowed to have no function body, though they can use one -if desired. This will be covered later (see "Decorators"). +if desired. This will be covered later (see [Decorators](0000-typed-metadata.md#decorators)). ```haxe @:metadata function myMeta(): Any; ``` @@ -148,7 +148,7 @@ The return type of the metadata function declaration dictates where this metadat can be used. The `Any` type denotes a metadata can be used anywhere. Another example is `haxe.macro.Expr` restricts a metadata's usage to expressions. -A full list of allowed return types can be found at "Allowed Return Types". +A full list of allowed return types can be found at [Allowed Return Types](0000-typed-metadata.md#allowed-return-types). Any return type besides the ones listed there are not allowed and should result in an error. ```haxe @@ -164,7 +164,7 @@ Any return type besides the ones listed there are not allowed and should result ### Basic Meta Arguments Arguments can be added to the metadata functions. Like with return types, there are only -certain types allowed. A full list can be found at "Allowed Argument Types". +certain types allowed. A full list can be found at [Allowed Argument Types](0000-typed-metadata.md#allowed-argument-types). Besides the argument type restriction, there are no other restrictions for arguments. Optional arguments, default arguments, and rest arguments should work as normal. @@ -525,7 +525,7 @@ There might be of a performance penalty since all metadata must be type checked. # Alternatives -Metadata can be typed checked manually, but requires a lot of unnecessary boilerplate. See "Motivation". +Metadata can be typed checked manually, but requires a lot of unnecessary boilerplate. See [Motivation](0000-typed-metadata.md#motivation). Decorators on expressions, fields, and variables can be replicated using global `@:build` macros, which are significantly slower and require writing boilerplate for checking all expressions/fields. From aaccffb5da54ddc337c49d92fcfebc472dc39173 Mon Sep 17 00:00:00 2001 From: RoBBoR <28355157+RobertBorghese@users.noreply.github.com> Date: Wed, 17 May 2023 08:38:29 -0400 Subject: [PATCH 07/12] Draft #2 --- proposals/0000-typed-metadata.md | 218 ++++++++++++++----------------- 1 file changed, 99 insertions(+), 119 deletions(-) diff --git a/proposals/0000-typed-metadata.md b/proposals/0000-typed-metadata.md index 4d2a207..6b43262 100644 --- a/proposals/0000-typed-metadata.md +++ b/proposals/0000-typed-metadata.md @@ -8,8 +8,7 @@ # Introduction -A typing system for Haxe metadata to validate a its arguments -and optionally provide compile-time transformations. +A typing system for Haxe metadata that can validate its arguments and optionally provide compile-time transformations. ```haxe // ----------------------- @@ -57,18 +56,23 @@ class MyClass { # Motivation -Metadata can be a little tedious to work with sometimes. +Sometimes metadata can be a little tedious to work with. -When making a program involving Haxe metadata, it's simple to check for -a specific meta's existance; however, things become way more complicated -when working with its arguments. A good amount of boilerplate needs -to be written to check if an argument exists, if it's the desired type, -and then to convert the `Expr` object into the desired format. +Making
+When writing code using Haxe metadata, it's simple to check for +a specific metadata's name; however, the arguments are a nightmare. +A lot of boilerplate needs to be written to: + * check if an argument exists + * check if it's the desired type + * convert from `Expr` to a usable data type -On the other hand using a Haxe library or framework that processes -metadata can become troublesome, as there is no guarentee the metadata -is documented properly. Not to mention there is no scoping control with -metadata, so there may be naming conflicts. +Using
+On the other hand, using someone's Haxe code that processes +metadata can become troublesome. There is no guarentee the metadata +is documented properly, and there is no scoping control to prevent +naming conflicts. + +- - - Typing metadata using function declarations provides a better format for finding, documenting, and error checking Haxe metadata. @@ -86,15 +90,17 @@ There's a lot to cover here. A table has been provided for your convenience: | [Haxe API Changes](0000-typed-metadata.md#haxe-api-changes) | The changes to the Haxe API required. | | [New Metadata ](0000-typed-metadata.md#new-metadata) | List of new metadata used to configure metadata functions. | | [Allowed Argument Types](0000-typed-metadata.md#allowed-argument-types) | List of argument types allowed for a typed metadata. | -| [Allowed Return Types](0000-typed-metadata.m#allowed-return-types) | List of return types allowed for a typed metadata. | -| [Decorators](0000-typed-metadata.md#decorators) | The design of metadata that runs code from its function to modify its subject. | +| [Allowed Return Types](0000-typed-metadata.md#allowed-return-types) | List of return types allowed for a typed metadata. | +| [Decorators](0000-typed-metadata.md#decorators) | The design of metadata that runs code from its function body. | + +  +  ## Basic Rules A metadata can be declared using a function declaration with the `@:metadata` meta. -Metadata functions are allowed to have no function body, though they can use one -if desired. This will be covered later (see [Decorators](0000-typed-metadata.md#decorators)). +Metadata functions are permitted to lack an implementation (similar to `extern` functions). Typed metadata with function code is still allowed and will be covered later (see [Decorators](0000-typed-metadata.md#decorators)). ```haxe @:metadata function myMeta(): Any; ``` @@ -123,33 +129,35 @@ Type parameters are not allowed on metadata functions. ### Metadata Scoping/Importing -This metadata can be used on anything that allows metadata on it currently. +Typed metadata can be used on anything that allows metadata on it currently. However, it follows the same scoping rules as Haxe functions. Meaning it must -either be used with its full path (packages.Module.funcName) or be imported: +use its full path or be imported: ```haxe -@mypack.MyModule.myMeta function doThing() { ... } -``` -```haxe -import mypack.MyModule; -@myMeta function doThing() { ... } +@mypack.MyModule.myMeta +function doThing() { ... } + +// OR + +import mypack.MyModule; + +// static function: @MyModule.myMeta +// module level: @myMeta + +@myMeta +function doThing() { ... } ``` ### Untyped Metadata -If the Haxe compiler encounters a metadata entry it cannot type, how it is -handled is an "Unresolved Question". +If the Haxe compiler encounters a metadata entry it cannot type, its behavior is currently an [Unresolved Question](0000-typed-metadata.md#unresolved-question). -For the time being, this proposal suggests throwing an error unless the -define `-D allow-untyped-meta` is defined. +For the time being, this proposal suggests throwing an error _unless_ `-D allow-untyped-meta` is defined. ### Metadata Target -The return type of the metadata function declaration dictates where this metadata -can be used. The `Any` type denotes a metadata can be used anywhere. Another example -is `haxe.macro.Expr` restricts a metadata's usage to expressions. +The return type of the metadata function declaration dictates where it's allowed to be used. -A full list of allowed return types can be found at [Allowed Return Types](0000-typed-metadata.md#allowed-return-types). -Any return type besides the ones listed there are not allowed and should result in an error. +The `Any` type denotes a metadata can be used anywhere. The `haxe.macro.Expr` restricts a metadata's usage to expressions. A full list of allowed return types can be found at [Allowed Return Types](0000-typed-metadata.md#allowed-return-types). Any return type besides those are not allowed and should result in an error. ```haxe // use this anywhere @@ -160,39 +168,33 @@ Any return type besides the ones listed there are not allowed and should result // error: Type `Int` is not valid type for metadata function. @:metadata function intMeta(): Int; -``` +``` + ### Basic Meta Arguments -Arguments can be added to the metadata functions. Like with return types, there are only -certain types allowed. A full list can be found at [Allowed Argument Types](0000-typed-metadata.md#allowed-argument-types). +Arguments can be added to the metadata functions. Like with return types, there are only certain types allowed. A full list can be found at [Allowed Argument Types](0000-typed-metadata.md#allowed-argument-types). -Besides the argument type restriction, there are no other restrictions for arguments. -Optional arguments, default arguments, and rest arguments should work as normal. +Outside the restriction of certain types, arguments should work exactly the same as they do on normal functions. This includes support for: optional arguments, default arguments, and rest arguments. ```haxe @:metadata function oneNum(num: Int): Any; @oneNum(123) function doThing() { ... } -// --- - +// default args @:metadata function maybeNum(num: Int = 0): Any; @maybeNum function doThing() { ... } @maybeNum(123) function doThing2() { ... } -// --- - +// optional args @:metadata function numAndStr(?num: Int, str: String): Any; @numAndStr(123, "test") function doThing() { ... } @numAndStr("test") function doThing2() { ... } -// --- - +// rest args @:metadata function numRest(...num: Int): Any; @numRest function doThing() { ... } @numRest(1) function doThing2() { ... } @numRest(1, 2, 3) function doThing3() { ... } -// --- - // error: Type `haxe.Exception` is not valid argument type for metadata function. @:metadata function invalidType(o: haxe.Exception): Any; ``` @@ -204,22 +206,18 @@ Optional arguments, default arguments, and rest arguments should work as normal. A new optional field should be added to `haxe.macro.Expr.MetadataEntry`. -If this metadata entry is typed, then this field `field` will be filled with a -reference to the `ClassField` of the metadata function. +If this metadata entry is typed, then `field` will contain a reference to the `ClassField` of the metadata function. ```haxe -// Unresolved question: would it be possible to have this typed? -// Maybe this should be haxe.macro.Expr.Field instead? -var ?field:Ref; +// Unresolved question +// Would it be possible to use Ref instead? +var ?field: Expr.Field; ``` ### Reading Arguments -There needs to be a mechanism for reading the arguments of a metadata. -To provide this, new class should be added: `haxe.macro.MetadataEntryTools`. +There needs to be a mechanism for reading metadata arguments. To provide this, new class should be added: `haxe.macro.MetadataEntryTools`. -This class provides static extension functions for `MetadataEntry` that read -a specific argument type. For example, for the `Int` argument type, there should -be a `getInt(index: Int)` function that looks like this: +This class provides static extension functions for `MetadataEntry` for reading arguments. Each function attempts to read an argument for a specific type. For example, for the `Int` argument type, there should be a `getInt(index: Int)` function that looks like this: ```haxe static function getInt(entry: MetadataEntry, index: Int): Null { @@ -236,9 +234,7 @@ static function getInt(entry: MetadataEntry, index: Int): Null { There should be one function for every possible argument type. -These functions should not throw any errors, as that is the job of the Haxe compiler -on typed metadata. Instead `null` is returned if the argument doesn't exist or match -the desired type. Technically, these could also be used on untyped metadata. +These functions should not throw any errors; that is the job of the Haxe compiler on typed metadata. Instead `null` is returned if the argument doesn't exist or doesn't match the desired type. Technically, these could also be used on untyped metadata. ### Function Type Struct @@ -250,11 +246,9 @@ typedef FunctionPath = { }; ``` -This is an untyped structure for storing type paths to functions. It is used as an -argument type for metadata. Long story short, it allows for type paths that end with -a lowercase identifier (`myFunc`, `Module.Sub.myFunc`). +This is a structure for storing type paths to functions. It is used as an argument type for metadata. Long story short, it allows for type paths that end with a lowercase identifier (`myFunc`, `Module.Sub.myFunc`). -Technically, function paths could be stored in `TypePath`, but they shouldn't. +Technically, function path data _could_ be stored in `TypePath`, but that's not preferable.     @@ -262,20 +256,20 @@ Technically, function paths could be stored in `TypePath`, but they shouldn't. ## New Metadata There needs to be a way for metadata functions to configure a couple options: - * Whether the metadata can be used multiple times on the same subject. - * Whether the metadata is compile-time only, or if its data should persist as rtti. - * Whether the metadata is restricted to a specific platform (or list of platforms). - * Whether the metadata requires another metadata to function. + * Can it be used multiple times on the same subject? + * Is it compile-time only? Or should it exist as rtti. + * Is it restricted to one or more platforms? + * Does it require another metadata to function? -While subject to change, this proposal recommends adding additional metadata to be used -in combination with `@:metadata` to configure these options: +While likely not set in stone, this proposal recommends adding the following metadata to be used in combination with `@:metadata` to configure these options: ```haxe /** Use on a metadata function. That meta will only exist at compile-time (will not generate any rtti data). Unresolved question: Should this force this meta to - be prefixed with a colon? + be prefixed with a colon? There needs to be a way to signify + that when typing built-in Haxe metadata in the future. **/ @:metadata @metadataCompileOnly @@ -286,9 +280,9 @@ function metadataCompileOnly(): haxe.macro.Expr.Function; Use on a metadata function. Generates an error if the metadata function is - used on a subject without a meta with the name `other`. + used on a subject without the `other` metadata - For example: `@:metadataRequire(Meta.metadata)` only allows + For example: `@:metadataRequire(@:metadata _)` only allows this meta to be used in combination with `@:metadata`. **/ @:metadata @@ -297,10 +291,11 @@ function metadataCompileOnly(): haxe.macro.Expr.Function; function metadataRequire(...other: haxe.macro.Expr.MetadataEntry): haxe.macro.Expr.Function; /** - Register a function as a typed meta. + Use on a metadata function. - `allowMultiple` defines whether this metadata can be - used multiple times on the same subject. + Allows this metadata to be used multiple times on the + same subject. Otherwise, an error is thrown if the same + metadata is used multiple times. **/ @:metadata @metadataCompileOnly @@ -312,9 +307,10 @@ function metadataAllowMulti(): haxe.macro.Expr.Function; Restricts the meta to only be used on specific platforms. - If untyped metadata throw an error, this is unnecessary, as one can - use conditional compilation to only define this metadata if a target's - define is defined. + NOTE: + If untyped metadata throw an error, this is unnecessary! Instead + conditional compilation can be used to only define a metadata + if a target's "define" is defined. (i.e. `#if js ... #end`) **/ @:metadata @metadataCompileOnly @@ -363,13 +359,11 @@ The following is the full list of allowed return types for metadata. ## Decorators -A typed metadata that has code in its function body is called a Decorator. -The code of a decorator is run for every entry of the typed metadata. +A typed metadata that has code in its function body is called a "decorator". A decorator's code is run for every entry of the typed metadata. ### Context.getDecoratorSubject() -To retrieve information about the subject of the decorator, `Context.getDecoratorSubject` is -a new `Context` function that may be used. +To retrieve information about the subject of the decorator, `Context.getDecoratorSubject` is a new `Context` function that may be used. ```haxe class Context { @@ -378,19 +372,19 @@ class Context { } ``` -`DecoratorSubject` is a new typedef from the `Context` module containing the entry that triggered -the metadata function call and the "target" it's being used on. - -`DecoratorSubjectTarget` is a new enum from the `Context` module containing all the possible -metadata targets and their equivalent untyped data structure. +`DecoratorSubject` is a new typedef from the `Context` module containing the `MetadataEntry` that triggered the call and the target. ```haxe -import haxe.macro.Expr; - typedef DecoratorSubject = { entry: MetadataEntry, type: DecoratorSubjectTarget } +``` + +`DecoratorSubjectTarget` is a new enum containing all the possible metadata targets and their "Expr" data structure. + +```haxe +import haxe.macro.Expr; // Prefix with "D" to prevent conflicts with `haxe.macro.` classes? enum DecoratorSubjectTarget { @@ -403,23 +397,20 @@ enum DecoratorSubjectTarget { ### Custom Decorator Validator -Decorators do not need to return a value. If `null` is returned, the decorator will not affect -its subject. However, this can be helpful as this allows developers to write their own logic -for whether a metadata is being used correctly. - -If one's metadata should only be used on a SPECIFIC type of expression or a SPECIFIC type of field, -this is where that can be enforced. +Decorators do not need to return a value. If `null` is returned, the decorator will not affect its subject. Developers can use this to write their own logic for ensuring their metadata is used correctly. -Note: when a throw statement is used in a metadata function, it should generate a compiler error -at the position of the metadata entry that triggered the function. +If one's metadata should only be used on a SPECIFIC type of expression or a SPECIFIC type of field, this is where that can be enforced. ```haxe // Only works on property fields @:metadata function propMeta(): haxe.macro.Expr.Field { switch(Context.getDecoratorSubject().type) { case DField(f): { switch(f.kind) { - case FProp(_, _, _, _): { /* don't do anything */ } - case _: throw "This metadata should only be used on properties."; + // Do something with property + case FProp(_, _, _, _): { } + + // Let the user know the metadata was used incorrectly! + case _: Context.error("This metadata should only be used on properties.", Context.getDecoratorSubject().entry.pos); } } case _: throw "Impossible"; @@ -431,10 +422,7 @@ at the position of the metadata entry that triggered the function. ### Subject-Modifying Decorator -If a decorator's function returns an instance of the "target" subject, that instance -will replace the decorator's subject in the compiler. - -For example, this decorator replaces its expression subject with a `0`. +If a decorator's function returns an non-null instance of its return type, that instance will replace the decorator's subject at compile-time. ```haxe @:metadata function makeZero(): haxe.macro.Expr { @@ -469,15 +457,14 @@ function main() { } ``` -A metadata that works on any subject can be smart and perform different actions based -on the subject type. +A metadata that works on any subject can be smart and perform different actions based on the type of subject it was used on. ```haxe /** Adds a meta to any subject. **/ @:metadata function markWithMeta(name: String): Any { return switch(Context.getDecoratorSubject().type) { - case DExpression(e) { + case DExpression(e): { { expr: TMeta({ name: name, pos: e.pos }, e), pos: e.pos @@ -507,18 +494,16 @@ on the subject type. # Impact on existing code -There will only be an impact on existing code if untyped metadata are decided to generate -errors. +There will only be an impact on existing code if untyped metadata generate errors. -Otherwise, the API additions should not cause any breaking changes and there should be -no impact on existing code. +Otherwise, the API additions do not cause any breaking changes, and there should be no impact on existing code.     # Drawbacks -There might be of a performance penalty since all metadata must be type checked. +There might be of a performance penalty since all metadata have to look up if they're typed?     @@ -527,8 +512,7 @@ There might be of a performance penalty since all metadata must be type checked. Metadata can be typed checked manually, but requires a lot of unnecessary boilerplate. See [Motivation](0000-typed-metadata.md#motivation). -Decorators on expressions, fields, and variables can be replicated using global `@:build` macros, -which are significantly slower and require writing boilerplate for checking all expressions/fields. +Decorators on expressions, fields, and variables can be replicated using `@:build` macros, which are significantly slower and require writing boilerplate for checking all expressions/fields. There is currently no alternatives for decorators on type definitions. @@ -537,12 +521,8 @@ There is currently no alternatives for decorators on type definitions. # Unresolved questions -Should untyped metadata throw an error? Maybe a warning? Maybe errors can be enabled/disabled with -a define? +Should untyped metadata throw an error? While it would be a major breaking change, it would be nice restrict metadata usage using conditional compilation (wrap with `#if js` for example) instead of using something like `@:metadataPlatform`. Maybe it could be a warning? Maybe errors can default to on, but turn off with a define (or vise versa)? -How should colons be handled? If the current built-in Haxe metadata is going to be typed, there should -probably be a way to set a metadata to use a colon to ensure compatibility. However, it might be prefered -to encourage/enforce that users are only make typed metadata without a colon? For the time being, -`@metadataCompileOnly` answers this question by requiring a colon but not generating rtti. +How should colons be handled? If the current built-in Haxe metadata is going to be typed, there should probably be a way to set a metadata to use a colon to ensure compatibility. However, it might be prefered to encourage/enforce that users are only make typed metadata without a colon? For the time being, `@metadataCompileOnly` answers this question by requiring a colon but not generating rtti. -Should `MetadataEntry`s `field` field be `Ref` or `haxe.macro.Expr.Field`? +Should `MetadataEntry`s `field` field be `Ref` or `haxe.macro.Expr.Field`? Would it be possible to type the field that early? From ae1a4b9a9856e2f0653436973d60bdd64fc51cf5 Mon Sep 17 00:00:00 2001 From: RoBBoR <28355157+RobertBorghese@users.noreply.github.com> Date: Wed, 17 May 2023 08:54:15 -0400 Subject: [PATCH 08/12] Fix incorrect return on sample code --- proposals/0000-typed-metadata.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0000-typed-metadata.md b/proposals/0000-typed-metadata.md index 6b43262..d6d3501 100644 --- a/proposals/0000-typed-metadata.md +++ b/proposals/0000-typed-metadata.md @@ -43,7 +43,7 @@ import mypack.Meta; @author("Anonymous") class MyClass { - public function printPlus3(a: Null): Int { + public function printPlus3(a: Null) { @ifSome(a) { trace(a + 3); } From e6256c338b5a12edc67b86cc41738bad4f2b2e2b Mon Sep 17 00:00:00 2001 From: RoBBoR <28355157+RobertBorghese@users.noreply.github.com> Date: Thu, 18 May 2023 06:52:00 -0400 Subject: [PATCH 09/12] Draft 3 (read extended description) * Provide new suggestion for handling untyped metadata errors. * Add `Array` and `Dynamic` to allowed arguments * Remove `@:metadataCompileOnly`, `@:metadataRequire`, `@:metadataRequire`, `@:metadataPlatforms` * Add options argument to `@:metadata`: ```haxe @:metadata({ allowMultiple: Bool, compileTime: Bool, platforms: [] }) ``` * Replace `MetadataEntryTools` with `typedMeta: StringMap` field on certain structures. * Added `Context.typeMetadata` function --- proposals/0000-typed-metadata.md | 249 ++++++++++++++++++------------- 1 file changed, 149 insertions(+), 100 deletions(-) diff --git a/proposals/0000-typed-metadata.md b/proposals/0000-typed-metadata.md index d6d3501..78ffc01 100644 --- a/proposals/0000-typed-metadata.md +++ b/proposals/0000-typed-metadata.md @@ -87,10 +87,10 @@ There's a lot to cover here. A table has been provided for your convenience: | Topic | Description | | --- | --- | | [Basic Rules](0000-typed-metadata.md#basic-rules) | The basic syntax and rules for a metadata "function" declaration. | +| [@:metadata Arguments](0000-typed-metadata.md#metadata-arguments) | The properies to configure @:metadata. | | [Haxe API Changes](0000-typed-metadata.md#haxe-api-changes) | The changes to the Haxe API required. | -| [New Metadata ](0000-typed-metadata.md#new-metadata) | List of new metadata used to configure metadata functions. | | [Allowed Argument Types](0000-typed-metadata.md#allowed-argument-types) | List of argument types allowed for a typed metadata. | -| [Allowed Return Types](0000-typed-metadata.md#allowed-return-types) | List of return types allowed for a typed metadata. | +| [Allowed Return Types](0000-typed-metadata.md#allowed-return-types) | List of return types allowed for a typed metadata. | | [Decorators](0000-typed-metadata.md#decorators) | The design of metadata that runs code from its function body. |   @@ -105,6 +105,8 @@ Metadata functions are permitted to lack an implementation (similar to `extern` @:metadata function myMeta(): Any; ``` +  + ### Metadata Function Restrictions The `@:metadata` meta may only be used on module-level or static functions. @@ -127,6 +129,8 @@ Type parameters are not allowed on metadata functions. @:metadata function myMeta(); // error: Type parameters disalloweed on metadata functions. ``` +  + ### Metadata Scoping/Importing Typed metadata can be used on anything that allows metadata on it currently. @@ -147,11 +151,28 @@ import mypack.MyModule; function doThing() { ... } ``` +  + ### Untyped Metadata If the Haxe compiler encounters a metadata entry it cannot type, its behavior is currently an [Unresolved Question](0000-typed-metadata.md#unresolved-question). -For the time being, this proposal suggests throwing an error _unless_ `-D allow-untyped-meta` is defined. +For the time being, this proposal suggests printing an error for each metadata entry that could not be typed (no metadata function could be found for its name/path) ONLY IF within a module that meets the following conditions: + * A `@:metadata` function is declared in that module. + * A `@:metadata` function is imported. + * A module with a `@:metadata` function is imported (including wildcard imports). + * At least one metadata in the module has been successfully typed (this counts even if its arguments do not pass typing). + +This ensures any user that intends to use typed metadata will receive proper typing. A define can be used to enforce metadata typing on all code used in a Haxe project: `-D strict-meta-typing` + +To use an untyped metadata in a "typed metadata" context, the `@:untypedMeta` metadata should be used: +```haxe +@:untypedMeta(something) +@:untypedMeta(another(1, "test")) +class MyClass {} +``` + +  ### Metadata Target @@ -170,6 +191,8 @@ The `Any` type denotes a metadata can be used anywhere. The `haxe.macro.Expr` re @:metadata function intMeta(): Int; ``` +  + ### Basic Meta Arguments Arguments can be added to the metadata functions. Like with return types, there are only certain types allowed. A full list can be found at [Allowed Argument Types](0000-typed-metadata.md#allowed-argument-types). @@ -202,6 +225,52 @@ Outside the restriction of certain types, arguments should work exactly the same     +## @:metadata Arguments + +There needs to be a way for metadata functions to configure a couple options: + * Can it be used multiple times on the same subject? + * Is it compile-time only (uses `@:`)? Or should it exist as rtti. + * Is it restricted to one or more platforms? + * Does it require another metadata to function? + +To resolve these, the `@:metadata` metadata provides a couple options that can be configured. The declaration for the `@:metadata` metadata would look something like this: +```haxe +@:metadata function metadata(?options: { + ?allowMultiple: Bool, + ?compileTime: Bool, + ?platforms: Array +}): haxe.macro.Expr.Function; +``` + +| Argument Name | Default Value | Description | +| --- | --- | --- | +| allowMultiple | `false` | If `true`, this metadata can be used on the same subject multiple times. | +| compileTime | `false` | If `true`, this metadata must be prefixed with a colon and does not generate rtti. | +| platforms | `[]` | If this Array contains at least one entry, this metadata can only be used on the platforms named. | + +These options are optional, but they can be overriden if needed: +```haxe +@:metadata({ allowMultiple: true }) +function author(name: String): Any; + +@:metadata({ compileTime: true }) +function tempData(): Any; + +@:metadata({ allowMultiple: true, platforms: ["java", "cs"] }) +function nativeMeta(m: Expr): Any; + +// --- + +@author("Me") +@author("You") +@:tempData +function myFunc() { +} +``` + +  +  + ## Haxe API Changes A new optional field should be added to `haxe.macro.Expr.MetadataEntry`. @@ -213,28 +282,65 @@ If this metadata entry is typed, then `field` will contain a reference to the `C var ?field: Expr.Field; ``` +  + ### Reading Arguments -There needs to be a mechanism for reading metadata arguments. To provide this, new class should be added: `haxe.macro.MetadataEntryTools`. +There needs to be a mechanism for reading metadata arguments. To provide this, a new field `typedMeta: StringMap` should be added to: + * `haxe.macro.Expr.TypeDefinition` + * `haxe.macro.Expr.Field` + * `haxe.macro.Expr.TypeParamDecl` -This class provides static extension functions for `MetadataEntry` for reading arguments. Each function attempts to read an argument for a specific type. For example, for the `Int` argument type, there should be a `getInt(index: Int)` function that looks like this: +The entires correlate directly to the full path of the metadata used on the subject. So to access the content of an `@Meta.date` metadata, `_.typedMeta.get("mypack.Meta.date")` must be used. This is to prevent naming conflicts. There may be multiple metadata of the same name in different modules. +The `Dynamic` value contains fields with the same name as the arguments of the typed metadata. These fields store the values passed to the metadata entry. How these values are converted can be viewed in [Allowed Argument Types](0000-typed-metadata.md#allowed-argument-types). + +If the metadata has `allowMultiple` enabled, the `Dynamic` value will ALWAYS be an Array, even if only one instance of the metadata is used. ```haxe -static function getInt(entry: MetadataEntry, index: Int): Null { - return if(entry.params != null && index < entry.params.length) { - switch(entry.params[index].expr) { - case EConst(CInt(v)): Std.parseInt(v); - case _: null; - } - } else { - null; - } +// MyModule.hx +package mypack; + +@:metadata({ allowMultiple: true }) +function author(name: String): TypeDefinition; + +class Meta { + @:metadata + public static function date(month: Int, day: Int): TypeDefinition; +} +class AnotherMeta { + @:metadata + public static function date(dateString: String): TypeDefinition; } + +@author("Something") +@Meta.date(11, 15) +@AnotherMeta.date("November 15, 2004") +class MyClass {} ``` -There should be one function for every possible argument type. +```haxe +// --- +// in some compile-time function +// var td: TypeDefinition; +final authorNames: Null> = td.typedMeta.get("mypack.MyModule.author")?.map(meta -> meta.name); -These functions should not throw any errors; that is the job of the Haxe compiler on typed metadata. Instead `null` is returned if the argument doesn't exist or doesn't match the desired type. Technically, these could also be used on untyped metadata. +final dateMonth: Int = td.typedMeta.get("mypack.MyModule.Meta.date")?.month; +``` + +  + +### Context.typeMetadata + +A new static function should be added to `haxe.macro.Context`: +```haxe +class Context { + // ... + public static function typeMetadata(meta: haxe.macro.Expr.Metadata): StringMap { ... } +``` + +This is a function that will generate an object like the `typedMeta: StringMap` field described in the previous section. This would be helpful for extracting typed metadata data found in untyped `EMeta` expressions. + +  ### Function Type Struct @@ -253,91 +359,25 @@ Technically, function path data _could_ be stored in `TypePath`, but that's not     -## New Metadata - -There needs to be a way for metadata functions to configure a couple options: - * Can it be used multiple times on the same subject? - * Is it compile-time only? Or should it exist as rtti. - * Is it restricted to one or more platforms? - * Does it require another metadata to function? - -While likely not set in stone, this proposal recommends adding the following metadata to be used in combination with `@:metadata` to configure these options: -```haxe -/** - Use on a metadata function. That meta will only exist at - compile-time (will not generate any rtti data). - - Unresolved question: Should this force this meta to - be prefixed with a colon? There needs to be a way to signify - that when typing built-in Haxe metadata in the future. -**/ -@:metadata -@metadataCompileOnly -@metadataRequire(@:metadata _) -function metadataCompileOnly(): haxe.macro.Expr.Function; - -/** - Use on a metadata function. - - Generates an error if the metadata function is - used on a subject without the `other` metadata - - For example: `@:metadataRequire(@:metadata _)` only allows - this meta to be used in combination with `@:metadata`. -**/ -@:metadata -@metadataCompileOnly -@metadataRequire(@:metadata _) -function metadataRequire(...other: haxe.macro.Expr.MetadataEntry): haxe.macro.Expr.Function; - -/** - Use on a metadata function. - - Allows this metadata to be used multiple times on the - same subject. Otherwise, an error is thrown if the same - metadata is used multiple times. -**/ -@:metadata -@metadataCompileOnly -@metadataRequire(@:metadata _) -function metadataAllowMulti(): haxe.macro.Expr.Function; - -/** - Use on a metadata function. - - Restricts the meta to only be used on specific platforms. - - NOTE: - If untyped metadata throw an error, this is unnecessary! Instead - conditional compilation can be used to only define a metadata - if a target's "define" is defined. (i.e. `#if js ... #end`) -**/ -@:metadata -@metadataCompileOnly -@metadataRequire(@:metadata _) -function metadataPlatforms(...platformName: String): haxe.macro.Expr.Function; -``` - -  -  - ## Allowed Argument Types The following is the full list of allowed argument types for metadata. -| Type | Expression Must Match | Decorator Argument Value | MetadataEntryTools Getter | Description | -| --- | --- | --- | --- | --- | -| `Bool` | `EConst(CIdent("true" \| "false"))` | `v == "true"` | `getBool` | Allows either `true` or `false`. | -| `Int` | `EConst(CInt(v))` | `Std.parseInt(v)` | `getInt` | Allows an integer literal. | -| `Float` | `EConst(CFloat(v))` or `EConst(CInt(v))` | `Std.parseFloat(v)` | `getFloat` | Allows an float literal. | -| `String` | `EConst(CString(v, DoubleQuotes))` | `v` | `getString` | Allows a string literal. Let there be unique error message if `SingleQuotes` is used. | -| `EReg` | `EConst(CRegexp(s, opt))` | `new EReg(s, opt)` | `getRegex` | Allows a regular expression literal. | -| `haxe.macro.Expr.Var` | `EVars([v])` | `v` | `getVarDecl` | Allows variable declaration expression. | -| `haxe.macro.Expr` | `e` | `e` | `getExpr` | Allows any expression. The expression object is passed directly. | -| `haxe.macro.Expr.TypePath` | `EConst(CIdent(_))` or `EField(_, _)` | ??? | `getTypePath` | Allows a type path. The expression will be converted to a `TypePath` manually by the Haxe compiler. Furthermore, it's only valid if the type path follows Haxe package/module naming rules (packages must be lowercase, module and sub names must start with uppercase). | -| `haxe.macro.Expr.FunctionPath` | `EConst(CIdent(_))` or `EField(_, _)` | ??? | `getFunctionPath` | Same as `TypePath`, but when converting/validating from an expression, this allows the final identifier to start with a lowercase letter. | -| `haxe.macro.Expr.ComplexType` | `ECheckType({ expr: EConst(EIdent("\_")) }, complexType)` | `complexType` | `getComplexType` | Allows any type. Must format as `_ : Type` to comply with expression parsing. | -| `haxe.macro.Expr.MetadataEntry` | `EMeta(metaEntry, { expr: EConst(EIdent("\_")) })` | `metaEntry` | `getMetadataEntry` | Allows any metadata. Must format as `@:meta _` to comply with expression parsing. | +| Type | Expression Must Match | Decorator Argument Value | Description | +| --- | --- | --- | --- | +| `Bool` | `EConst(CIdent("true" \| "false"))` | `v == "true"` | Allows either `true` or `false`. | +| `Int` | `EConst(CInt(v))` | `Std.parseInt(v)` | Allows an integer literal. | +| `Float` | `EConst(CFloat(v))` or `EConst(CInt(v))` | `Std.parseFloat(v)` | Allows an float literal. | +| `String` | `EConst(CString(v, DoubleQuotes))` | `v` | Allows a string literal. Let there be unique error message if `SingleQuotes` is used. | +| `EReg` | `EConst(CRegexp(s, opt))` | `new EReg(s, opt)` | Allows a regular expression literal. | +| `haxe.macro.Expr.Var` | `EVars([v])` | `v` | Allows variable declaration expression. | +| `haxe.macro.Expr` | `e` | `e` | Allows any expression. The expression object is passed directly. | +| `Array` | `EArrayDecl(_)` | ??? | Allows array declarations. `TYPE` should be a from this list. Requires some internal logic to convert `Array` into the `TYPE`. | +| `{ name: TYPE, ... }` | `EObjectDecl(_)` | ??? | Allows object declarations. All types used should be from this list. Requires some internal logic to convert `Array` into a `Dynamic` with the fields. | +| `haxe.macro.Expr.TypePath` | `EConst(CIdent(_))` or `EField(_, _)` | ??? | Allows a type path. The expression will be converted to a `TypePath` manually by the Haxe compiler. Furthermore, it's only valid if the type path follows Haxe package/module naming rules (packages must be lowercase, module and sub names must start with uppercase). | +| `haxe.macro.Expr.FunctionPath` | `EConst(CIdent(_))` or `EField(_, _)` | ??? | Same as `TypePath`, but when converting/validating from an expression, this allows the final identifier to start with a lowercase letter. | +| `haxe.macro.Expr.ComplexType` | `ECheckType({ expr: EConst(EIdent("\_")) }, complexType)` | `complexType` | Allows any type. Must format as `_ : Type` to comply with expression parsing. | +| `haxe.macro.Expr.MetadataEntry` | `EMeta(metaEntry, { expr: EConst(EIdent("\_")) })` | `metaEntry` | Allows any metadata. Must format as `@:meta _` to comply with expression parsing. |     @@ -361,6 +401,8 @@ The following is the full list of allowed return types for metadata. A typed metadata that has code in its function body is called a "decorator". A decorator's code is run for every entry of the typed metadata. +  + ### Context.getDecoratorSubject() To retrieve information about the subject of the decorator, `Context.getDecoratorSubject` is a new `Context` function that may be used. @@ -395,6 +437,8 @@ enum DecoratorSubjectTarget { } ``` +  + ### Custom Decorator Validator Decorators do not need to return a value. If `null` is returned, the decorator will not affect its subject. Developers can use this to write their own logic for ensuring their metadata is used correctly. @@ -420,18 +464,21 @@ If one's metadata should only be used on a SPECIFIC type of expression or a SPEC } ``` +  + ### Subject-Modifying Decorator If a decorator's function returns an non-null instance of its return type, that instance will replace the decorator's subject at compile-time. ```haxe -@:metadata function makeZero(): haxe.macro.Expr { +@:metadata({ compileTime: true }) +function makeZero(): haxe.macro.Expr { return macro 0; } // --- -trace(@makeZero "Hello!"); // Main.hx:1: 0 +trace(@:makeZero "Hello!"); // Main.hx:1: 0 ``` ```haxe @@ -521,7 +568,9 @@ There is currently no alternatives for decorators on type definitions. # Unresolved questions -Should untyped metadata throw an error? While it would be a major breaking change, it would be nice restrict metadata usage using conditional compilation (wrap with `#if js` for example) instead of using something like `@:metadataPlatform`. Maybe it could be a warning? Maybe errors can default to on, but turn off with a define (or vise versa)? +Should a new metadata syntax be used: `@.myMeta`? This would ensure all new metadata could be typed properly. + +If no `@.` syntax, should untyped metadata throw an error? While it would be a major breaking change, it would be nice restrict metadata usage using conditional compilation (wrap with `#if js` for example) instead of using something like `@:metadataPlatform`. Maybe it could be a warning? Maybe errors can default to on, but turn off with a define (or vise versa)? How should colons be handled? If the current built-in Haxe metadata is going to be typed, there should probably be a way to set a metadata to use a colon to ensure compatibility. However, it might be prefered to encourage/enforce that users are only make typed metadata without a colon? For the time being, `@metadataCompileOnly` answers this question by requiring a colon but not generating rtti. From 489ff3096d74691823ea3ed9200a205736c4de0b Mon Sep 17 00:00:00 2001 From: RoBBoR <28355157+RobertBorghese@users.noreply.github.com> Date: Thu, 18 May 2023 07:38:41 -0400 Subject: [PATCH 10/12] Convert FunctionPath -> FieldPath --- proposals/0000-typed-metadata.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/0000-typed-metadata.md b/proposals/0000-typed-metadata.md index 78ffc01..f4be115 100644 --- a/proposals/0000-typed-metadata.md +++ b/proposals/0000-typed-metadata.md @@ -342,13 +342,13 @@ This is a function that will generate an object like the `typedMeta: StringMap TypePath, - functionName: String; + field: String; }; ``` @@ -375,7 +375,7 @@ The following is the full list of allowed argument types for metadata. | `Array` | `EArrayDecl(_)` | ??? | Allows array declarations. `TYPE` should be a from this list. Requires some internal logic to convert `Array` into the `TYPE`. | | `{ name: TYPE, ... }` | `EObjectDecl(_)` | ??? | Allows object declarations. All types used should be from this list. Requires some internal logic to convert `Array` into a `Dynamic` with the fields. | | `haxe.macro.Expr.TypePath` | `EConst(CIdent(_))` or `EField(_, _)` | ??? | Allows a type path. The expression will be converted to a `TypePath` manually by the Haxe compiler. Furthermore, it's only valid if the type path follows Haxe package/module naming rules (packages must be lowercase, module and sub names must start with uppercase). | -| `haxe.macro.Expr.FunctionPath` | `EConst(CIdent(_))` or `EField(_, _)` | ??? | Same as `TypePath`, but when converting/validating from an expression, this allows the final identifier to start with a lowercase letter. | +| `haxe.macro.Expr.Field` | `EConst(CIdent(_))` or `EField(_, _)` | ??? | Same as `TypePath`, but when converting/validating from an expression, this allows the final identifier to start with a lowercase letter. | | `haxe.macro.Expr.ComplexType` | `ECheckType({ expr: EConst(EIdent("\_")) }, complexType)` | `complexType` | Allows any type. Must format as `_ : Type` to comply with expression parsing. | | `haxe.macro.Expr.MetadataEntry` | `EMeta(metaEntry, { expr: EConst(EIdent("\_")) })` | `metaEntry` | Allows any metadata. Must format as `@:meta _` to comply with expression parsing. | From e0802d3f1d21382a7740ea12e23eeaaf97828d8a Mon Sep 17 00:00:00 2001 From: RoBBoR <28355157+RobertBorghese@users.noreply.github.com> Date: Thu, 18 May 2023 07:44:20 -0400 Subject: [PATCH 11/12] Change "compileTime" option to "rtti" (reverse default option) --- proposals/0000-typed-metadata.md | 49 ++++++++++++++++---------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/proposals/0000-typed-metadata.md b/proposals/0000-typed-metadata.md index f4be115..2e59c3b 100644 --- a/proposals/0000-typed-metadata.md +++ b/proposals/0000-typed-metadata.md @@ -17,7 +17,8 @@ A typing system for Haxe metadata that can validate its arguments and optionally /** Define an author for a type definition. **/ -@:metadata function author(name: String): haxe.macro.Expr.TypeDefinition; +@:metadata({ rtti: true }) +function author(name: String): haxe.macro.Expr.TypeDefinition; /** Transform an `Expr` to execute only if the `input` @@ -44,7 +45,7 @@ import mypack.Meta; @author("Anonymous") class MyClass { public function printPlus3(a: Null) { - @ifSome(a) { + @:ifSome(a) { trace(a + 3); } } @@ -137,7 +138,7 @@ Typed metadata can be used on anything that allows metadata on it currently. However, it follows the same scoping rules as Haxe functions. Meaning it must use its full path or be imported: ```haxe -@mypack.MyModule.myMeta +@:mypack.MyModule.myMeta function doThing() { ... } // OR @@ -147,7 +148,7 @@ import mypack.MyModule; // static function: @MyModule.myMeta // module level: @myMeta -@myMeta +@:myMeta function doThing() { ... } ``` @@ -200,23 +201,23 @@ Arguments can be added to the metadata functions. Like with return types, there Outside the restriction of certain types, arguments should work exactly the same as they do on normal functions. This includes support for: optional arguments, default arguments, and rest arguments. ```haxe @:metadata function oneNum(num: Int): Any; -@oneNum(123) function doThing() { ... } +@:oneNum(123) function doThing() { ... } // default args @:metadata function maybeNum(num: Int = 0): Any; -@maybeNum function doThing() { ... } -@maybeNum(123) function doThing2() { ... } +@:maybeNum function doThing() { ... } +@:maybeNum(123) function doThing2() { ... } // optional args @:metadata function numAndStr(?num: Int, str: String): Any; -@numAndStr(123, "test") function doThing() { ... } -@numAndStr("test") function doThing2() { ... } +@:numAndStr(123, "test") function doThing() { ... } +@:numAndStr("test") function doThing2() { ... } // rest args @:metadata function numRest(...num: Int): Any; -@numRest function doThing() { ... } -@numRest(1) function doThing2() { ... } -@numRest(1, 2, 3) function doThing3() { ... } +@:numRest function doThing() { ... } +@:numRest(1) function doThing2() { ... } +@:numRest(1, 2, 3) function doThing3() { ... } // error: Type `haxe.Exception` is not valid argument type for metadata function. @:metadata function invalidType(o: haxe.Exception): Any; @@ -237,7 +238,7 @@ To resolve these, the `@:metadata` metadata provides a couple options that can b ```haxe @:metadata function metadata(?options: { ?allowMultiple: Bool, - ?compileTime: Bool, + ?rtti: Bool, ?platforms: Array }): haxe.macro.Expr.Function; ``` @@ -245,16 +246,16 @@ To resolve these, the `@:metadata` metadata provides a couple options that can b | Argument Name | Default Value | Description | | --- | --- | --- | | allowMultiple | `false` | If `true`, this metadata can be used on the same subject multiple times. | -| compileTime | `false` | If `true`, this metadata must be prefixed with a colon and does not generate rtti. | +| rtti | `false` | If `true`, this metadata should not use a colon and will generate [rtti information](https://haxe.org/manual/cr-rtti.html). | | platforms | `[]` | If this Array contains at least one entry, this metadata can only be used on the platforms named. | These options are optional, but they can be overriden if needed: ```haxe -@:metadata({ allowMultiple: true }) +@:metadata({ rtti: true }) function author(name: String): Any; -@:metadata({ compileTime: true }) -function tempData(): Any; +@:metadata({ allowMultiple: true }) +function tempData(e: Expr): Any; @:metadata({ allowMultiple: true, platforms: ["java", "cs"] }) function nativeMeta(m: Expr): Any; @@ -262,8 +263,8 @@ function nativeMeta(m: Expr): Any; // --- @author("Me") -@author("You") -@:tempData +@:tempData(123) +@:tempData("Hello") function myFunc() { } ``` @@ -300,7 +301,7 @@ If the metadata has `allowMultiple` enabled, the `Dynamic` value will ALWAYS be // MyModule.hx package mypack; -@:metadata({ allowMultiple: true }) +@:metadata({ allowMultiple: true, rtti: true }) function author(name: String): TypeDefinition; class Meta { @@ -313,8 +314,8 @@ class AnotherMeta { } @author("Something") -@Meta.date(11, 15) -@AnotherMeta.date("November 15, 2004") +@:Meta.date(11, 15) +@:AnotherMeta.date("November 15, 2004") class MyClass {} ``` @@ -471,7 +472,7 @@ If one's metadata should only be used on a SPECIFIC type of expression or a SPEC If a decorator's function returns an non-null instance of its return type, that instance will replace the decorator's subject at compile-time. ```haxe -@:metadata({ compileTime: true }) +@:metadata function makeZero(): haxe.macro.Expr { return macro 0; } @@ -494,7 +495,7 @@ trace(@:makeZero "Hello!"); // Main.hx:1: 0 // --- -@changeName("YourClass") +@:changeName("YourClass") class MyClass { public function new() {} } From 04fe870f98f2a12bfb932da3b0690897eb88ae4d Mon Sep 17 00:00:00 2001 From: RoBBoR <28355157+RobertBorghese@users.noreply.github.com> Date: Thu, 18 May 2023 07:46:13 -0400 Subject: [PATCH 12/12] Minor spelling mistake --- proposals/0000-typed-metadata.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0000-typed-metadata.md b/proposals/0000-typed-metadata.md index 2e59c3b..62fa78a 100644 --- a/proposals/0000-typed-metadata.md +++ b/proposals/0000-typed-metadata.md @@ -127,7 +127,7 @@ function main() { Type parameters are not allowed on metadata functions. ```haxe -@:metadata function myMeta(); // error: Type parameters disalloweed on metadata functions. +@:metadata function myMeta(); // error: Type parameters disallowed on metadata functions. ```