Thread overview
Testing implicit conversion to template instance with is() expression
Mar 15, 2015
Marc Schütz
Mar 15, 2015
Ali Çehreli
Mar 15, 2015
Marc Schütz
Mar 15, 2015
Marc Schütz
Mar 15, 2015
Marc Schütz
Mar 15, 2015
Marc Schütz
Mar 15, 2015
Nicolas Sicard
Mar 15, 2015
Marc Schütz
March 15, 2015
Should this work?

    struct V(string s) {
    }

    struct S(int U) {
        V!"xyz" x;
        alias x this;
    }

    void main() {
        S!10 a;
        static assert(is(a : V!Args, Args...));
    }

With DMD Git master, the static assert() fails. Should it? Am I doing something wrong? How can I test whether something is implicitly convertible to any instance of a particular template?
March 15, 2015
On 03/15/2015 08:47 AM, "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm@gmx.net>" wrote:

> Should this work?
>
>      struct V(string s) {
>      }
>
>      struct S(int U) {
>          V!"xyz" x;
>          alias x this;
>      }
>
>      void main() {
>          S!10 a;
>          static assert(is(a : V!Args, Args...));
>      }
>
> With DMD Git master, the static assert() fails. Should it? Am I doing
> something wrong? How can I test whether something is implicitly
> convertible to any instance of a particular template?

There is no way other than checking for compile-time duck typing (see the implementations of isInputRange and others).

One reason is that the compiler does not have the concept of "an instance of a template". Templates are for code generation and only the end-result (i.e. S!10) lives as a concept when compiling.

Ali

March 15, 2015
On Sunday, 15 March 2015 at 16:44:14 UTC, Ali Çehreli wrote:
> On 03/15/2015 08:47 AM, "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm@gmx.net>" wrote:
>
> > Should this work?
> >
> >      struct V(string s) {
> >      }
> >
> >      struct S(int U) {
> >          V!"xyz" x;
> >          alias x this;
> >      }
> >
> >      void main() {
> >          S!10 a;
> >          static assert(is(a : V!Args, Args...));
> >      }
> >
> > With DMD Git master, the static assert() fails. Should it? Am
> I doing
> > something wrong? How can I test whether something is
> implicitly
> > convertible to any instance of a particular template?
>
> There is no way other than checking for compile-time duck typing (see the implementations of isInputRange and others).
>
> One reason is that the compiler does not have the concept of "an instance of a template". Templates are for code generation and only the end-result (i.e. S!10) lives as a concept when compiling.

The code contained a small mistake, I forgot a `typeof()`:

    // static assert(is(a : V!Args, Args...));
    // should be:
    static assert(is(typeof(a) : V!Args, Args...));

This still fails, but it works when I change it to:

    static assert(is(typeof(a) : S!Args, Args...));

This means I can indeed test whether something _is_ an instance of a template. It just doesn't take the `alias this` into account. So I guess that's a bug?
March 15, 2015
On Sunday, 15 March 2015 at 16:53:34 UTC, Marc Schütz wrote:
> On Sunday, 15 March 2015 at 16:44:14 UTC, Ali Çehreli wrote:
>> On 03/15/2015 08:47 AM, "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm@gmx.net>" wrote:
>>
>> > Should this work?
>> >
>> >     struct V(string s) {
>> >     }
>> >
>> >     struct S(int U) {
>> >         V!"xyz" x;
>> >         alias x this;
>> >     }
>> >
>> >     void main() {
>> >         S!10 a;
>> >         static assert(is(a : V!Args, Args...));
>> >     }
>> >
>> > With DMD Git master, the static assert() fails. Should it? Am
>> I doing
>> > something wrong? How can I test whether something is
>> implicitly
>> > convertible to any instance of a particular template?
>>
>> There is no way other than checking for compile-time duck typing (see the implementations of isInputRange and others).
>>
>> One reason is that the compiler does not have the concept of "an instance of a template". Templates are for code generation and only the end-result (i.e. S!10) lives as a concept when compiling.
>
> The code contained a small mistake, I forgot a `typeof()`:
>
>     // static assert(is(a : V!Args, Args...));
>     // should be:
>     static assert(is(typeof(a) : V!Args, Args...));
>
> This still fails, but it works when I change it to:
>
>     static assert(is(typeof(a) : S!Args, Args...));
>
> This means I can indeed test whether something _is_ an instance of a template. It just doesn't take the `alias this` into account. So I guess that's a bug?

