October 02, 2020
On Thursday, 1 October 2020 at 18:26:48 UTC, H. S. Teoh wrote:
> On Thu, Oct 01, 2020 at 02:08:11PM -0400, Steven Schveighoffer via Digitalmars-d wrote:
>> On 10/1/20 1:35 PM, Andrei Alexandrescu wrote:
> [...]
>> > How do you implement Variant.get(T) without reifying is(T : U)?
>> 
>> How do you implement Variant.get(T) *with* reifying is(U : T)?
>> 
>> step 1: determine using reified constructs that U is convertible to T
>> step 2: ?????
>> step 3: Return a T from a U!
>> 
>> BTW, Variant already does a *limited* form of this. reification doesn't change what needs to happen.
>> 
>> I don't see why Variant's needs have to dictate how you need to reason about types at compile time in CTFE.
> [...]
>
> I think what Andrei is getting at is, we want to be able to transfer the compiler's knowledge and implementation of implicit type conversions to runtime.  I.e., we'd like to do this:
>

The compiler's knowledge of implicit conversions is a table that hasn't changed since 2010.  Why can't you just have a form of this table in Phobos?

October 02, 2020
On Thursday, 1 October 2020 at 08:21:24 UTC, Stefan Koch wrote:
> Hi People,
>
> To further show the intuitive type function syntax I just created a way to print a conversion matrix [...SNIP...]

Awesome. I like.
October 02, 2020
On Friday, 2 October 2020 at 09:57:18 UTC, Iain Buclaw wrote:
> On Thursday, 1 October 2020 at 18:26:48 UTC, H. S. Teoh wrote:
>> On Thu, Oct 01, 2020 at 02:08:11PM -0400, Steven Schveighoffer via Digitalmars-d wrote:
>>> [...]
>> [...]
>>> [...]
>> [...]
>>
>> I think what Andrei is getting at is, we want to be able to transfer the compiler's knowledge and implementation of implicit type conversions to runtime.  I.e., we'd like to do this:
>>
>
> The compiler's knowledge of implicit conversions is a table that hasn't changed since 2010.  Why can't you just have a form of this table in Phobos?

