Thread overview
foreach on a tuple using aliases
Aug 05, 2018
Alex
Aug 05, 2018
Timon Gehr
Aug 06, 2018
Timon Gehr
August 05, 2018
I have found something that looks like a bug to me, but also looks like it could simply be a limitation of the foreach construct.

Consider this code:

struct Foo {}

enum isFoo(alias x) = is(typeof(x) == Foo);

void main()
{
    Foo foo;
    assert(isFoo!foo);
    static struct X { int i; Foo foo; }
    X x;
    foreach(i, ref item; x.tupleof)
        static if(is(typeof(item) == Foo))  // line A
            static assert(isFoo!item);      // line B
        else
            static assert(!isFoo!item);
}

Consider just the two lines A and B. If you saw those lines anywhere, given the isFoo definition, you would expect the assert to pass. But in this case, it fails.

What is happening is that the first time through the loop, we are considering x.i. This is an int, and not a Foo, so it assigns false to the template isFoo!item.

The second time through the loop on x.foo, the compiler decides that it ALREADY FIGURED OUT isFoo!item, and so it just substitutes false, even though the item in question is a different item.

So is this a bug? Is it expected? Is it too difficult to fix?

The workaround of course is to use x.tupleof[i] when instantiating isFoo. But it's a bit ugly. I can also see other issues cropping up if you use `item` for other meta things.

-Steve
August 05, 2018
On Sunday, 5 August 2018 at 14:07:30 UTC, Steven Schveighoffer wrote:
> I have found something that looks like a bug to me, but also looks like it could simply be a limitation of the foreach construct.
>
> Consider this code:
>
> struct Foo {}
>
> enum isFoo(alias x) = is(typeof(x) == Foo);
>
> void main()
> {
>     Foo foo;
>     assert(isFoo!foo);
>     static struct X { int i; Foo foo; }
>     X x;
>     foreach(i, ref item; x.tupleof)
>         static if(is(typeof(item) == Foo))  // line A
>             static assert(isFoo!item);      // line B
>         else
>             static assert(!isFoo!item);
> }
>
> Consider just the two lines A and B. If you saw those lines anywhere, given the isFoo definition, you would expect the assert to pass. But in this case, it fails.
>
> What is happening is that the first time through the loop, we are considering x.i. This is an int, and not a Foo, so it assigns false to the template isFoo!item.
>
> The second time through the loop on x.foo, the compiler decides that it ALREADY FIGURED OUT isFoo!item, and so it just substitutes false, even though the item in question is a different item.
>
> So is this a bug? Is it expected? Is it too difficult to fix?
>
> The workaround of course is to use x.tupleof[i] when instantiating isFoo. But it's a bit ugly. I can also see other issues cropping up if you use `item` for other meta things.
>
> -Steve

Another workaround would be
´´´
void main()
{
    Foo foo;
    assert(isFoo!foo);
    static struct X { int i; Foo foo; }
    X x;
    static foreach(i, item; typeof(x).tupleof)
        static if(is(typeof(item) == Foo))  // line A
            static assert(isFoo!item);      // line B
        else
            static assert(!isFoo!item);
}
´´´
wouldn't it?
August 05, 2018
On 05.08.2018 16:07, Steven Schveighoffer wrote:
> I have found something that looks like a bug to me, but also looks like it could simply be a limitation of the foreach construct.
> 
> Consider this code:
> 
> struct Foo {}
> 
> enum isFoo(alias x) = is(typeof(x) == Foo);
> 
> void main()
> {
>      Foo foo;
>      assert(isFoo!foo);
>      static struct X { int i; Foo foo; }
>      X x;
>      foreach(i, ref item; x.tupleof)
>          static if(is(typeof(item) == Foo))  // line A
>              static assert(isFoo!item);      // line B
>          else
>              static assert(!isFoo!item);
> }
> 
> Consider just the two lines A and B. If you saw those lines anywhere, given the isFoo definition, you would expect the assert to pass. But in this case, it fails.
> 
> What is happening is that the first time through the loop, we are considering x.i. This is an int, and not a Foo, so it assigns false to the template isFoo!item.
> 
> The second time through the loop on x.foo, the compiler decides that it ALREADY FIGURED OUT isFoo!item, and so it just substitutes false, even though the item in question is a different item.
> 
> So is this a bug? Is it expected?

It's a bug. The two copies of 'item' are not supposed to be the same symbol. (Different types -> different symbols.)

> Is it too difficult to fix?
> ...

Unlikely.

> The workaround of course is to use x.tupleof[i] when instantiating isFoo. But it's a bit ugly. I can also see other issues cropping up if you use `item` for other meta things.
> 
> -Steve

August 06, 2018
On 8/5/18 10:48 AM, Alex wrote:
> void main()
> {
>      Foo foo;
>      assert(isFoo!foo);
>      static struct X { int i; Foo foo; }
>      X x;
>      static foreach(i, item; typeof(x).tupleof)
>          static if(is(typeof(item) == Foo))  // line A
>              static assert(isFoo!item);      // line B
>          else
>              static assert(!isFoo!item);
> }

I did try static foreach, but it doesn't work.

The difference here is you are using typeof(x).tupleof, whereas I want x.tupleof.

Note that in my real code, I do more than just the static assert, I want to use item as a reference to the real field in x.

-Steve
August 06, 2018
On 8/5/18 11:40 AM, Timon Gehr wrote:
> On 05.08.2018 16:07, Steven Schveighoffer wrote:
>> So is this a bug? Is it expected?
> 
> It's a bug. The two copies of 'item' are not supposed to be the same symbol. (Different types -> different symbols.)

Yep. I even found it has nothing to do with foreach on a tuple: https://run.dlang.io/is/vxQlIi

I wonder though, it shouldn't really be a different type that triggers it, right? I mean 2 separate aliases to different variables that are the same type, I would hope would re-instantiate. Otherwise something like .offsetof would be wrong.

> 
>> Is it too difficult to fix?
>> ...
> 
> Unlikely.

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

-Steve
August 06, 2018
On 06.08.2018 14:37, Steven Schveighoffer wrote:
> On 8/5/18 11:40 AM, Timon Gehr wrote:
>> On 05.08.2018 16:07, Steven Schveighoffer wrote:
>>> So is this a bug? Is it expected?
>>
>> It's a bug. The two copies of 'item' are not supposed to be the same symbol. (Different types -> different symbols.)
> 
> Yep. I even found it has nothing to do with foreach on a tuple: https://run.dlang.io/is/vxQlIi
> 
> I wonder though, it shouldn't really be a different type that triggers it, right?

It shouldn't.