January 16, 2014
On Thursday, 16 January 2014 at 18:15:39 UTC, Vladimir Panteleev wrote:
> 1. I guess TypeCheck is a temporary name? I would suggest e.g. CustomRTInfo.

My first thought when writing this was the TypeCheck would just contain a bunch of static asserts to ensure the type matches whatever your project's special requirements are; it would act kinda like a plugin to the compiler's semantic check. (Actually, it does the bool because my first try was to use it as a template constraint, but if that fails, ti just stops the RTInfo from instantiating; it doesn't actually fail the compile like static assert inside does.)

But CustomRTInfo is a good name too, especially if we are adding data.

> 2. How about using a template mixin instead of a template?

If we actually mix it in that could change the binary layout of the thing though.

We don't need the dummy bool btw, I just tried and it works the same without it.

> It all fits together quite nicely. You can access stuff in your main program by importing its modules within the template mixin.

yes
January 16, 2014
On Thursday, 16 January 2014 at 18:44:54 UTC, Namespace wrote:
> I guess that could work pretty well for a C++ friend emulation with UDA's! Were not you the one who wanted to show me an example for that with using RTInfo?

I've played with rtinfo a lot before, but I don't remember doing C++ friend emulation...

> Offer your way the possibility to check automatically for null references? With an UDA e.g. @safe_ref or @not_null

I think those would be better done with the type info, like NotNull!t.

What's cool about the RTInfo thing though is it is checked on everything, so it can check for the *absence* of a UDA as well as for the presence.

You might say something must not have any nullable references unless specially marked. The rtinfo checker can confirm that: loop over all members, if it is potentially nullable and isn't marked with either NotNull or @i_know_this_is_nullable_and_that_is_ok, it can throw an error.

It is possible to do that kind of thing with static assert on the module level too.... sort of. See, the module level one for one can be forgotten and it can't access nested things in functions:

void foo() {
  struct InnerStruct {}
}

// can't see foo.InnerStuct here!
static assert(ModulePassesCheck!(mixin("this_module")));


But RTInfo *does* see InnerStruct, and you don't have to remember to add the check to your module too.
January 16, 2014
On Thursday, 16 January 2014 at 19:13:42 UTC, Adam D. Ruppe wrote:
> On Thursday, 16 January 2014 at 18:44:54 UTC, Namespace wrote:
>> I guess that could work pretty well for a C++ friend emulation with UDA's! Were not you the one who wanted to show me an example for that with using RTInfo?
>
> I've played with rtinfo a lot before, but I don't remember doing C++ friend emulation...
>
>> Offer your way the possibility to check automatically for null references? With an UDA e.g. @safe_ref or @not_null
>
> I think those would be better done with the type info, like NotNull!t.
>
> What's cool about the RTInfo thing though is it is checked on everything, so it can check for the *absence* of a UDA as well as for the presence.
>
> You might say something must not have any nullable references unless specially marked. The rtinfo checker can confirm that: loop over all members, if it is potentially nullable and isn't marked with either NotNull or @i_know_this_is_nullable_and_that_is_ok, it can throw an error.
>
> It is possible to do that kind of thing with static assert on the module level too.... sort of. See, the module level one for one can be forgotten and it can't access nested things in functions:
>
> void foo() {
>   struct InnerStruct {}
> }
>
> // can't see foo.InnerStuct here!
> static assert(ModulePassesCheck!(mixin("this_module")));
>
>
> But RTInfo *does* see InnerStruct, and you don't have to remember to add the check to your module too.

All right, it wasn't you, it was Andrej Mitrovic on my thread http://forum.dlang.org/thread/pdshwedjqgquoibxhrxa@forum.dlang.org#post-mailman.1977.1381065059.1719.digitalmars-d-learn:40puremagic.com

The problem on a library NotNull struct is: nobody will use it, because it requires NotNull on both sides, by the caller and by the callee:

----
class A { }

void safe_foo(NotNull!A na) { }

void main() {
    NotNull!A na = NotNull!A(new A());
    safe_foo(na);
}
----

Which is a lot more effor than:

----
class A { }

void safe_foo(@safe_ref A na) { }

void main() {
    A a = new A();
    safe_foo(a);
}
----

because it moves the check to the callee.
January 16, 2014
> All right, it wasn't you, it was Andrej Mitrovic on my thread http://forum.dlang.org/thread/pdshwedjqgquoibxhrxa@forum.dlang.org#post-mailman.1977.1381065059.1719.digitalmars-d-learn:40puremagic.com
>
> The problem on a library NotNull struct is: nobody will use it, because it requires NotNull on both sides, by the caller and by the callee:
>
> ----
> class A { }
>
> void safe_foo(NotNull!A na) { }
>
> void main() {
>     NotNull!A na = NotNull!A(new A());
>     safe_foo(na);
> }
> ----
>
> Which is a lot more effor than:
>
> ----
> class A { }
>
> void safe_foo(@safe_ref A na) { }
>
> void main() {
>     A a = new A();
>     safe_foo(a);
> }
> ----
>
> because it moves the check to the callee.

Also it is far more readable and easier to write as a contract like:
----
void safe _foo(A a) in {
    assert(a !is null);
} body { }
----
January 16, 2014
On 2014-01-16 16:57, Adam D. Ruppe wrote:

> 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.

Does this only work once? Say I have two libraries that use this technique, would that work?

This seems somewhat complicated, depending on the orders of modules passed to the compiler.

> 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.

No, not yet. As far as I know, the idea was to use RTInfo to collect and store type info for a precise GC.

> 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!

I have had a different idea for a solution to this problem for a while. That is, add a new manifest constant to object.d, called rtInfo. This will act like a UDA. Any template with the object.rtInfo UDA attached to itself will behave just as RTInfo does today.

module object;

enum rtInfo;

@rtInfo template RTInfo (T)
{
    enum RTInfo = null;
}

module foo.check_virtual_methods;

@rtInfo template CheckVirtualMethods (T)
{
    static if (!containsVirtualMethods!(T))
        static assert(false, "Error, contains virtual methods");

    enum CheckVirtualMethods = null;
}

An idea for storing data in TypeInfo is to create an associative array. The keys would be the fully qualified name of the template, in this case "foo.check_virtual_methods.CheckVirtualMethods". The values would be whatever the template evaluates to. There's one problem with this, the associative array would need to be built at runtime due to separate compilation.

-- 
/Jacob Carlborg
January 16, 2014
Am Thu, 16 Jan 2014 15:57:04 +0000
schrieb "Adam D. Ruppe" <destructionator@gmail.com>:

> 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?

This solve quite a few issues at once! It looks like
stuff that could only be done with compiler extensions or AST
manipulations and yet it works with a simple template.
Just for the neat "once in a lifetime" idea it should be
accepted. :)

