Thread overview
June 16

Compilers utilise a technique that enables the linker to produce a list of symbols or data, available for iteration over. This is used by D compilers for a number of use cases such as ModuleInfo.

However, the method that each linker supports does vary and changes over time, so it is unsuitable as a general pattern that people should rely upon. It is also not supported in all compilers either.

Putting the behaviour into the compiler guarantees that it has ability to work, if D were to work on a given target. Consistently, in a way that does not rely upon knowledge of how linkers work.

Four new behaviours here are needed:

  1. A special identifier is added to alias template parameters called __udasymbol, this will be populated by the compiler by default with the symbol that the UDA is placed upon.

  2. A new type is introduced, called __linkerlist, this type is always __gshared, it cannot be copied nor heap allocated. It may be located in object.d or in language.

    2.1. It has two methods on it, opApply and opOpAssign.

    2.2. The opApply method is only callable at runtime, doing so at CT would affect reproducibility of builds.

    2.3. The opOpAssign method is limited to appending opOpAssign!"~", it is is only callable at CT, it is used to append to the list.

The benefits of this are that it can minimise templates and start-up times in the registration of symbols. This is used quite often in web development.

It is likely that you will want to have per-binary linker lists, if so, you will need a proposal such as what is described in this ticket: https://github.com/dlang/dmd/issues/21455

An example of this:

struct __linkerlist(Type) {
    int opApply(int delegate(immutable(Type)*) del); // not callable at CT
    void opOpAssign(string op:"~")(Type); // only callable at CT
}

struct RouteInfo {
    string path;
    void function() func;
}

__linkerlist!RouteInfo routes;

void Route(alias __udasymbol)(string path) {
    routes ~= RouteInfo(path, &__udasymbol);
}

@Route("/path") // but actually is: @Route!myRoute("/path")
void myRoute() {

}

void main() {
    import std.stdio;
    foreach(immutable(RouteInfo)* route; routes) {
        writeln(route.path);
    }
}
June 20

On Monday, 16 June 2025 at 12:21:59 UTC, Richard (Rikki) Andrew Cattermole wrote:

>

Compilers utilise a technique that enables the linker to produce a list of symbols or data, available for iteration over. This is used by D compilers for a number of use cases such as ModuleInfo.

[...]

>

On Monday, 16 June 2025 at 12:21:59 UTC, Richard (Rikki) Andrew Cattermole wrote:

I needed something like this many times. Don't know how easy it is to do with separate compilation, but I'm definitely adding this to my language (which build whole executable at once).

19 hours ago
On Monday, 16 June 2025 at 12:21:59 UTC, Richard (Rikki) Andrew Cattermole wrote:
> Four new behaviours here are needed:

How well would this interact with dead-code elimination?
18 hours ago
On 30/06/2025 5:52 AM, 0xEAB wrote:
> On Monday, 16 June 2025 at 12:21:59 UTC, Richard (Rikki) Andrew Cattermole wrote:
>> Four new behaviours here are needed:
> 
> How well would this interact with dead-code elimination?

Any symbol that is in the linker list is a symbol a human wanted to be in the binary.

It will effect the object file being pulled in like ModuleInfo does, it shouldn't.

Is there a scenario that you think will effect dead code elimination that isn't the above?

16 hours ago
On Sunday, 29 June 2025 at 19:10:34 UTC, Richard (Rikki) Andrew Cattermole wrote:
> Is there a scenario that you think will effect dead code elimination that isn't the above?

Thanks for elaborating!
No, I was just curious.