Check this out. Modify your druntime slightly. In object.di, find template RTInfo(T) and add this bit:

    import core.config;
    alias checkResult = TypeCheck!T;

Then, pop open import/core/config.d and put these contents in it:

module core.config;
template TypeCheck(T) {
    enum TypeCheck = true;
}


Compile a program, any program. Should be no difference in anything - no errors, no changes to the binary, no compile slowdown - from before the modifications.


Now, create a file named anything you want with these contents:

module core.config;
template TypeCheck(T) {
  static if(T.stringof == "Cool")
    static assert(0, "No type named 'Cool' is allowed!");
  enum TypeCheck = true;
}


Compile your program, should still work. Then add "struct Cool{}" to it anywhere... and you get a compile error.



Brothers and sisters, I've talked to you before about doing custom RTInfo using __traits(compiles) tricks to look for the config module, but my previous ways - while they worked - sucked because the compiler would look for the module over and over again, slowing things down a bit.

This new method achieves success on all counts. By offering a file with druntime to be the module up front, the compiler does no special searching, it finds it. It is a trivial file, so impact on compile speed is negligible.

But then, if you offer your own file with the same module name on the command line, dmd looks THERE first, finds your special code, and knows to skip searching the import path! Even true for object.d, since this is all in templates with local imports; the template still needs to be instantiated.

Since this object.d doesn't actually store anything* in the RTInfo itself, there's no worry of binary incompatibilities resulting from separate compiliation with different rtinfo modules. It simply provides a hook into the types for checking.

Using this, we can automatically check all kinds of per-project requirements. Don't want virtual functions in your class unless they are reviewed? Make your TypeCheck loop over them and look for a UDA, issuing a static assert if not there. Don't want references to un-owned mutable data? Ditto. Have a naming convention to check? Can do that too.


I see lots of upsides, and the downsides from my last experiment with this I believe have been eliminated. Am I missing something? I wanna do a druntime pull request!


* User data extensions could potentially be allowed if RTInfo stored a pointer to an interface, which may be null. Other modules may implement the interface differently, but as long as they implement it (or set it to null), it should still have consistent binary compatibility.

You might make object.d's RTInfo create a static struct with information the GC needs first, then a pointer to the user defined data. The RTInfo template ultimately resolves to the pointer to that struct. The data itself is consistent - still defined in only one place, druntime itself, and the GC info is available right there, no added indirection, and we have the hook for user additions too, all under a consistent interface.

You can also add runtime info about a type using static constructors:
===
module core.config;

string[] types;

template TypeCheck(T) {
    enum TypeCheck = true;

    shared static this() {
        types ~= T.mangleof;
    }
}
===
module test500.d
struct Cool {}

void main() {
        import core.config, std.stdio;
        writeln(types);
}
===
dmd test500.d rtinfo.d

$ ./test500
["S7test5004Cool", "S3std5array17__T8AppenderTAyaZ8Appender4Data", "S3std5array17__T8AppenderTAyaZ8Appender"]


Run time info available about all the types used in that program!


Soooo yeah, this is pretty much a pure win all around to my eyes. Am I blind to the suck?

Reply via email to