www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - [core.reflect] TemplateInstance reflection

reply Stefan Koch <uplink.coder googlemail.com> writes:
Good Evening everyone.

Today I have made progress with reflecting over template 
instances.

TLDR. core.reflect can almost replace `__traits` now and 
preliminary tests show that the reflection data-creation scales 
linearly.
Meaning 10 times as much code takes 10 times as much time to 
reflect.


The following code:
```D
import core.reflect.reflect;

template TypeDef(string name, T)
{
     struct TypeDef
     {
         T value;
         alias value this;
     }
}

alias myInt = TypeDef!("myInt", int);

pragma(msg, () {
     auto n = nodeFromName("myInt", ReflectFlags.Everything);
     string[immutable(ulong)] cmap;
     return nodeToString(n, &cmap);
} ());
```

```JSON
AliasDeclaration = {
     internalPointer: unhandled,
     serial: 599,
     astTypeName: "AliasDeclaration",
     name: "myInt",
     attributes: [],
     linkage: Default,
     comment: "",
     mangledName: "",
     location: Location = {
         filename: "testTemplateReflection.d",
         line: 12,
         column: 1,
     },
     parent: Module = {
         internalPointer: unhandled,
         serial: 580,
         astTypeName: "Module",
         _scope: Scope = {
             internalPointer: unhandled,
             serial: 0,
             astTypeName: "Scope",
         },
         identifier: "testTemplateReflection",
         parent: null,
         members: [
             DeclarationRef to 'myInt',
         ],
         packages: [],
         source_filename: "testTemplateReflection.d",
         string_import_filenames: [],
         declDefs: [],
     },
     storageClasses: [],
     type: TypeStruct = {
         internalPointer: unhandled,
         serial: 31545,
         astTypeName: "TypeStruct",
         kind: "struct",
         alignSize: 4,
         size: 4,
         identifier: "TypeDef!("myInt", int)",
         _unqualified: null,
         sym: StructDeclaration = {
             internalPointer: unhandled,
             serial: 31544,
             astTypeName: "StructDeclaration",
             name: "TypeDef",
             attributes: [],
             linkage: D,
             comment: "",
             mangledName: 
"22testTemplateReflection__T7TypeDefVAyaa5_6d79496e74TiZQBc",
             location: Location = {
                 filename: "testTemplateReflection.d",
                 line: 5,
                 column: 5,
             },
             parent: TemplateInstance = {
                 internalPointer: unhandled,
                 serial: 597,
                 astTypeName: "TemplateInstance",
                 _scope: null,
                 identifier: "",
                 parent: Module = {
                     internalPointer: unhandled,
                     serial: 580,
                     astTypeName: "Module",
                     _scope: Scope = {
                         internalPointer: unhandled,
                         serial: 0,
                         astTypeName: "Scope",
                     },
                     identifier: "testTemplateReflection",
                     parent: null,
                     members: [
                         DeclarationRef to 'myInt',
                     ],
                     packages: [],
                     source_filename: "testTemplateReflection.d",
                     string_import_filenames: [],
                     declDefs: [],
                 },
                 members: [
                     DeclarationRef to 'TypeDef',
                 ],
                 name: "TypeDef",
                 decl: TemplateDeclaration = {
                     internalPointer: unhandled,
                     serial: 594,
                     astTypeName: "TemplateDeclaration",
                     name: "TypeDef",
                     attributes: [],
                     linkage: Default,
                     comment: "",
                     mangledName: 
"22testTemplateReflection7TypeDef",
                     location: Location = {
                         filename: "testTemplateReflection.d",
                         line: 3,
                         column: 1,
                     },
                     parent: Module = {
                         internalPointer: unhandled,
                         serial: 580,
                         astTypeName: "Module",
                         _scope: Scope = {
                             internalPointer: unhandled,
                             serial: 0,
                             astTypeName: "Scope",
                         },
                         identifier: "testTemplateReflection",
                         parent: null,
                         members: [
                             DeclarationRef to 'myInt',
                         ],
                         packages: [],
                         source_filename: 
"testTemplateReflection.d",
                         string_import_filenames: [],
                         declDefs: [],
                     },
                     storageClasses: [],
                     isEponymous: true,
                     constraint: null,
                 },
             },
             storageClasses: [],
             type: TypeRef to 'TypeDef!("myInt", int)',
             fields: [
                 VariableDeclaration = {
                     internalPointer: unhandled,
                     serial: 31547,
                     astTypeName: "VarDeclaration",
                     name: "value",
                     attributes: [],
                     linkage: D,
                     comment: "",
                     mangledName: 
"_D22testTemplateReflection__T7TypeDefVAyaa5_6d79496e74TiZQBc5valuei",
                     location: Location = {
                         filename: "testTemplateReflection.d",
                         line: 7,
                         column: 11,
                     },
                     parent: DeclarationRef to 'TypeDef',
                     storageClasses: [
                         Field,
                         Ctorinit,
                     ],
                     type: TypeBasic = {
                         internalPointer: unhandled,
                         serial: 138,
                         astTypeName: "TypeBasic",
                         kind: "int",
                         alignSize: 4,
                         size: 4,
                         identifier: "int",
                         _unqualified: null,
                     },
                     _init: null,
                     offset: unhandled,
                 },
             ],
             members: [
                 DeclarationRef to 'value',
             ],
         },
     },
     decl: null,
}

```

