www.digitalmars.com         C & C++   DMDScript  

digitalmars.dip.development - First Draft: Implicit Conversion of Template Instantiations

reply Walter Bright <newshound2 digitalmars.com> writes:
https://github.com/WalterBright/documents/blob/9dba63a4c2887bdb2b988c354ebb0d6bb44c4968/templatecast.md

DConf: https://dconf.org/2024/online/index.html#walterb
Mar 16
next sibling parent Dukc <ajieskola gmail.com> writes:
On Saturday, 16 March 2024 at 15:50:27 UTC, Walter Bright wrote:
 https://github.com/WalterBright/documents/blob/9dba63a4c2887bdb2b988c354ebb0d6bb44c4968/templatecast.md
Thanks. Having a look:
 But `const X!(int*)` will instantiate `bar` as `void bar(int*)`.
Nope. It will instantiate it as `void bar(int*) const`.
 The template struct or template class will be treated as a list 
 of its members.
Three points. 1. Why wouldn't this apply only to templated structs? It'd make sense for the rules to apply to all structs/classes. 2. In the presented example, no fields are actually converted. The only field, `x`, is the same type in both cases (`const(int[])`). Please add an example where type of the field(s) differ, say converting `X!(immutable(int))` to `const(X!int)`. 3. Could allowing these casts break assumptions of a type that has ` system` fields (DIP1035)? I haven't thought it deeply enough to conclude one way pr another but the DIP should probably explain it.
 Only members that are fields or non-static functions are 
 considered.
I don't see why member functions would need to be considered, static or no. An instance of a struct doesn't contain it's member functions as fields.
Mar 16
prev sibling next sibling parent reply andy <andy-hanson protonmail.com> writes:
On Saturday, 16 March 2024 at 15:50:27 UTC, Walter Bright wrote:
 https://github.com/WalterBright/documents/blob/9dba63a4c2887bdb2b988c354ebb0d6bb44c4968/templatecast.md

 DConf: https://dconf.org/2024/online/index.html#walterb
There must be a typo here:
 Consider also that const(int)[] and const(int)[] are not the 
 same type, either
Mar 16
parent Walter Bright <newshound2 digitalmars.com> writes:
On 3/16/2024 5:06 PM, andy wrote:
 There must be a typo here:
 
 Consider also that const(int)[] and const(int)[] are not the same type, either
The same typo that was in my slides and fixed :-/
Mar 17
prev sibling next sibling parent max haughton <maxhaton gmail.com> writes:
On Saturday, 16 March 2024 at 15:50:27 UTC, Walter Bright wrote:
 https://github.com/WalterBright/documents/blob/9dba63a4c2887bdb2b988c354ebb0d6bb44c4968/templatecast.md

 DConf: https://dconf.org/2024/online/index.html#walterb
I once wrote a small patch to the compiler that added a syntax to enable user defined conversions for this exact use case, can't remember the syntax I came up with (it was a bit like an is expression suffixing the template parameters), must dig it up.
Mar 17
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 3/16/24 16:50, Walter Bright wrote:
 https://github.com/WalterBright/documents/blob/9dba63a4c2887bdb2b988c354ebb0d6bb44c4968/templatecast.md
 
 DConf: https://dconf.org/2024/online/index.html#walterb
In general: Yes, I think something along those lines would be great. From my DConf question: ```d struct S(alias a){ void f(){ a(); } } void main(){ import std.stdio; S!({ writeln("first"); }) s1; S!({ writeln("second"); }) s2 = s1; // ok s1.f(); // first s2.f(); // second pragma(msg, is(typeof(s1):typeof(s2))); // true } ``` I think this is a bit dangerous. (And on stream you seemed to argue that this has the potential to kill the proposal.) I think the way to fix it would be to simply use a more strict standard than "must belong to the same template". Add an opt-in annotation to template parameters that indicates "this parameter may change in a const conversion". For other parameters, you still require them to match by default. This way, people can opt into the behavior.
Mar 17
parent Walter Bright <newshound2 digitalmars.com> writes:
On 3/17/2024 5:32 PM, Timon Gehr wrote:
 I think this is a bit dangerous. (And on stream you seemed to argue that this 
 has the potential to kill the proposal.)
