Thread overview
[Issue 13196] Infer struct/class template args from constructor args
Jul 24, 2014
Jakob Ovrum
Jul 24, 2014
Manu
Jul 24, 2014
Jonathan M Davis
Jul 25, 2014
Manu
Jul 25, 2014
Jonathan M Davis
Jul 25, 2014
Manu
July 24, 2014
https://issues.dlang.org/show_bug.cgi?id=13196

Jakob Ovrum <jakobovrum@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEW                         |RESOLVED
                 CC|                            |jakobovrum@gmail.com
         Resolution|---                         |DUPLICATE

--- Comment #1 from Jakob Ovrum <jakobovrum@gmail.com> ---
This has been discussed before, and there is even a DIP for it. See #6082.

It's not so easy to do in the general case:

---
struct S(T)
{
    static if(is(T == bool))
    {
        this(T[] t) {}
    }
    else
    {
        this(int i) {}
    }
}

auto s = S([true, false]);
auto s2 = S(2);
---

How do you propose we deduce T for the above two?

*** This issue has been marked as a duplicate of issue 6082 ***

--
July 24, 2014
https://issues.dlang.org/show_bug.cgi?id=13196

--- Comment #2 from Manu <turkeyman@gmail.com> ---
(In reply to Jakob Ovrum from comment #1)
> This has been discussed before, and there is even a DIP for it. See #6082.
> 
> It's not so easy to do in the general case:
> 
> ---
> struct S(T)
> {
>     static if(is(T == bool))
>     {
>         this(T[] t) {}
>     }
>     else
>     {
>         this(int i) {}
>     }
> }
> 
> auto s = S([true, false]);
> auto s2 = S(2);
> ---
> 
> How do you propose we deduce T for the above two?
> 
> *** This issue has been marked as a duplicate of issue 6082 ***

I don't think your example represents the 'general case'.
In this example I expect there must be a compile error; the static if can't be
resolved without T specified explicitly, so no constructor can be known to
exist, so it can't possibly even attempt to be inferred.

--
July 24, 2014
https://issues.dlang.org/show_bug.cgi?id=13196

Steven Schveighoffer <schveiguy@yahoo.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |schveiguy@yahoo.com

--- Comment #3 from Steven Schveighoffer <schveiguy@yahoo.com> ---
(In reply to Jakob Ovrum from comment #1)
> This has been discussed before, and there is even a DIP for it. See #6082.
> 
> It's not so easy to do in the general case:
> 
> ---
> struct S(T)
> {
>     static if(is(T == bool))
>     {
>         this(T[] t) {}
>     }
>     else
>     {
>         this(int i) {}
>     }
> }
> 
> auto s = S([true, false]);
> auto s2 = S(2);
> ---
> 
> How do you propose we deduce T for the above two?

I'll respond here since the question is here.

Answer: you don't. Just like you can't do IFTI on this:

template foo(T)
{
   static if(is(T == bool))
      void foo(T t) {}
   else
      void foo(int t) {}
}

--
July 24, 2014
https://issues.dlang.org/show_bug.cgi?id=13196

Jonathan M Davis <jmdavisProg@gmx.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |jmdavisProg@gmx.com

--- Comment #4 from Jonathan M Davis <jmdavisProg@gmx.com> ---
(In reply to Manu from comment #2)
> I don't think your example represents the 'general case'.

I think that in most cases, when the term "general case" is used, it means more along the lines of every case or the "generic" case, not necessarily the common case.

Regardless, we can potentially make it so that the compiler is able to infer the constructor's template arguments for templated types in some basic cases, but I think that it's clear that we can only do it for a certain subset. Ultimately, you need a wrapper function to do the IFTI for you (like redBlackTree) if you want to get full IFTI with templated types with templated constructors.

--
July 25, 2014
https://issues.dlang.org/show_bug.cgi?id=13196

--- Comment #5 from Manu <turkeyman@gmail.com> ---
(In reply to Jonathan M Davis from comment #4)
> Regardless, we can potentially make it so that the compiler is able to infer the constructor's template arguments for templated types in some basic cases, but I think that it's clear that we can only do it for a certain subset.

It's not clear at all. Present a case where it can't work? I haven't seen any yet.

> Ultimately, you need a wrapper function to do the IFTI for you (like redBlackTree) if you want to get full IFTI with templated types with templated constructors.

Why?
I don't think I understand what you mean. I don't see any reason a wrapper
function is a requirement. Perhaps you mean something by 'full IFTI' which
isn't clear to me?

--
July 25, 2014
https://issues.dlang.org/show_bug.cgi?id=13196

--- Comment #6 from Jonathan M Davis <jmdavisProg@gmx.com> ---
(In reply to Manu from comment #5)
> (In reply to Jonathan M Davis from comment #4)
> > Regardless, we can potentially make it so that the compiler is able to infer the constructor's template arguments for templated types in some basic cases, but I think that it's clear that we can only do it for a certain subset.
> 
> It's not clear at all. Present a case where it can't work? I haven't seen any yet.

An obvious case is something like

struct S(T)
{
    this(U)(U u)
        if(is(U : T))
    {...}

    T _t;
}

Also, the previous example of

struct S(T)
{
    static if(is(T == bool))
    {
        this(T[] t) {}
    }
    else
    {
        this(int i) {}
    }
}

wouldn't work as was previously discussed. The basic case of

struct S(T)
{
    this(T t) {}
}

should be possible, but I'd expect that much of anything fancier wouldn't. Regardless, the fact that

struct S(T)
{
    this(T t) {}
}

is equivalent to

template S(T)
{
    struct S
    {
        this(T t) {}
    }
}

is a major hurdle from an implementation point of view, because it would mean having to know what was going to be in the template before it's even instantiated. Also, you could have something like

template S(T)
{
    struct S
    {
        this(int i) {}
    }
}

so the compiler can't assume that the type that's being passed to the constructor has anything to do with the template argument.

When you do S!int(5), the compiler knows the template argument, can instantiate the template, and _then_ look at whether trying to call the result of the template instantiation with an argument of 5 makes any sense. What you're asking for basically requires that the compiler figure out the guts of the template before it's even instantiated. I expect that it's possible to do that, but it would be a major shift in how templates are handled in the compiler, so it wouldn't surprise me at all if it never happened, as nice as it would be.

--
July 25, 2014
https://issues.dlang.org/show_bug.cgi?id=13196

--- Comment #7 from Manu <turkeyman@gmail.com> ---
(In reply to Jonathan M Davis from comment #6)
> (In reply to Manu from comment #5)
> > (In reply to Jonathan M Davis from comment #4)
> > > Regardless, we can potentially make it so that the compiler is able to infer the constructor's template arguments for templated types in some basic cases, but I think that it's clear that we can only do it for a certain subset.
> > 
> > It's not clear at all. Present a case where it can't work? I haven't seen any yet.
> 
> An obvious case is something like
> 
> struct S(T)
> {
>     this(U)(U u)
>         if(is(U : T))
>     {...}
> 
>     T _t;
> }
> 
> Also, the previous example of
> 
> struct S(T)
> {
>     static if(is(T == bool))
>     {
>         this(T[] t) {}
>     }
>     else
>     {
>         this(int i) {}
>     }
> }

I don't understand why you seem to suggest that either of those cases would be expected to work? They both make no logical sense.

The first can't possibly deduce T, because U:T is not a 1:1 relationship.

The second is backwards. T must be known to resolve the static if prior to the constructor existing. So it can't possibly use the constructor to deduce T, because at the time T must be evaluated, it doesn't exist yet. =

Why would you suggest it's reasonable for people to assume that these should
work?
They're not examples of failing edge cases, they're just nonsense.
Neither case works for functions right now, and so it shouldn't.


> wouldn't work as was previously discussed. The basic case of
> 
> struct S(T)
> {
>     this(T t) {}
> }
> 
> should be possible, but I'd expect that much of anything fancier wouldn't. Regardless, the fact that
> 
> struct S(T)
> {
>     this(T t) {}
> }
> 
> is equivalent to
> 
> template S(T)
> {
>     struct S
>     {
>         this(T t) {}
>     }
> }
> 
> is a major hurdle from an implementation point of view, because it would mean having to know what was going to be in the template before it's even instantiated. Also, you could have something like
> 
> template S(T)
> {
>     struct S
>     {
>         this(int i) {}
>     }
> }
> 
> so the compiler can't assume that the type that's being passed to the constructor has anything to do with the template argument.
> 
> When you do S!int(5), the compiler knows the template argument, can instantiate the template, and _then_ look at whether trying to call the result of the template instantiation with an argument of 5 makes any sense. What you're asking for basically requires that the compiler figure out the guts of the template before it's even instantiated.

I don't know how the parser works, but I can't imagine that this is challenging for the parser when considering all the other magic it is capable of. I'd suspect the parser knows that there is a struct, and that it has a constructor prior to requiring complete resolution of every detail relating to the struct.

I find it hard to imagine how it's different from the function case.
Is it basically just that the function argument list isn't inside a scope
block,, whereas the constructor is... and you suspect that's a show stopper?

How would forward referencing ever work if the parser isn't capable of functionality like this?


> I expect that it's
> possible to do that, but it would be a major shift in how templates are
> handled in the compiler, so it wouldn't surprise me at all if it never
> happened, as nice as it would be.

I have no evidence to believe this one way or another. It seems simple enough though, the logic is obviously implemented for functions, and it should be basically identical in functionality... I don't see what makes it so difficult by comparison.

This seems like very easy stuff considering the kind of magic the D parser is capable of.

--
July 28, 2014
https://issues.dlang.org/show_bug.cgi?id=13196

--- Comment #8 from Steven Schveighoffer <schveiguy@yahoo.com> ---
The *general* rule should be, if I can make an IFTI function that simply forwards to the ctor of the struct, then the compiler should simply allow it to be called via the ctor as if the IFTI function existed.

Of course, there will be exceptions to the rule. Of course, it's not going to be as flexible as IFTI, as you can decouple to some degree the return value of an IFTI function, but you cannot do that with a templated struct/class.

But the things that would work should cover a lot of ground. If we can find ways to tweak it upward, we can do that. But I think it's worth adding the feature, even for the simple cases.

--