Ok, now I'm pretty sure:

    class V(string s) {
    }

    class S(int U) : V!"xyz" {
    }

    void main() {
        S!10 a;
        static if(is(typeof(a) : V!Args, Args...))
            pragma(msg, Args);
    }

This works, and it even correctly infers `Args` to be `tuple("xyz")`. As `alias this` is supposed to be interchangeable with subtyping, it must be a bug.
March 15, 2015
https://issues.dlang.org/show_bug.cgi?id=14286

In the meantime, does someone know of a suitable workaround?
March 15, 2015
On Sunday, 15 March 2015 at 17:03:42 UTC, Marc Schütz wrote:
> https://issues.dlang.org/show_bug.cgi?id=14286
>
> In the meantime, does someone know of a suitable workaround?

I found the following workaround. Not beautiful, but it works:

enum isValue(alias T) = __traits(compiles, typeof(T));

template isConvertibleToInstanceOf(alias From, alias To)
if(isValue!From)
{
    enum isConvertibleToInstanceOf = isConvertibleToInstanceOf!(typeof(From), To);
}

template isConvertibleToInstanceOf(From, alias To)
if(!is(From == struct) && !is(From == class) && !is(From == interface))
{
    enum isConvertibleToInstanceOf = false;
}

template isConvertibleToInstanceOf(From, alias To)
if(is(From == struct) || is(From == class) || is(From == interface))
{
    // workaround for https://issues.dlang.org/show_bug.cgi?id=14286
    import std.typetuple : anySatisfy;
    enum aliasThisConvertible(string name) = isConvertibleToInstanceOf!(mixin("typeof(From." ~ name ~ ")"), To);
    enum isConvertibleToInstanceOf =
            anySatisfy!(aliasThisConvertible, __traits(getAliasThis, From)) ||
            is(From : To!Args, Args...);
}
March 15, 2015
On Sunday, 15 March 2015 at 18:33:32 UTC, Marc Schütz wrote:
> On Sunday, 15 March 2015 at 17:03:42 UTC, Marc Schütz wrote:
>> https://issues.dlang.org/show_bug.cgi?id=14286
>>
>> In the meantime, does someone know of a suitable workaround?
>
> I found the following workaround. Not beautiful, but it works:
>
> enum isValue(alias T) = __traits(compiles, typeof(T));
>
> template isConvertibleToInstanceOf(alias From, alias To)
> if(isValue!From)
> {
>     enum isConvertibleToInstanceOf = isConvertibleToInstanceOf!(typeof(From), To);
> }
>
> template isConvertibleToInstanceOf(From, alias To)
> if(!is(From == struct) && !is(From == class) && !is(From == interface))
> {
>     enum isConvertibleToInstanceOf = false;
> }
>
> template isConvertibleToInstanceOf(From, alias To)
> if(is(From == struct) || is(From == class) || is(From == interface))
> {
>     // workaround for https://issues.dlang.org/show_bug.cgi?id=14286
>     import std.typetuple : anySatisfy;
>     enum aliasThisConvertible(string name) = isConvertibleToInstanceOf!(mixin("typeof(From." ~ name ~ ")"), To);
>     enum isConvertibleToInstanceOf =
>             anySatisfy!(aliasThisConvertible, __traits(getAliasThis, From)) ||
>             is(From : To!Args, Args...);
> }

It works for your previous code example:
static assert(isConvertibleToInstanceOf!(S!10, V)); // OK

But this also works:
static assert(!isConvertibleToInstanceOf!(S!10, V!"abc")); // OK

Can be reduced to:
struct Foo(int i) {}
alias Foo1 = Foo!1;
static assert(is(Foo!2 == Foo1!T, T...)); // OK

I think it's another bug.

March 15, 2015
On Sunday, 15 March 2015 at 18:53:33 UTC, Nicolas Sicard wrote:
> Can be reduced to:
> struct Foo(int i) {}
> alias Foo1 = Foo!1;
> static assert(is(Foo!2 == Foo1!T, T...)); // OK
>
> I think it's another bug.

Right, I've filed another report:
https://issues.dlang.org/show_bug.cgi?id=14290