Jump to page: 1 2
Thread overview
First Draft: Implicit Conversion of Template Instantiations
Mar 16
Dukc
Mar 17
andy
March 16
https://github.com/WalterBright/documents/blob/9dba63a4c2887bdb2b988c354ebb0d6bb44c4968/templatecast.md

DConf: https://dconf.org/2024/online/index.html#walterb
March 16

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.

March 17
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
March 17
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 :-/
March 17
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.
March 18
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.

March 18
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.
March 18
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.)
March 18
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.


March 18
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
}
```
« First   ‹ Prev
1 2