I'm not convinced it is clean, nor am I convinced that it is dangerous. One solution would be simply to reject alias arguments that are functions. In general, the way may be to just keep rejecting things at compile time that aren't correct.
 I think the way to fix it would be to simply use a more strict standard than 
 "must belong to the same template". Add an opt-in annotation to template 
 parameters that indicates "this parameter may change in a const conversion".
For 
 other parameters, you still require them to match by default.
 
 This way, people can opt into the behavior.
I hear you, but I am concerned about yet another annotation.
Mar 18
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 3/16/24 16:50, Walter Bright wrote:
 https://github.com/WalterBright/documents/blob/9dba63a4c2887bdb2b988c354ebb0d6bb44c4968/templatecast.md
 
 DConf: https://dconf.org/2024/online/index.html#walterb
Keep in mind that to get rid of qualifier magic, you also have to add common types: ```d struct S(T){ T[] payload; } void main(){ immutable a1 = [1,2,3]; auto a2 = [1,2,3]; assert(a1 == a2); auto s1 = S!(immutable(int))(a1); auto s2 = S!(int)(a2); assert(s1 == s2); // TODO auto a3 = [a1, a2]; auto s3 = [s1, s2]; // TODO } ``` This is a bit more tricky.
Mar 17
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 3/17/2024 5:38 PM, Timon Gehr wrote:
 This is a bit more tricky.
These already are supposed to work.
Mar 18
parent Walter Bright <newshound2 digitalmars.com> writes:
Oops.

This will not work, only the top level qualifiers are convertible, not the next 
level down.
Mar 18
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 3/16/24 16:50, Walter Bright wrote:
 https://github.com/WalterBright/documents/blob/9dba63a4c2887bdb2b988c354ebb0d6bb44c4968/templatecast.md
 
 DConf: https://dconf.org/2024/online/index.html#walterb
Issue: It does not seem to work yet for class references: ```d struct S(T){ T a; } class A{} class B:A{} void main(){ S!(const(int)[]) s1 = S!(int[])([1,2,3]); // ok S!A s2=S!B(new A); // error } ``` The B is an A in essentially the same way an `int[]` is a `const(int)[]`. (Subtyping, no change in memory representation required for conversion.)
Mar 17
parent Walter Bright <newshound2 digitalmars.com> writes:
On 3/17/2024 5:45 PM, Timon Gehr wrote:
 Issue: It does not seem to work yet for class references:
It only works for const conversion. Not for inheritance conversions. We could perhaps add the latter later, but I want to stick with const conversion only for now.
Mar 18
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 3/16/24 16:50, Walter Bright wrote:
 https://github.com/WalterBright/documents/blob/9dba63a4c2887bdb2b988c354ebb0d6bb44c4968/templatecast.md
 
 DConf: https://dconf.org/2024/online/index.html#walterb