As you can see it is possible to look through the type alias 
myInt Into the template-instance TypeDef!("myInt", int) and 
through there into the eponymous struct Declaration.

I am aware that the produced tree is fairly big however I've 
thought that a more faithful representation of the Tree inside 
the compiler would be useful to more people than a cut down 
version which is tailored to specific use-cases that I know of.

As always I would be very happy about positive feedback or 
constructive criticism.

Have a very nice day/night/evening/morning.

Cheers,
Stefan
Sep 30 2021
next sibling parent russhy <russhy gmail.com> writes:
Playing with traits is often very tedious, i can already see the 
advantages of using core.reflect instead, and it's working with 
templates, impressive work
Sep 30 2021
prev sibling next sibling parent rikki cattermole <rikki cattermole.co.nz> writes:
On 01/10/2021 11:19 AM, Stefan Koch wrote:
 TLDR. core.reflect can almost replace `__traits` now and preliminary 
 tests show that the reflection data-creation scales linearly.
 Meaning 10 times as much code takes 10 times as much time to reflect.
All heil the linear O(n)! May it ever get cheaper!
Oct 01 2021
prev sibling next sibling parent reply Elronnd <elronnd elronnd.net> writes:
On Thursday, 30 September 2021 at 22:19:33 UTC, Stefan Koch wrote:
 preliminary tests show that the reflection data-creation scales 
 linearly
I feel like all I ever do when you show off your incredible work on core.reflect or newctfe is propose completely impractical changes. Well, let me propose another one :) Have you considered using the same in-memory layout for the compiler's own structures as for the core.reflect ones, so it's not linear-time but constant-time?
Oct 03 2021
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Monday, 4 October 2021 at 04:40:37 UTC, Elronnd wrote:
 On Thursday, 30 September 2021 at 22:19:33 UTC, Stefan Koch 
 wrote:
 preliminary tests show that the reflection data-creation 
 scales linearly
I feel like all I ever do when you show off your incredible work on core.reflect or newctfe is propose completely impractical changes. Well, let me propose another one :) Have you considered using the same in-memory layout for the compiler's own structures as for the core.reflect ones, so it's not linear-time but constant-time?
I am not sure what you mean with impractical changes. It's true that newCTFE turned out to be much bigger in scope and required work than anticipated. Whereas core.reflect is actually manageable. I have considered just exposing the compiler internal data-structures directly. But that would be completely unmaintainable as the ABI would break on nearly every compiler release. The reason core.reflect uses it's own class hierarchy is to have a buffer between what is exposed to the user and the compiler internals. Such that there is at least the possibility of changing one without having to change the other at the same time.
Oct 04 2021
next sibling parent Stefan Koch <uplink.coder googlemail.com> writes:
On Monday, 4 October 2021 at 07:26:30 UTC, Stefan Koch wrote:
 On Monday, 4 October 2021 at 04:40:37 UTC, Elronnd wrote:
 On Thursday, 30 September 2021 at 22:19:33 UTC, Stefan Koch 
 wrote:
 preliminary tests show that the reflection data-creation 
 scales linearly
I feel like all I ever do when you show off your incredible work on core.reflect or newctfe is propose completely impractical changes. Well, let me propose another one :) Have you considered using the same in-memory layout for the compiler's own structures as for the core.reflect ones, so it's not linear-time but constant-time?
I am not sure what you mean with impractical changes.
ahhh you said; _you_ are proposing impractical changes, not that I am proposing impractical changes .... :-) My bad. Yeah using the same structure is completely impractical.
Oct 04 2021
prev sibling parent reply bauss <jj_1337 live.dk> writes:
On Monday, 4 October 2021 at 07:26:30 UTC, Stefan Koch wrote:
 Such that there is at least the possibility of changing one 
 without having to change the other at the same time.
