www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Shouldn't __traits return Tuples?

reply Trass3r <mrmocool gmx.de> writes:
Shouldn't __traits return Tuples instead of arrays (esp. allMembers) to 
allow sophisticated compile time reflection?
Currently it seems impossible to do something along the lines of

foreach (member; __traits (allMembers, Class))
{
     foreach (overload; __traits (getVirtualFunctions, Class, member))
     {
         // do stuff
     }
}

Maybe it would be possible with Tuples using template recursion.



Also the following doesn't work with dmd, returns 0 for all members:

Base base = new Base;
auto members = __traits(allMembers, typeof(base));
foreach(m; members)
     writefln(base.classinfo.getMembers(m).length);
Mar 30 2009
next sibling parent reply Max Samukha <samukha voliacable.com.removethis> writes:
On Mon, 30 Mar 2009 15:06:09 +0200, Trass3r <mrmocool gmx.de> wrote:

Shouldn't __traits return Tuples instead of arrays (esp. allMembers) to 
allow sophisticated compile time reflection?
Currently it seems impossible to do something along the lines of

foreach (member; __traits (allMembers, Class))
{
     foreach (overload; __traits (getVirtualFunctions, Class, member))
     {
         // do stuff
     }
}

It's possible to statically unroll the foreach's with a template generating a tuple of consequtive integers (not tested): template Sequence(size_t count, size_t index = 0) { static if (index < count) alias Tuple!(index, Sequence!(count, index + 1)) Sequence; ... } enum members = __traits (allMembers, Class); foreach (i; Sequence!(members.length)) { enum funcs = __traits (getVirtualFunctions, Class, members[i]); foreach (j; Sequence!(funcs.length)) { // do stuff } } I'm not sure __traits should return tuples because arrays can be used with CTFE without generating an extra symbol, meaning less code bloat.
Maybe it would be possible with Tuples using template recursion.



Also the following doesn't work with dmd, returns 0 for all members:

Base base = new Base;
auto members = __traits(allMembers, typeof(base));
foreach(m; members)
     writefln(base.classinfo.getMembers(m).length);

getMembers has not been implemented, AFAIK
Mar 30 2009
parent reply Trass3r <mrmocool gmx.de> writes:
Max Samukha schrieb:
 It's possible to statically unroll the foreach's with a template
 generating a tuple of consequtive integers (not tested):
 
 template Sequence(size_t count, size_t index = 0)
 {
    static if (index < count)
       alias Tuple!(index, Sequence!(count, index + 1)) Sequence;  
    ...
 }
 
 enum members = __traits (allMembers, Class);
 foreach (i; Sequence!(members.length))
 {
      enum funcs = __traits (getVirtualFunctions, Class, members[i]);
      foreach (j; Sequence!(funcs.length))
      {
          // do stuff
      }
 }

Well this is interesting. That code yields: variable main.main._funcs_field_0 cannot be declared to be a function. class Class { int foo(int i) { return 1; } } [...] auto funcs = __traits(getVirtualFunctions, Class, members[i])(1); yields Error: function expected before (), not tuple(foo) of type (int(int i)) So it is a tuple although the docs tell us it's an array? Strangely this doesn't work either: enum members = __traits (allMembers, Class); foreach (i; Sequence!(members.length)) { foreach (j; __traits(getVirtualFunctions, Class, members[i])) { writefln(typeid(typeof(j))); // do stuff } } Though it correctly gets foo() (leaving out typeof above yields "function main.Class.foo is used as a type") it doesn't compile when adding typeof: "variable main.main.i voids have no value"
Mar 30 2009
parent reply Max Samukha <samukha voliacable.com.removethis> writes:
On Mon, 30 Mar 2009 22:04:57 +0200, Trass3r <mrmocool gmx.de> wrote:

Max Samukha schrieb:
 It's possible to statically unroll the foreach's with a template
 generating a tuple of consequtive integers (not tested):
 
 template Sequence(size_t count, size_t index = 0)
 {
    static if (index < count)
       alias Tuple!(index, Sequence!(count, index + 1)) Sequence;  
    ...
 }
 
 enum members = __traits (allMembers, Class);
 foreach (i; Sequence!(members.length))
 {
      enum funcs = __traits (getVirtualFunctions, Class, members[i]);
      foreach (j; Sequence!(funcs.length))
      {
          // do stuff
      }
 }

Well this is interesting. That code yields: variable main.main._funcs_field_0 cannot be declared to be a function. class Class { int foo(int i) { return 1; } } [...] auto funcs = __traits(getVirtualFunctions, Class, members[i])(1); yields Error: function expected before (), not tuple(foo) of type (int(int i)) So it is a tuple although the docs tell us it's an array?

Yeah, __traits returns expressions of various types depending on the Id. In case of getVirtualFunctions, it's a tuple expression. You may want to look through traits.c in dmd sources to see what's going on.
Strangely this doesn't work either:

enum members = __traits (allMembers, Class);
foreach (i; Sequence!(members.length))
{
	
      foreach (j; __traits(getVirtualFunctions, Class, members[i]))
      {
		writefln(typeid(typeof(j)));
          // do stuff
      }
}

Though it correctly gets foo() (leaving out typeof above yields 
"function main.Class.foo is used as a type") it doesn't compile when 
adding typeof:

"variable main.main.i voids have no value"

__traits is buggy. You could try to do what you want like this: template isFunction(C, string member) { enum isFunction = is(typeof(mixin("C." ~ member)) == function); } void main() { //SpinLock lock; enum members = __traits(allMembers, Class); foreach (i; Sequence!(members.length)) { static if (isFunction!(Class, members[i])) { foreach (j; __traits(getVirtualFunctions, Class, members[i])) writefln(members[i], ": ", typeid(typeof(j))); } } }
Mar 31 2009
next sibling parent Max Samukha <samukha voliacable.com.removethis> writes:
On Tue, 31 Mar 2009 10:55:22 +0200, Max Samukha
<samukha voliacable.com.removethis> wrote:

On Mon, 30 Mar 2009 22:04:57 +0200, Trass3r <mrmocool gmx.de> wrote:

Max Samukha schrieb:
 It's possible to statically unroll the foreach's with a template
 generating a tuple of consequtive integers (not tested):
 
 template Sequence(size_t count, size_t index = 0)
 {
    static if (index < count)
       alias Tuple!(index, Sequence!(count, index + 1)) Sequence;  
    ...
 }
 
 enum members = __traits (allMembers, Class);
 foreach (i; Sequence!(members.length))
 {
      enum funcs = __traits (getVirtualFunctions, Class, members[i]);
      foreach (j; Sequence!(funcs.length))
      {
          // do stuff
      }
 }

Well this is interesting. That code yields: variable main.main._funcs_field_0 cannot be declared to be a function. class Class { int foo(int i) { return 1; } } [...] auto funcs = __traits(getVirtualFunctions, Class, members[i])(1); yields Error: function expected before (), not tuple(foo) of type (int(int i)) So it is a tuple although the docs tell us it's an array?

Yeah, __traits returns expressions of various types depending on the Id. In case of getVirtualFunctions, it's a tuple expression. You may want to look through traits.c in dmd sources to see what's going on.
Strangely this doesn't work either:

enum members = __traits (allMembers, Class);
foreach (i; Sequence!(members.length))
{
	
      foreach (j; __traits(getVirtualFunctions, Class, members[i]))
      {
		writefln(typeid(typeof(j)));
          // do stuff
      }
}

Though it correctly gets foo() (leaving out typeof above yields 
"function main.Class.foo is used as a type") it doesn't compile when 
adding typeof:

"variable main.main.i voids have no value"

__traits is buggy. You could try to do what you want like this: template isFunction(C, string member) { enum isFunction = is(typeof(mixin("C." ~ member)) == function); } void main() { //SpinLock lock; enum members = __traits(allMembers, Class); foreach (i; Sequence!(members.length)) { static if (isFunction!(Class, members[i])) { foreach (j; __traits(getVirtualFunctions, Class, members[i])) writefln(members[i], ": ", typeid(typeof(j))); } } }

SpinLock, you are out of place here.
Mar 31 2009
prev sibling next sibling parent reply Trass3r <mrmocool gmx.de> writes:
Max Samukha schrieb:
 So it is a tuple although the docs tell us it's an array?

Yeah, __traits returns expressions of various types depending on the Id. In case of getVirtualFunctions, it's a tuple expression. You may want to look through traits.c in dmd sources to see what's going on.

So the docs are incorrect: "getVirtualFunctions The result is an array of the virtual overloads of that function."
Mar 31 2009
parent Max Samukha <samukha voliacable.com.removethis> writes:
On Tue, 31 Mar 2009 13:53:02 +0200, Trass3r <mrmocool gmx.de> wrote:

Max Samukha schrieb:
 So it is a tuple although the docs tell us it's an array?

Yeah, __traits returns expressions of various types depending on the Id. In case of getVirtualFunctions, it's a tuple expression. You may want to look through traits.c in dmd sources to see what's going on.

So the docs are incorrect: "getVirtualFunctions The result is an array of the virtual overloads of that function."

I've got a feeling that the compile-time introspection thing is going to change, so relying on its current crippled implementation is risky.
Mar 31 2009
prev sibling parent reply Trass3r <mrmocool gmx.de> writes:
Max Samukha schrieb:
 "variable main.main.i voids have no value"

__traits is buggy. template isFunction(C, string member) { enum isFunction = is(typeof(mixin("C." ~ member)) == function); } void main() { //SpinLock lock; enum members = __traits(allMembers, Class); foreach (i; Sequence!(members.length)) { static if (isFunction!(Class, members[i])) { foreach (j; __traits(getVirtualFunctions, Class, members[i])) writefln(members[i], ": ", typeid(typeof(j))); } } }

Yeah extreeemely buggy. dmd also crashes if that enum members is changed to const members or invariant members. In fact, what I'm trying to do is getting all the functions of a given class and checking their parameters for correctness.
Mar 31 2009
next sibling parent reply Christopher Wright <dhasenan gmail.com> writes:
Trass3r wrote:
 Yeah extreeemely buggy. dmd also crashes if that enum members is changed 
 to const members or invariant members.

Buggy __traits is the major reason I'm not on d2. If __traits were not buggy, I could do, god, so many things! Though it would be even better to have the runtime equivalent.
Mar 31 2009
parent reply Trass3r <mrmocool gmx.de> writes:
Christopher Wright schrieb:
 Buggy __traits is the major reason I'm not on d2. If __traits were not 
 buggy, I could do, god, so many things!
 
 Though it would be even better to have the runtime equivalent.

Any chance traits get fixed anytime soon?
Mar 31 2009
parent reply Christopher Wright <dhasenan gmail.com> writes:
Trass3r wrote:
 Christopher Wright schrieb:
 Buggy __traits is the major reason I'm not on d2. If __traits were not 
 buggy, I could do, god, so many things!

 Though it would be even better to have the runtime equivalent.

Any chance traits get fixed anytime soon?

I was using __traits + CTFE around 2.006 through 2.011. Every release, things were broken in new and incompatible ways. I could never narrow it down sufficiently to get a good bug report (well, usually not), but I could usually make it work by bashing my code in a few times. The one consistent bug I found was fixed. So I could get back to it, but I don't want to keep chasing the CTFE engine.
Mar 31 2009
parent reply Don <nospam nospam.com> writes:
Christopher Wright wrote:
 Trass3r wrote:
 Christopher Wright schrieb:
 Buggy __traits is the major reason I'm not on d2. If __traits were 
 not buggy, I could do, god, so many things!

 Though it would be even better to have the runtime equivalent.

Any chance traits get fixed anytime soon?

I was using __traits + CTFE around 2.006 through 2.011. Every release, things were broken in new and incompatible ways. I could never narrow it down sufficiently to get a good bug report (well, usually not), but I could usually make it work by bashing my code in a few times. The one consistent bug I found was fixed. So I could get back to it, but I don't want to keep chasing the CTFE engine.

That situation should be much better now that we have all of the source code. If something changes, it'll be really easy to find exactly what caused the change.
Apr 01 2009
parent reply Trass3r <mrmocool gmx.de> writes:
Don schrieb:
 That situation should be much better now that we have all of the source 
 code. If something changes, it'll be really easy to find exactly what 
 caused the change.

Well, if dmd was available in a source control repository we could really easily track the changes.
Apr 01 2009
parent Don <nospam nospam.com> writes:
Trass3r wrote:
 Don schrieb:
 That situation should be much better now that we have all of the 
 source code. If something changes, it'll be really easy to find 
 exactly what caused the change.

Well, if dmd was available in a source control repository we could really easily track the changes.

Agreed, but we effectively have a repository with a once-per-month commit, which isn't too bad. It's more of a problem for people submitting bugfixes -- there's been a couple of cases where someone's submitted a patch, only to be told by Walter that he'd already fixed it. Since there are about 1000 bugs in bugzilla, it's a shame to waste effort that way.
Apr 01 2009
prev sibling parent reply Trass3r <mrmocool gmx.de> writes:
Trass3r schrieb:
     enum members = __traits(allMembers, Class);
     foreach (i; Sequence!(members.length))
     {
         static if (isFunction!(Class, members[i]))
         {
             foreach (j; __traits(getVirtualFunctions, Class,
 members[i]))
                 writefln(members[i], ": ", typeid(typeof(j)));
         }
     }
 }

Yeah extreeemely buggy. dmd also crashes if that enum members is changed to const members or invariant members.

Ok, narrowed that down to: template Sequence(size_t count, size_t index = 0) { static if (index < count) alias Tuple!(index, Sequence!(count, index + 1)) Sequence; } void main() { const members = ["foo", "bar"]; foreach (i; Sequence!(members.length)) { writefln(members[i]); } } Also crashes. Changing to auto or even enum (i mean enum is also constant, so ???) yields: Error: variable main2.main.i voids have no value
Apr 01 2009
parent reply Max Samukha <samukha voliacable.com.removethis> writes:
On Wed, 01 Apr 2009 20:54:12 +0200, Trass3r <mrmocool gmx.de> wrote:

template Sequence(size_t count, size_t index = 0)
{
    static if (index < count)
       alias Tuple!(index, Sequence!(count, index + 1)) Sequence;
}

There was ellipsis to suggest that you should terminate the recursion properly :) Sorry template Sequence(size_t count, size_t index = 0) { static if (index < count) alias Tuple!(index, Sequence!(count, index + 1)) Sequence; else alias Tuple!() Sequence; }
Apr 01 2009
parent reply Trass3r <mrmocool gmx.de> writes:
Max Samukha schrieb:
 On Wed, 01 Apr 2009 20:54:12 +0200, Trass3r <mrmocool gmx.de> wrote:
 
 template Sequence(size_t count, size_t index = 0)
 {
    static if (index < count)
       alias Tuple!(index, Sequence!(count, index + 1)) Sequence;
 }

There was ellipsis to suggest that you should terminate the recursion properly :) Sorry template Sequence(size_t count, size_t index = 0) { static if (index < count) alias Tuple!(index, Sequence!(count, index + 1)) Sequence; else alias Tuple!() Sequence; }

OMG, I knew the recursion base case was missing, but I couldn't imagine how to represent "nothing". Of course, an empty tuple. But the reason I posted is cause that bug(?) is still present. dmd doesn't crash anymore, but using const still doesn't work.
Apr 01 2009
parent reply Max Samukha <samukha voliacable.com.removethis> writes:
On Wed, 01 Apr 2009 23:22:40 +0200, Trass3r <mrmocool gmx.de> wrote:

OMG, I knew the recursion base case was missing, but I couldn't imagine 
how to represent "nothing". Of course, an empty tuple.

But the reason I posted is cause that bug(?) is still present.
dmd doesn't crash anymore, but using const still doesn't work.

I think it's not a bug because "const" storage class doesn't always mean "compile time". It's rather a variable (we should find a better name) which is initialized once and cannot be changed later. To ensure compiletimeness, you can declare the variable as "static const", "static invariant" or "enum"
Apr 01 2009
parent reply Trass3r <mrmocool gmx.de> writes:
Max Samukha schrieb:
 On Wed, 01 Apr 2009 23:22:40 +0200, Trass3r <mrmocool gmx.de> wrote:
 
 OMG, I knew the recursion base case was missing, but I couldn't imagine 
 how to represent "nothing". Of course, an empty tuple.

 But the reason I posted is cause that bug(?) is still present.
 dmd doesn't crash anymore, but using const still doesn't work.

I think it's not a bug because "const" storage class doesn't always mean "compile time". It's rather a variable (we should find a better name) which is initialized once and cannot be changed later. To ensure compiletimeness, you can declare the variable as "static const", "static invariant" or "enum"

Man, static is one of the things that are totally confusing. It has various meanings depending on where you use it and moreover it's hard to find documentation about the one you need cause it's spread all over the docs. But what you are talking about is in fact what "final" does (once initialized it can't be changed).
Apr 01 2009
parent Max Samukha <samukha voliacable.com.removethis> writes:
On Thu, 02 Apr 2009 00:07:56 +0200, Trass3r <mrmocool gmx.de> wrote:

Max Samukha schrieb:
 On Wed, 01 Apr 2009 23:22:40 +0200, Trass3r <mrmocool gmx.de> wrote:
 
 OMG, I knew the recursion base case was missing, but I couldn't imagine 
 how to represent "nothing". Of course, an empty tuple.

 But the reason I posted is cause that bug(?) is still present.
 dmd doesn't crash anymore, but using const still doesn't work.

I think it's not a bug because "const" storage class doesn't always mean "compile time". It's rather a variable (we should find a better name) which is initialized once and cannot be changed later. To ensure compiletimeness, you can declare the variable as "static const", "static invariant" or "enum"

Man, static is one of the things that are totally confusing. It has various meanings depending on where you use it and moreover it's hard to find documentation about the one you need cause it's spread all over the docs.

Agree, there are too many meanings of 'static' - global, compile-time, fixed-size, non-instance. The good thing is that these meanings are interrelated. Probably, 'static' in module constructor definitions is redundant. On the other hand, module constructors are not many and could be otherwise easily confused with class instance constructors.
But what you are talking about is in fact what "final" does (once 
initialized it can't be changed).

There is no 'final' storage class in D2. Or you are talking about the concept?
Apr 01 2009
prev sibling next sibling parent Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
On Mon, Mar 30, 2009 at 10:57 AM, Max Samukha
<samukha voliacable.com.removethis> wrote:
Also the following doesn't work with dmd, returns 0 for all members:

Base base =3D new Base;
auto members =3D __traits(allMembers, typeof(base));
foreach(m; members)
 =A0 =A0 writefln(base.classinfo.getMembers(m).length);

getMembers has not been implemented, AFAIK

It has in LDC ;)
Mar 30 2009
prev sibling next sibling parent Tomas Lindquist Olsen <tomas.l.olsen gmail.com> writes:
On Mon, Mar 30, 2009 at 5:11 PM, Jarrett Billingsley
<jarrett.billingsley gmail.com> wrote:
 On Mon, Mar 30, 2009 at 10:57 AM, Max Samukha
 <samukha voliacable.com.removethis> wrote:
Also the following doesn't work with dmd, returns 0 for all members:

Base base =3D new Base;
auto members =3D __traits(allMembers, typeof(base));
foreach(m; members)
 =C2=A0 =C2=A0 writefln(base.classinfo.getMembers(m).length);

getMembers has not been implemented, AFAIK

It has in LDC ;)

I think we need D2 support first ;)
Mar 30 2009
prev sibling next sibling parent Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
On Mon, Mar 30, 2009 at 11:13 AM, Tomas Lindquist Olsen
<tomas.l.olsen gmail.com> wrote:
 getMembers has not been implemented, AFAIK

It has in LDC ;)

I think we need D2 support first ;)

I was talking about the RTTI getMembers function :)
Mar 30 2009
prev sibling next sibling parent Tomas Lindquist Olsen <tomas.l.olsen gmail.com> writes:
On Mon, Mar 30, 2009 at 5:13 PM, Tomas Lindquist Olsen
<tomas.l.olsen gmail.com> wrote:
 On Mon, Mar 30, 2009 at 5:11 PM, Jarrett Billingsley
 <jarrett.billingsley gmail.com> wrote:
 On Mon, Mar 30, 2009 at 10:57 AM, Max Samukha
 <samukha voliacable.com.removethis> wrote:
Also the following doesn't work with dmd, returns 0 for all members:

Base base =3D new Base;
auto members =3D __traits(allMembers, typeof(base));
foreach(m; members)
 =C2=A0 =C2=A0 writefln(base.classinfo.getMembers(m).length);

getMembers has not been implemented, AFAIK

It has in LDC ;)

I think we need D2 support first ;)

Maybe you're thinking of OffsetTypeInfo (classinfo.offTi) ? Yes that can be enabled when configuring LDC, before building the compiler.
Mar 30 2009
prev sibling parent reply Trass3r <mrmocool gmx.de> writes:
Trass3r schrieb:
 foreach (member; __traits (allMembers, Class))
 {
     foreach (overload; __traits (getVirtualFunctions, Class, member))
     {
         // do stuff
     }
 }
 

omg, those bugs are known for nearly 2 years now. http://d.puremagic.com/issues/show_bug.cgi?id=1386 http://d.puremagic.com/issues/show_bug.cgi?id=1499
Apr 01 2009
parent reply Trass3r <mrmocool gmx.de> writes:
I came across another strange bug(?):
Using const, static const or auto instead of enum makes it work.
Using the foreach way also works with enum.

void main()
{
	enum members = ["foo", "bar"];
	for (uint i=0; i<members.length; i++)
//	foreach (i; Sequence!(members.length))
	{
		writefln(members[i]);
	}
}