Idea: Explicit variance annotations. Benefits: - Makes it easy to determine a common type. - I expect overall faster compilation, because subtyping can be refuted without analyzing all fields. - Solves question of how to annotate in an opt-in solution. (I think opt-in is absolutely required for this feature.) - Very easy to implement by simple additions on top of the existing prototype. - Principled. Drawbacks: - Slightly more expensive to determine that subtyping holds. (First, you check the arguments according to the variance annotations, if that succeeds, you still have to perform the member-wise check.) - This particular proposal would disallow a template type with variance of one template parameter varying based on one of the other template parameters. E.g. ```d struct S(+T){ T payload; } ``` This annotation means that `S` is _covariant_ in `T`. Intuitively, this means that an rvalue of type `S!T` can produce values of type `T`. Other examples: ```d struct S(+T){ T foo(); } ``` ```d struct S(+T){ void foo(void delegate(T) callback){} } ``` The common type of `S!T1` and `S!T2` is `S!T3`, where `T3` is the common type of `T1` and `T2`. ``d struct S(-T){ void delegate(T) payload; } ``` This annotation means that `S` is _contravariant_ in `T`. Intuitively, this means that an rvalue of type `S!T` can consume values of type `T`. The common type of `S!T1` and `S!T2` is `S!T3`, where `T3` is the shared type of `T1` and `T2`. The implementation currently also supports a case like this one: ```d struct S(T){ T[] payload; } ``` Here, `S` is covariant in `T` only if converting to a non-mutable type. This would require a different annotation, I suggest `+const`. ```d struct S(+const T){ T[] payload; } ``` In principle we could also have `-const`, but currently I do not see a case where that would be helpful.
Mar 17
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 3/18/24 02:11, Timon Gehr wrote:
 
 
 Drawbacks:
 
 - Slightly more expensive to determine that subtyping holds. (First, you 
 check the arguments according to the variance annotations, if that 
 succeeds, you still have to perform the member-wise check.)
 
 - This particular proposal would disallow a template type with variance 
 of one template parameter varying based on one of the other template 
 parameters.
A way to address this would be to also allow `struct S(+-T)`, which always just uses the field-wise check.
Mar 17
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 3/16/24 16:50, Walter Bright wrote:
 https://github.com/WalterBright/documents/blob/9dba63a4c2887bdb2b988c354ebb0d6bb44c4968/templatecast.md
 
 DConf: https://dconf.org/2024/online/index.html#walterb
Issue: I think the field names also have to match. Consider: ```d struct S(bool swapped,T1,T2){ static if(swapped){ T2 second; T1 first; }else{ T1 first; T2 second; } } void main(){ import std.stdio; auto s1=S!(true,int,int)(1,2); S!(false,int,int) s2=s1; writeln(s1.first," ",s1.second); // 1 2 writeln(s2.first," ",s2.second); // 2 1 } ```
Mar 17
parent Walter Bright <newshound2 digitalmars.com> writes:
On 3/17/2024 6:17 PM, Timon Gehr wrote:
 Issue: I think the field names also have to match.
I think you're right.
Mar 18
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 3/16/24 16:50, Walter Bright wrote:
 https://github.com/WalterBright/documents/blob/9dba63a4c2887bdb2b988c354ebb0d6bb44c4968/templatecast.md
 
 DConf: https://dconf.org/2024/online/index.html#walterb
Issue: Subtyping under indirections remains magic. ```d struct Mutability(bool mutable,T){ static if(mutable){ T payload; }else{ const(T) payload; } } void main(){ int[] a1 = [1,2,3]; const(int)[] a2 = a1; // ok auto m1=[Mutability!(true,int)(1), Mutability!(true,int)(2), Mutability!(true,int)(3)]; Mutability!(false,int)[] m2=m1; // error } ``` (With variance annotations, this example would need some kind of support for variance annotations for template value parameters.)
Mar 17
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 3/18/24 02:28, Timon Gehr wrote:
 On 3/16/24 16:50, Walter Bright wrote:
 https://github.com/WalterBright/documents/blob/9dba63a4c2887bdb2b988c354ebb0d6bb44c4968/templatecast.md

 DConf: https://dconf.org/2024/online/index.html#walterb
Issue: Subtyping under indirections remains magic. ...
Another example of a similar kind of issue: ```d struct S(T){ T x; } void main(){ S!int[] a = [S!int(1),S!int(2),S!int(3)]; S!(const(int))[] b = a; // error } ``` I think this one may be just an oversight in the prototype implementation.
Mar 17
parent Walter Bright <newshound2 digitalmars.com> writes:
On 3/17/2024 6:32 PM, Timon Gehr wrote:
 Another example of a similar kind of issue:
 
 ```d
 struct S(T){
      T x;
 }
 void main(){
      S!int[] a = [S!int(1),S!int(2),S!int(3)];
      S!(const(int))[] b = a; // error
 }
 ```
 
 I think this one may be just an oversight in the prototype implementation.
That should work.
Mar 18