That would also help when it comes to user code, as users wouldn't have to change due to breaking changes, only core.reflect behind the scene would have to maintain breaking changes. Unless that's what you meant
Oct 04 2021
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Monday, 4 October 2021 at 09:31:45 UTC, bauss wrote:
 On Monday, 4 October 2021 at 07:26:30 UTC, Stefan Koch wrote:
 Such that there is at least the possibility of changing one 
 without having to change the other at the same time.
That would also help when it comes to user code, as users wouldn't have to change due to breaking changes, only core.reflect behind the scene would have to maintain breaking changes. Unless that's what you meant
That is exactly what I meant. The interface for the reflection consumer it supposed to be stable after it has shown that it is sufficient.
Oct 04 2021
parent bauss <jj_1337 live.dk> writes:
On Monday, 4 October 2021 at 09:46:03 UTC, Stefan Koch wrote:
 On Monday, 4 October 2021 at 09:31:45 UTC, bauss wrote:
 On Monday, 4 October 2021 at 07:26:30 UTC, Stefan Koch wrote:
 Such that there is at least the possibility of changing one 
 without having to change the other at the same time.
That would also help when it comes to user code, as users wouldn't have to change due to breaking changes, only core.reflect behind the scene would have to maintain breaking changes. Unless that's what you meant
That is exactly what I meant. The interface for the reflection consumer it supposed to be stable after it has shown that it is sufficient.
That's great! I'm really looking forward to this addition tbh.
Oct 04 2021
prev sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
On Thursday, 30 September 2021 at 22:19:33 UTC, Stefan Koch wrote:
 [snip]
In the code below you can only get the target of the alias, such as int and float. Would it be possible to use core.reflect or something similar, to be able to get back Foo or Foo!int? ```d template Foo(T) { static if(is(T == int)) alias Foo = int; else alias Foo = float; } void main() { Foo!int x; Foo!double y; static assert(is(typeof(x) == int)); static assert(is(typeof(y) == float)); } ```
Oct 04 2021
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Monday, 4 October 2021 at 11:54:09 UTC, jmh530 wrote:
 On Thursday, 30 September 2021 at 22:19:33 UTC, Stefan Koch 
 wrote:
 [snip]
In the code below you can only get the target of the alias, such as int and float. Would it be possible to use core.reflect or something similar, to be able to get back Foo or Foo!int? ```d template Foo(T) { static if(is(T == int)) alias Foo = int; else alias Foo = float; } void main() { Foo!int x; Foo!double y; static assert(is(typeof(x) == int)); static assert(is(typeof(y) == float)); } ```
You can get Foo by asking for the name "Foo" but it won't tell you much, right now as reflecting template declarations is a really tricky thing, they can have hidden errors in them, which puts the reflector in a bad state. Therefore I don't touch the bodies of template declarations right now.
Oct 04 2021
parent reply jmh530 <john.michael.hall gmail.com> writes:
On Monday, 4 October 2021 at 12:07:36 UTC, Stefan Koch wrote:
 [snip]

 You can get Foo by asking for the name "Foo"
 but it won't tell you much, right now as reflecting template 
 declarations is a really tricky thing, they can have hidden 
 errors in them, which puts the reflector in a bad state.
 Therefore I don't touch the bodies of template declarations 
 right now.
Tanks. I was thinking getting the name of `Foo` from `x` or `y`.
Oct 04 2021
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Monday, 4 October 2021 at 14:00:49 UTC, jmh530 wrote:
 On Monday, 4 October 2021 at 12:07:36 UTC, Stefan Koch wrote:
 [snip]

 You can get Foo by asking for the name "Foo"
 but it won't tell you much, right now as reflecting template 
 declarations is a really tricky thing, they can have hidden 
 errors in them, which puts the reflector in a bad state.
 Therefore I don't touch the bodies of template declarations 
 right now.
Tanks. I was thinking getting the name of `Foo` from `x` or `y`.
That isn't currently possible. DMD rewrites that before core.reflect sees the function body.
Oct 04 2021
parent jmh530 <john.michael.hall gmail.com> writes:
On Monday, 4 October 2021 at 16:04:32 UTC, Stefan Koch wrote:
 [snip]

 That isn't currently possible.
 DMD rewrites that before core.reflect sees the function body.
Yeah, I figured. I keep thinking about DIP 1021 (resolution of alias template parameters) and keeping that information around somehow (pragma?) seems like it would be needed in the more general case.
Oct 04 2021