www.digitalmars.com         C & C++   DMDScript  

digitalmars.dip.development - First Draft: Implicit Type Template Instantiation via Constructors

reply Meta <jared771 gmail.com> writes:
This DIP is a partial resurrection of DIP 40 
(https://wiki.dlang.org/DIP40) by me and Dennis Korpel. Dennis is 
working on the implementation 
(https://github.com/dlang/dmd/pull/16910) while I am writing the 
DIP.

The purpose of this DIP is to propose a new avenue for Implicit 
Template Instantiation (ITI) via constructors. Currently (that 
I'm aware of) D only does Implicit _Function_ Template 
Instantiation (IFTI); the proposal is to extend this implicit 
instantiation to types as well, via their constructors.

Before this DIP:
```d
struct Pair(T, U)
{
     T t;
     U u;

     this(T t, U u)
     {
         this.t = t;
         this.u = u;
     }
}

void main()
{
     auto p1 = Pair(1, "asdf"); // Error: struct `Pair` is not 
callable using argument types `!()(int, string)`
                                // Candidate is: `Pair(T, U)`
}
```

After this DIP:
```
struct Pair(T, U)
{
     T t;
     U u;

     this(T t, U u)
     {
         this.t = t;
         this.u = u;
     }
}

void main()
{
     auto p1 = Pair(1, "asdf"); // Okay, T is deduced as int and U 
as string
}

```

The DIP:
https://github.com/MetaLang/DIPs/blob/dip1050/DIPs/DIP1050.md
Mar 11 2025
next sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
On Wednesday, 12 March 2025 at 00:20:55 UTC, Meta wrote:
 This DIP is a partial resurrection of DIP 40 
 (https://wiki.dlang.org/DIP40) by me and Dennis Korpel. Dennis 
 is working on the implementation 
 (https://github.com/dlang/dmd/pull/16910) while I am writing 
 the DIP.

 [...]
Glad to see someone working on this problem. How would this apply to template aliases (e.g. DIP1023)?
Mar 11 2025
parent Meta <jared771 gmail.com> writes:
On Wednesday, 12 March 2025 at 02:37:06 UTC, jmh530 wrote:
 On Wednesday, 12 March 2025 at 00:20:55 UTC, Meta wrote:
 This DIP is a partial resurrection of DIP 40 
 (https://wiki.dlang.org/DIP40) by me and Dennis Korpel. Dennis 
 is working on the implementation 
 (https://github.com/dlang/dmd/pull/16910) while I am writing 
 the DIP.

 [...]
Glad to see someone working on this problem. How would this apply to template aliases (e.g. DIP1023)?
From my understanding, it should work the same way as IFTI, i.e., if eventually this code compiles: ```d struct TemplateType(T) { } alias TemplateAlias(T) = TemplateType!T; void templateFunction(T)(TemplateAlias!T arg) { } void main() { TemplateAlias!int ta; templateFunction(ta); } ``` Then this code should as well: ```d struct TemplateType(T) { } alias TemplateAlias(T) = TemplateType!T; struct AnotherTemplateType(T) { this(TemplateAlias!T arg) {} } void main() { TemplateAlias!int ta; auto att = AnotherTemplateType(ta); // Currently does not compile, even if you add AnotherTemplateType!(TemplateAlias!int)(ta) } ```
Mar 11 2025
prev sibling next sibling parent reply 12345swordy <alexanderheistermann gmail.com> writes:
On Wednesday, 12 March 2025 at 00:20:55 UTC, Meta wrote:
 This DIP is a partial resurrection of DIP 40 
 (https://wiki.dlang.org/DIP40) by me and Dennis Korpel. Dennis 
 is working on the implementation 
 (https://github.com/dlang/dmd/pull/16910) while I am writing 
 the DIP.

 The purpose of this DIP is to propose a new avenue for Implicit 
 Template Instantiation (ITI) via constructors. Currently (that 
 I'm aware of) D only does Implicit _Function_ Template 
 Instantiation (IFTI); the proposal is to extend this implicit 
 instantiation to types as well, via their constructors.

 Before this DIP:
 ```d
 struct Pair(T, U)
 {
     T t;
     U u;

     this(T t, U u)
     {
         this.t = t;
         this.u = u;
     }
 }

 void main()
 {
     auto p1 = Pair(1, "asdf"); // Error: struct `Pair` is not 
 callable using argument types `!()(int, string)`
                                // Candidate is: `Pair(T, U)`
 }
 ```

 After this DIP:
 ```
 struct Pair(T, U)
 {
     T t;
     U u;

     this(T t, U u)
     {
         this.t = t;
         this.u = u;
     }
 }

 void main()
 {
     auto p1 = Pair(1, "asdf"); // Okay, T is deduced as int and 
 U as string
 }

 ```

 The DIP:
 https://github.com/MetaLang/DIPs/blob/dip1050/DIPs/DIP1050.md
You spell "Changes" wrong, you better fixed that. It is a straight up massive miss opportunity to not handle Partial template parameter deduction here. C++ doesn't even handle that edge case which due to that you had to resort to an all or nothing in C++, which can be very annoying at times.
Mar 20 2025
parent Meta <jared771 gmail.com> writes:
On Thursday, 20 March 2025 at 18:01:05 UTC, 12345swordy wrote:
 You spell "Changes" wrong, you better fixed that.
Thank you, fixed.
 It is a straight up massive miss opportunity to not handle 
 Partial template parameter deduction here. C++ doesn't even 
 handle that edge case which due to that you had to resort to an 
 all or nothing in C++, which can be very annoying at times.
I know, and I agree, but that can be added by a later DIP, and W&A suggested that _this_ DIP be as simple as possible. We hope to expand on this feature further with subsequent proposals; this is just a first step.
May 12 2025
prev sibling parent reply Sebastiaan Koppe <mail skoppe.eu> writes:
On Wednesday, 12 March 2025 at 00:20:55 UTC, Meta wrote:
 This DIP is a partial resurrection of DIP 40 
 (https://wiki.dlang.org/DIP40) by me and Dennis Korpel. Dennis 
 is working on the implementation 
 (https://github.com/dlang/dmd/pull/16910) while I am writing 
 the DIP.

 The purpose of this DIP is to propose a new avenue for Implicit 
 Template Instantiation (ITI) via constructors. Currently (that 
 I'm aware of) D only does Implicit _Function_ Template 
 Instantiation (IFTI); the proposal is to extend this implicit 
 instantiation to types as well, via their constructors.

 [...]

 The DIP:
 https://github.com/MetaLang/DIPs/blob/dip1050/DIPs/DIP1050.md
Makes a lot of sense to me. What about implicit constructors though? ``` struct Pair(T,U) { T t; U u; } void main() { auto p = Pair(1,"string"); } ```
May 12 2025
parent reply Meta <jared771 gmail.com> writes:
On Tuesday, 13 May 2025 at 06:06:54 UTC, Sebastiaan Koppe wrote:
 On Wednesday, 12 March 2025 at 00:20:55 UTC, Meta wrote:
 This DIP is a partial resurrection of DIP 40 
 (https://wiki.dlang.org/DIP40) by me and Dennis Korpel. Dennis 
 is working on the implementation 
 (https://github.com/dlang/dmd/pull/16910) while I am writing 
 the DIP.

 The purpose of this DIP is to propose a new avenue for 
 Implicit Template Instantiation (ITI) via constructors. 
 Currently (that I'm aware of) D only does Implicit _Function_ 
 Template Instantiation (IFTI); the proposal is to extend this 
 implicit instantiation to types as well, via their 
 constructors.

 [...]

 The DIP:
 https://github.com/MetaLang/DIPs/blob/dip1050/DIPs/DIP1050.md
Makes a lot of sense to me. What about implicit constructors though? ``` struct Pair(T,U) { T t; U u; } void main() { auto p = Pair(1,"string"); } ```
That is not actually a constructor - it is struct literal syntax. I did not realize there was a difference either until recently.
May 13 2025
parent reply Sebastiaan Koppe <mail skoppe.eu> writes:
On Wednesday, 14 May 2025 at 06:46:09 UTC, Meta wrote:
 On Tuesday, 13 May 2025 at 06:06:54 UTC, Sebastiaan Koppe wrote:
 On Wednesday, 12 March 2025 at 00:20:55 UTC, Meta wrote:
 This DIP is a partial resurrection of DIP 40 
 (https://wiki.dlang.org/DIP40) by me and Dennis Korpel. 
 Dennis is working on the implementation 
 (https://github.com/dlang/dmd/pull/16910) while I am writing 
 the DIP.

 The purpose of this DIP is to propose a new avenue for 
 Implicit Template Instantiation (ITI) via constructors. 
 Currently (that I'm aware of) D only does Implicit _Function_ 
 Template Instantiation (IFTI); the proposal is to extend this 
 implicit instantiation to types as well, via their 
 constructors.

 [...]

 The DIP:
 https://github.com/MetaLang/DIPs/blob/dip1050/DIPs/DIP1050.md
Makes a lot of sense to me. What about implicit constructors though? ``` struct Pair(T,U) { T t; U u; } void main() { auto p = Pair(1,"string"); } ```
That is not actually a constructor - it is struct literal syntax. I did not realize there was a difference either until recently.
Fair enough, but could the dip support it, or perhaps in the future? The fact that we both expected it was a constructor - and you could argue it actually is, on the grounds that it does in fact construct an object - suggests more users would expect it to work.
May 14 2025
next sibling parent jmh530 <john.michael.hall gmail.com> writes:
On Wednesday, 14 May 2025 at 16:41:44 UTC, Sebastiaan Koppe wrote:
 [snip]
 That is not actually a constructor - it is struct literal 
 syntax. I did not realize there was a difference either until 
 recently.
Fair enough, but could the dip support it, or perhaps in the future? The fact that we both expected it was a constructor - and you could argue it actually is, on the grounds that it does in fact construct an object - suggests more users would expect it to work.
Struct literal syntax only works if there are no constructors. So at (current) step 4, you would want to check if there are no constructors. But then it might be a little more complicated to actually handle that case.
May 14 2025
prev sibling next sibling parent Meta <jared771 gmail.com> writes:
On Wednesday, 14 May 2025 at 16:41:44 UTC, Sebastiaan Koppe wrote:
 On Wednesday, 14 May 2025 at 06:46:09 UTC, Meta wrote:
 On Tuesday, 13 May 2025 at 06:06:54 UTC, Sebastiaan Koppe 
 wrote:
 On Wednesday, 12 March 2025 at 00:20:55 UTC, Meta wrote:
 This DIP is a partial resurrection of DIP 40 
 (https://wiki.dlang.org/DIP40) by me and Dennis Korpel. 
 Dennis is working on the implementation 
 (https://github.com/dlang/dmd/pull/16910) while I am writing 
 the DIP.

 The purpose of this DIP is to propose a new avenue for 
 Implicit Template Instantiation (ITI) via constructors. 
 Currently (that I'm aware of) D only does Implicit 
 _Function_ Template Instantiation (IFTI); the proposal is to 
 extend this implicit instantiation to types as well, via 
 their constructors.

 [...]

 The DIP:
 https://github.com/MetaLang/DIPs/blob/dip1050/DIPs/DIP1050.md
Makes a lot of sense to me. What about implicit constructors though? ``` struct Pair(T,U) { T t; U u; } void main() { auto p = Pair(1,"string"); } ```
That is not actually a constructor - it is struct literal syntax. I did not realize there was a difference either until recently.
Fair enough, but could the dip support it, or perhaps in the future? The fact that we both expected it was a constructor - and you could argue it actually is, on the grounds that it does in fact construct an object - suggests more users would expect it to work.
I was going to say that this is not possible without at least 1 defined constructor due to how the implementation works. However, look at step 5 of the algorithm in the DIP: ``` 5. If a matching constructor is found (in this case, this(T t, U u), create a new constructor this(T, U)(T t, U u) and try IFTI with that. ``` If it's creating a new constructor anyway to be instantiated, it could probably do this for structs even when there are no explicit constructors defined... I'll see what Dennis thinks want this and whether it's worth doing, because it *should* be very simple to add.
May 14 2025
prev sibling parent reply Dennis <dkorpel gmail.com> writes:
On Wednesday, 14 May 2025 at 16:41:44 UTC, Sebastiaan Koppe wrote:
 Fair enough, but could the dip support it, or perhaps in the 
 future? The fact that we both expected it was a constructor - 
 and you could argue it actually is, on the grounds that it does 
 in fact construct an object - suggests more users would expect 
 it to work.
It could be added, but there's something to watch out for: as the DIP specifies, the search for constructors in a template is shallow, so just because the search didn't find one, doesn't mean there is none. I'm imagining a scenario where a constructor is hidden behind a mixin / static if or whatever, so Implicit Template Instantiation would assume a generated 'default constructor', but after the instantiation it ends up calling the written constructor instead but with template parameters resolved incorrectly. Granted, that might also happen when a 'hidden' constructor exists with a stronger match than the constructor found for IFTI, so I'm not sure. Perhaps an extra check can be added that the constructor used for deriving template parameters is the one that ends up actually being called, but it's getting rather complex.
May 14 2025
parent Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Wednesday, 14 May 2025 at 22:30:27 UTC, Dennis wrote:
 On Wednesday, 14 May 2025 at 16:41:44 UTC, Sebastiaan Koppe 
 wrote:
 Fair enough, but could the dip support it, or perhaps in the 
 future? The fact that we both expected it was a constructor - 
 and you could argue it actually is, on the grounds that it 
 does in fact construct an object - suggests more users would 
 expect it to work.
It could be added, but there's something to watch out for: as the DIP specifies, the search for constructors in a template is shallow, so just because the search didn't find one, doesn't mean there is none. I'm imagining a scenario where a constructor is hidden behind a mixin / static if or whatever, so Implicit Template Instantiation would assume a generated 'default constructor', but after the instantiation it ends up calling the written constructor instead but with template parameters resolved incorrectly. Granted, that might also happen when a 'hidden' constructor exists with a stronger match than the constructor found for IFTI, so I'm not sure. Perhaps an extra check can be added that the constructor used for deriving template parameters is the one that ends up actually being called, but it's getting rather complex.
Simple things ought to work. D’s templates, contrary to C++’s templates, are extremely flexible. Without actually instantiating them, you can’t really know what’s going to be in there. To assess if an instantiated template won’t have a constructor explicitly defined, the language rules must do a conservative guess. It’ll never good enough for some, but pretending this isn’t possible won’t make people happy either. A lexical analysis can look for a constructor. This is quick and cheap. Contrary to semantic analysis, lexical analysis can see through `static if`, `version`, and `debug`. It can also see if there’s any mixin template or a string mixin. If s string mixin is present, things get really difficult. ITTI cannot assume the template instance (the type) has an implicitly defined constructor. Since we’re inferring the arguments from a constructor call, we need to know which constructor it will be, and it won’t be the implicitly defined one if it won’t exist. The same kind of lexical analysis can examine mixin templates and show that it can’t possibly add constructors. Then, instantiating the the mixin template is fine, even if it’s instantiated using some of the struct template’s type parameters. Here, semantic is needed to examine it. One option is to synthesize a new template with only the string mixin present, instantiating all type parameters with the internal `__error__` type and try running the string mixin. If it succeeds without error and doesn’t add a constructor, it won’t do so in the original struct template. That might be too expensive to run, though. If I’m not mistaken, `version` and `debug` cannot depend on template arguments, so it can be decided which branch they take. If that excludes a the only constructors defined, the template instance definitely won’t have any even if they’re lexically present. A deeper analysis might be able to show that the `static if` condition or the mixin stuff doesn’t depend on the struct template’s arguments. That is only relevant if one branch defines a constructor or has a string mixin and the other does neither. In that case, we can employ an approach similar to the one for string mixins.
Jul 16 2025