Thread overview
is(some template instantiation) is true, but the actual instantiation fails
Jan 29, 2016
Adrian Matoga
Jan 29, 2016
Basile B.
Jan 29, 2016
Adrian Matoga
Jan 29, 2016
Basile B.
Jan 30, 2016
Adrian Matoga
Jan 30, 2016
Ali Çehreli
Jan 30, 2016
Adrian Matoga
January 29, 2016
Code:

----
struct HasFoo { void foo() {} }

struct NoFoo {}

struct CallsFoo(T) {
	T t;
	void bar() { t.foo(); }
}

static assert(is(CallsFoo!HasFoo));
alias Bar = CallsFoo!HasFoo;

static assert(is(CallsFoo!NoFoo)); // (1)
//alias Baz = CallsFoo!NoFoo;      // (2)
----

This compiles, although I expected that (1) should fail.
Now try uncommenting (2) and it can't be compiled.

Why does `is(CallsFoo!NoFoo)` evaluate to true if `is(CallsFoo!NoFoo)` can't be instantiated?
Am I missing something about `is(T)` or is it a bug?
How can I reliably test if CallsFoo can be instantiated?

January 29, 2016
On Friday, 29 January 2016 at 15:28:29 UTC, Adrian Matoga wrote:
> How can I reliably test if CallsFoo can be instantiated?

You can use a constraint to prevent invalid instantiation:

struct HasFoo { void foo() {} }

struct NoFoo {}

struct CallsFoo(T)
if (__traits(hasMember, T, "foo"))
{
	T t;
	void bar() { t.foo(); }
}

static assert(is(CallsFoo!HasFoo));
static assert(!is(CallsFoo!NoFoo));



January 29, 2016
On 1/29/16 10:28 AM, Adrian Matoga wrote:
> Code:
>
> ----
> struct HasFoo { void foo() {} }
>
> struct NoFoo {}
>
> struct CallsFoo(T) {
>      T t;
>      void bar() { t.foo(); }
> }
>
> static assert(is(CallsFoo!HasFoo));
> alias Bar = CallsFoo!HasFoo;
>
> static assert(is(CallsFoo!NoFoo)); // (1)
> //alias Baz = CallsFoo!NoFoo;      // (2)
> ----
>
> This compiles, although I expected that (1) should fail.
> Now try uncommenting (2) and it can't be compiled.
>
> Why does `is(CallsFoo!NoFoo)` evaluate to true if `is(CallsFoo!NoFoo)`
> can't be instantiated?
> Am I missing something about `is(T)` or is it a bug?
> How can I reliably test if CallsFoo can be instantiated?
>

is(T) is supposed to be false if T is not a valid type.

I would agree with you that the static assert should fail.

-Steve
January 29, 2016
On Friday, 29 January 2016 at 16:36:01 UTC, Steven Schveighoffer wrote:
> On 1/29/16 10:28 AM, Adrian Matoga wrote:
>> Code:
>>
>> ----
>> struct HasFoo { void foo() {} }
>>
>> struct NoFoo {}
>>
>> struct CallsFoo(T) {
>>      T t;
>>      void bar() { t.foo(); }
>> }
>>
>> static assert(is(CallsFoo!HasFoo));
>> alias Bar = CallsFoo!HasFoo;
>>
>> static assert(is(CallsFoo!NoFoo)); // (1)
>> //alias Baz = CallsFoo!NoFoo;      // (2)
>> ----
>>
>> This compiles, although I expected that (1) should fail.
>> Now try uncommenting (2) and it can't be compiled.
>>
>> Why does `is(CallsFoo!NoFoo)` evaluate to true if `is(CallsFoo!NoFoo)`
>> can't be instantiated?
>> Am I missing something about `is(T)` or is it a bug?
>> How can I reliably test if CallsFoo can be instantiated?
>>
>
> is(T) is supposed to be false if T is not a valid type.
>
> I would agree with you that the static assert should fail.
>
> -Steve