That's true for basic types.
If you want to know it as well for alias this.
it's somewhat more tricky :)
October 02, 2020
On Friday, 2 October 2020 at 09:51:10 UTC, Walter Bright wrote:
> On 10/1/2020 7:27 PM, Andrei Alexandrescu wrote:
>> auto makeConvMatrix(Ts...)() {
>>      bool[T.length[T.length] result;
>>      static foreach (i, T : Ts)
>>          static foreach (j, U : Ts)
>>              result[i][j] = is(T : U);
>>      return result;
>> }
>
> This piece of code may be useful in re-implementing std.meta.MostDerived.

Doesn't seem like it.
The code results in a parser error.

October 02, 2020
On 10/2/20 6:51 AM, Stefan Koch wrote:
> On Friday, 2 October 2020 at 09:51:10 UTC, Walter Bright wrote:
>> On 10/1/2020 7:27 PM, Andrei Alexandrescu wrote:
>>> auto makeConvMatrix(Ts...)() {
>>>      bool[T.length[T.length] result;
>>>      static foreach (i, T : Ts)
>>>          static foreach (j, U : Ts)
>>>              result[i][j] = is(T : U);
>>>      return result;
>>> }
>>
>> This piece of code may be useful in re-implementing std.meta.MostDerived.
> 
> Doesn't seem like it.
> The code results in a parser error.

So many errors. Deficiency of copypasta strikes again.

auto makeConvMatrix(Ts...)() {
     bool[Ts.length][Ts.length] result;
     static foreach (i, T ; Ts)
         static foreach (j, U ; Ts)
             result[i][j] = is(T : U);
     return result;
}

That won't really help much with MostDerived and friends - as Iain said, for known types the conversions are well known.
October 02, 2020
On Friday, 2 October 2020 at 04:53:12 UTC, Stefan Koch wrote:
> You still create unnecessary symbols.

Not true.

extern(C)
void main()
{
    import core.stdc.stdio;
    static immutable convMatrix = "pretend matrix";
    static immutable convMatrix2 = "pretend matrix 2";

    printf("%s\n", convMatrix.ptr);
    printf("%s\n", convMatrix2.ptr);
}


No template in sight.

$ dmd -betterC bc
$ nm bc.o
0000000000000000 t
0000000000000000 D _D2bc4mainUZ10convMatrixyAa
0000000000000010 D _D2bc4mainUZ11convMatrix2yAa
                 U _GLOBAL_OFFSET_TABLE_
0000000000000000 r _TMP0
000000000000000f r _TMP1
0000000000000000 W main
                 U printf


Or without -betterC

$ dmd bc.d
$ nm bc.o
0000000000000000 t
0000000000000008 R _D2bc12__ModuleInfoZ
0000000000000000 D _D2bc4mainUZ10convMatrixyAa
0000000000000010 D _D2bc4mainUZ11convMatrix2yAa
                 U _GLOBAL_OFFSET_TABLE_
0000000000000000 r _TMP0
000000000000000f r _TMP1
                 U __start_minfo
                 U __stop_minfo
                 U _d_dso_registry
0000000000000000 W main
                 U printf

> Just use the template twice with different arguments.
>
> 0000000000000000 t
> 0000000000000008 R _D2t412__ModuleInfoZ
> 0000000000000000 D _D2t44mainUZ10convMatrixyAa
> 0000000000000010 D _D2t44mainUZ11convMatrix2yAa
>                  U _d_dso_registry
>                  U _GLOBAL_OFFSET_TABLE_
> 0000000000000000 W main
>                  U printf
>                  U __start_minfo
>                  U __stop_minfo
> 0000000000000000 r _TMP0
> 0000000000000142 r _TMP1



Absolutely identical. An eponymous template resolving to an enum of a common type is indistinguishable to a literal of that type. It does *not* generate anything extra for nested helper functions in there.
October 02, 2020
On Friday, 2 October 2020 at 12:56:35 UTC, Adam D. Ruppe wrote:
> On Friday, 2 October 2020 at 04:53:12 UTC, Stefan Koch wrote:
>> You still create unnecessary symbols.
>
> Not true.
>
> Absolutely identical. An eponymous template resolving to an enum of a common type is indistinguishable to a literal of that type. It does *not* generate anything extra for nested helper functions in there.

Replace the static immutable with enum and check again.

October 02, 2020
On Friday, 2 October 2020 at 14:34:54 UTC, Stefan Koch wrote:
> Replace the static immutable with enum and check again.

It is identical again. As expected.

# with a string literal
$ dmd -betterC bc
$ nm bc.o
0000000000000000 t
                 U _GLOBAL_OFFSET_TABLE_
0000000000000000 W main
                 U printf

# with the template

$ dmd -betterC bc
$ nm bc.o
0000000000000000 t
                 U _GLOBAL_OFFSET_TABLE_
0000000000000000 W main
                 U printf


October 02, 2020
On Friday, 2 October 2020 at 14:42:15 UTC, Adam D. Ruppe wrote:
> On Friday, 2 October 2020 at 14:34:54 UTC, Stefan Koch wrote:
>> Replace the static immutable with enum and check again.
>
> It is identical again. As expected.
>
> # with a string literal
> $ dmd -betterC bc
> $ nm bc.o
> 0000000000000000 t
>                  U _GLOBAL_OFFSET_TABLE_
> 0000000000000000 W main
>                  U printf
>
> # with the template
>
> $ dmd -betterC bc
> $ nm bc.o
> 0000000000000000 t
>                  U _GLOBAL_OFFSET_TABLE_
> 0000000000000000 W main
>                  U printf

indeed.
now measure memory consumption during compilation :)
October 02, 2020
On Fri, Oct 02, 2020 at 03:36:32AM +0000, Adam D. Ruppe via Digitalmars-d wrote: [...]
> The basic idea though is it doesn't take magic to do this. All the types the Variant needs *are* available to it thanks to normal templates, every type it ever sees are sent in (and ones it never sees it never needs), just the difficulty is they come in two separate calls. Thus it extracts what it needs from them into a runtime class to bridge that gap.
[...]

This is awesome!  I thought about it last night, and realized that this is the same recipe that I discovered for automatic extraction of i18n-able strings.  The key ingredient is static ctors, usually wrapped in a wrapper struct, that register the needed data with a central collector at program startup (or even when dynamically-loaded libraries are loaded):

	auto myTemplateFunc(T)(T t) {
		struct RuntimeStartupHook {
			static this() {
				registerType!T( /* some info here */);
			}
		}

		return ... /* function implementation here */;
	}

	static RuntimeInfo db;
	void registerType(T)(...) {
		db.add( ... /* information about T */);
	}

Each time somebody calls myTemplateFunc, information about the type T is injected into a static ctor that will get called upon program startup, which registers information about T into the runtime database `db`. This can be anything from the type name, to the typeid, or whatever traits introspected from T in RuntimeStartupHook.  By the time main() runs, we have collected information about *all* types that have been used to instantiate myTemplateFunc.  This works with separate compilation.

Better yet, this will even work for dynamically-loaded libraries (provided their static ctors are automatically run upon startup), because the static ctors will register any new T's that were used in the library code with the central database. So after loading the library, `db` will contain this new information, and the implementation of myTemplateFunc can then make use of it.

In this way, we can collect *all* types used to instantiate a particular template function, and be able to make decisions over that at runtime. Such as Variant being able to loop over all types it was instantiated with.  For example, if we wanted to support, say, invoking some method .abc on T if it exists, we can do it by having RuntimeStartupHook introspect T at compile-time to bind any method named .abc and wrap that in a function pointer or delegate.  Then at runtime, we simply consult the database to determine whether T has such a method, and if so, invoke it.  A lot of compile-time information can be transferred to runtime this way. This is powerful stuff!

About the only thing we can't do is things like implicit conversions between two instantiations of T, because is(T : U) requires knowledge of *both* T and U simultaneously at compile-time, but in the scheme above, T and U are individually registered at runtime, and we cannot know both simultaneously until after the runtime hooks have run. (E.g., if T was used in main but U was used in a dynamically-loaded library, then there is no way to leverage is(T : U) at compile-time.)  So unfortunately, we still have to re-implement is(T : U) manually in library code.


T

-- 
People walk. Computers run.