-- 
Marco

January 16, 2014
On Thursday, 16 January 2014 at 15:57:05 UTC, Adam D. Ruppe wrote:
> Soooo yeah, this is pretty much a pure win all around to my eyes. Am I blind to the suck?

Wow, great trick! I'd love to see this merged.
January 16, 2014
This is a bit of a side discussion because the RTInfo can't *change* the type, it can only look at it. It is more like a centralized place to static asserts or maybe static data than an AST macro.

But...

On Thursday, 16 January 2014 at 19:32:10 UTC, Namespace wrote:
> The problem on a library NotNull struct is: nobody will use it, because it requires NotNull on both sides, by the caller and by the callee:

That's a lot of the benefit: the type system forces you to handle it sooner rather than later, so you can see where the invalid state came from.

>     NotNull!A na = NotNull!A(new A());

This could be easily automated though with a helper function:

NotNull!T create(T, Args...)(Args args) {
    return assumeNotNull(new T(args));
}

// use it! notice that it is notnull already
auto na = create!A();


And then you can just use it.

> Also it is far more readable and easier to write as a contract

That's what I tend to do now but the advantage of NotNull!() is catching it earlier.
January 17, 2014
On Thursday, 16 January 2014 at 23:56:14 UTC, Adam D. Ruppe wrote:
> // use it! notice that it is notnull already
> auto na = create!A();

That's neat. Didn't Andrei mention something about wanting new to be removed from the language and replaced with a library solution using allocators? If that were the case, it'd be pretty easy to enforce that class allocators return NotNull!T. It'd get rid of nullable references without needing a breaking language change.
January 17, 2014
On Thursday, 16 January 2014 at 20:13:29 UTC, Jacob Carlborg wrote:
> Does this only work once? Say I have two libraries that use this technique, would that work?

No, then you'd get complaints about the module being defined in two files. This would be a once-per-project thing.

Libraries however could provide helper functions with instructions for the user to call them in their project file. For example

module my.serialization;
mixin template SerializationAdditionToTypeInfo(T) {
    Whatever serialize() { ..... }
}

and then the user would open up their magic file

module core.config; // BTW this name is just because it was on my mind from another discussion with it for enabled features

template CustomRtInfo(T) {
    import my.serialization;
    mixin SerializationAdditionToTypeInfo!T;

    // you can also do other library's stuff here
}



hmmm Vladimir might be right that the custom info thing would be better done as a mixin template, then we could throw all this right in there and maybe build the whole class. I've gotta try it, I'm not sure yet.

> No, not yet. As far as I know, the idea was to use RTInfo to collect and store type info for a precise GC.

Yeah, RTInfo is one of the most potentially cool things that has sat untapped for a long time.


> I have had a different idea for a solution to this problem for a while. That is, add a new manifest constant to object.d, called rtInfo. This will act like a UDA. Any template with the object.rtInfo UDA attached to itself will behave just as RTInfo does today.

I think that would need compiler support though and might not work as well when compiling two files separately since then part of the rtinfo wouldn't get checked. True, here you'd have to include the config file each time you compile a file, but at least that bit is centralized so easier to manage.

> An idea for storing data in TypeInfo is to create an associative array. The keys would be the fully qualified name of the template, in this case "foo.check_virtual_methods.CheckVirtualMethods". The values would be whatever the template evaluates to. There's one problem with this, the associative array would need to be built at runtime due to separate compilation.

Yeah, I've played with this idea before too, it is definitely doable today.