Oh, there's more:
// this should fail:
static assert(is(CallsFoo!NoFoo));
// this should fail too:
static assert(is(typeof({ alias Baz = CallsFoo!NoFoo; return Baz.init; }())));
// and this:
static assert(__traits(compiles, { alias Baz = CallsFoo!NoFoo; return Baz.init; }()));
// but only this fails:
alias Baz = CallsFoo!NoFoo;

https://issues.dlang.org/show_bug.cgi?id=15623

January 29, 2016
On Friday, 29 January 2016 at 17:01:46 UTC, Adrian Matoga wrote:
> On Friday, 29 January 2016 at 16:36:01 UTC, Steven Schveighoffer wrote:
>> On 1/29/16 10:28 AM, Adrian Matoga wrote:
>>>[...]
>>
>> is(T) is supposed to be false if T is not a valid type.
>>
>> I would agree with you that the static assert should fail.
>>
>> -Steve
>
> Oh, there's more:
> // this should fail:
> static assert(is(CallsFoo!NoFoo));
> // this should fail too:
> static assert(is(typeof({ alias Baz = CallsFoo!NoFoo; return Baz.init; }())));
> // and this:
> static assert(__traits(compiles, { alias Baz = CallsFoo!NoFoo; return Baz.init; }()));
> // but only this fails:
> alias Baz = CallsFoo!NoFoo;
>
> https://issues.dlang.org/show_bug.cgi?id=15623

Haven't you seen my answer about constraint ?

If you put a constraint on your function template then invalid instantiations are rejected. I mean... this language feature is not just ornamental...

What do you think constraints are used for otherwise ^^
January 29, 2016
On 1/29/16 6:44 PM, Basile B. wrote:
>
> Haven't you seen my answer about constraint ?
>
> If you put a constraint on your function template then invalid
> instantiations are rejected. I mean... this language feature is not just
> ornamental...
>
> What do you think constraints are used for otherwise ^^

A constraint should not be necessary here. Constraints are useful when you have multiple templates that may match (without specializations), or you want to affect the way the compiler reports errors.

Iff a template instantiation T compiles, then is(T) should evaluate to true. At least, that's my understanding.

-Steve
January 29, 2016
On 01/29/2016 09:01 AM, Adrian Matoga wrote:

> Oh, there's more:
> // this should fail:
> static assert(is(CallsFoo!NoFoo));
> // this should fail too:
> static assert(is(typeof({ alias Baz = CallsFoo!NoFoo; return Baz.init;
> }())));
> // and this:
> static assert(__traits(compiles, { alias Baz = CallsFoo!NoFoo; return
> Baz.init; }()));
> // but only this fails:
> alias Baz = CallsFoo!NoFoo;
>
> https://issues.dlang.org/show_bug.cgi?id=15623

As I noted on the bug report, they are work when moved from module scope to inside a function (e.g. main()). At least there's that workaround...

Ali

January 30, 2016
On Friday, 29 January 2016 at 23:44:56 UTC, Basile B. wrote:
> Haven't you seen my answer about constraint ?
>
> If you put a constraint on your function template then invalid instantiations are rejected. I mean... this language feature is not just ornamental...
>
> What do you think constraints are used for otherwise ^^

Yes, I've seen it, thanks.
Requiring the user to write the constraint might indeed enforce a better style, but I want to be able to test it even if the user forgets the constraint. Otherwise she'll get cryptic error messages from some other code assuming that CallsFoo!NoFoo is a valid type.

January 30, 2016
On Saturday, 30 January 2016 at 00:16:21 UTC, Ali Çehreli wrote:
> > https://issues.dlang.org/show_bug.cgi?id=15623
>
> As I noted on the bug report, they are work when moved from module scope to inside a function (e.g. main()). At least there's that workaround...
>
> Ali

Thanks a lot! Now I can continue my work. :)