www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - foreach on a tuple using aliases

reply Steven Schveighoffer <schveiguy gmail.com> writes:
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
Aug 05 2018
next sibling parent reply Alex <sascha.orlov gmail.com> writes:
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?
Aug 05 2018
parent Steven Schveighoffer <schveiguy gmail.com> writes:
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
Aug 06 2018
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
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
Aug 05 2018
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
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
Aug 06 2018
parent Timon Gehr <timon.gehr gmx.ch> writes:
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.
Aug 06 2018