But enum with for yields:

object.Error: Access Violation
std.encoding.EncodingSchemeASCII            à‘B ANSI_X3.4-1968     ’B 
ANSI_X3.4-1986     (’B ASCII       ’B IBM367     P’B ISO646-US 
   	   `’B ISO_646.irv:1991           x’B US-ASCII           ˜’B 
cp367      °’B csASCIIiso-ir-6    À’B us         Ø’B ?         
è’B 
€“B     std.encoding.EncodingSchemeASCII                TB        ø’B 
      “B    €“B         ”B                                    0“B 
GA ƒ  ƒ  pƒ  tFA GA $GA 4GA LGA œGA ìGA ÔHA  IA `IA KA 8KA dKA 
encoding.1270           pOB
    È“B    ”B    ”B    øGA                 pkB 0“B Unable to
create 
class      ”B Unrecognized Encoding:      ”B à”B 
std.encoding.EncodingScheme             TB        `”B    h”B    à”B 
         0QB                                    ”B 
                          ÔHA  IA `IA KA 8KA dKA         ЕB 
                       std.encoding.UnrecognizedEncodingException  TB 
    $   0•B *   T•B    ЕB          –B          
     €•B €ƒ  ƒ  ƒ  pƒ              –B 
  std.encoding.EncodingException              TB     $   ð•B  
–B    –B         ðYB                                     –B €ƒ  
ƒ  ƒ  pƒ              P—B 
core.exception.UnicodeException         TB     (   °–B    Ø–B   
P—B 
         ðYB                                     —B €ƒ  ƒ  ƒ  pƒ  
          No appropriate switch clause found      "   p—B 0˜B 
                       core.exception.SwitchError  TB     $    —B  
  Ä—B    0˜B         pSB                                    à—B 
€ƒ  ƒ  ƒ  pƒ              Hidden method called for           P˜B 
      ™B                                 core.exception.HiddenFuncError 
              TB     $   €˜B    ¤˜B     ™B         pSB          
                       ИB €ƒ  ƒ  ƒ  pƒ              Finalization 
error          ™B An exception was thrown while finalizing an instance 
of class   >   `™B         PšB 
core.exception.FinalizeError            TB     (   °™B    Ø™B    PšB 
         pSB                                     šB LA ƒ  ƒ  pƒ  
           qB             std.dateparse   pOB
    €šB    ÈšB    КB
                        ðWB  qB À©B                unrecognized 
attribute         äšB redundant attribute        ›B unmatched ')'
    (›B q           ›B            P›B printProgram()
   10:    	REchar '
Apr 01 2009
next sibling parent Daniel Keep <daniel.keep.lists gmail.com> writes:
Trass3r wrote:
 I came across another strange bug(?):
 Using const, static const or auto instead of enum makes it work.
 Using the foreach way also works with enum.
 
 void main()
 {
     enum members = ["foo", "bar"];
     for (uint i=0; i<members.length; i++)
 //    foreach (i; Sequence!(members.length))
     {
         writefln(members[i]);
     }
 }
 
 But enum with for yields:
 
 ...

Wow, I think you just made the executable puke its guts! :S -- Daniel
Apr 01 2009
prev sibling next sibling parent Don <nospam nospam.com> writes:
Trass3r wrote:
 I came across another strange bug(?):
 Using const, static const or auto instead of enum makes it work.
 Using the foreach way also works with enum.
 
 void main()
 {
     enum members = ["foo", "bar"];
     for (uint i=0; i<members.length; i++)
 //    foreach (i; Sequence!(members.length))
     {
         writefln(members[i]);
     }
 }
 
 But enum with for yields:
 
 object.Error: Access Violation
 std.encoding.EncodingSchemeASCII            à‘B ANSI_X3.4-1968     ’B 

Confirmed. Congratulations, you've discovered the Bug of the Year!
Apr 02 2009
prev sibling parent Gide Nwawudu <gide btinternet.com> writes:
On Thu, 02 Apr 2009 03:56:35 +0200, Trass3r <mrmocool gmx.de> wrote:

I came across another strange bug(?):
Using const, static const or auto instead of enum makes it work.
Using the foreach way also works with enum.

void main()
{
	enum members = ["foo", "bar"];
	for (uint i=0; i<members.length; i++)
//	foreach (i; Sequence!(members.length))
	{
		writefln(members[i]);
	}
}

But enum with for yields:

object.Error: Access Violation
std.encoding.EncodingSchemeASCII            B ANSI_X3.4-1968     B 
ANSI_X3.4-1986     (B ASCII       B IBM367     PB ISO646-US 

Logged as http://d.puremagic.com/issues/show_bug.cgi?id=2792 Gide
Apr 03 2009