enum confusion

Don Allen donaldcallen at gmail.com
Mon May 9 01:50:44 UTC 2022


I've posted here before and then took a D hiatus. I'm working on 
the pre-hiatus project again, which involves porting about 9000 
lines of working but ugly C I wrote 10 years ago, implementing a 
suite of personal financial management software.

Programming languages are an area of interest of mine and have 
been for a long time. In the years since I started writing code 
(over 60 years ago!) professionally and otherwise, I've used just 
about every language you can think of and some you can't. I 
considered a number of languages for this project (e.g, Rust, 
Scheme, Haskell, Nim, Zig) and rejected all for various reasons. 
So far, I've been mostly happy with my choice of D and much of 
the work is done, with the core functionality working. The code 
is more concise and readable than the C it came from. Performance 
is absolutely fine. I love the fast compile times (dmd on a 
FreeBSD system) and the ability to debug with gdb.

But ... you knew this was coming ... I find enums and/or their 
documentation to be a weak spot. As time permits, I'll have more 
to say about this, but I want to raise an initial issue here for 
comments.

The Language Reference has three subsections on enums -- Named 
Enums, Anonymous Enums and Manifest Constants. The first mention 
that I can find of when enums are evaluated and how/where they 
are stored occurs in the Manifest Constants subsection (17.3): 
"Manifest constants are not lvalues, meaning their address cannot 
be taken. They exist only in the memory of the compiler." This 
can be read to suggest that the other two types of enums 
described *are* lvalues, though the documentation doesn't say one 
way or the other. Finding this hard to believe, I wrote a little 
test code:

````
   1 import std.stdio;
   2
   3 enum Foo {
   4     bar
   5 }
   6
   7 int main(string[] args)
   8 {
   9     writefln("debug: %d\n", &(Foo.bar));
  10     return 0;
  11 }
````

Compiling results in

test1.d(9): Error: manifest constant `bar` cannot be modified

Foo.bar is *not* a manifest constant according to the Language 
Ref; it's a Named Enum as described in 17.1. 17.3 defines 
manifest constants as a special case of Anonymous Enums, those 
having a single member. But if you add another member to the 
Named Enum in my test:

````
   1 import std.stdio;
   2
   3 enum Foo {
   4     bar,
   5     baz
   6 }
   7
   8 int main(string[] args)
   9 {
  10     writefln("debug: %d\n", &(Foo.bar));
  11     return 0;
  12 }
````

making a Named Enum with multiple members -- certainly not what 
the documentation is calling a manifest constant -- and yet you 
get the same error message from the compiler, referring to 'bar' 
as a manifest constant.

This suggests to me that the compiler is doing what I expect it 
would and should do -- evaluating all enums at compile time, none 
of them lvalues -- whether named, anonymous or what the 
documentation calls manifest constants. It would appear that the 
only difference among the various enum flavors is whether a new 
type is created or not (named vs anonymous) and a little 
syntactic sugar allowing the omission of braces for single-member 
anonymous enums. There may be additional differences in how the 
various cases are treated by to!string that I can't discuss right 
now because I haven't looked at this carefully enough.

So my conclusion is that there's a disconnect between the 
Language Reference and what the compiler is actually doing and I 
think the problem is that documentation is incorrect, or 
misleading at best.

I'm happy to hear the observations of people who know this 
language better than I do. Perhaps I'm missing something here.






More information about the Digitalmars-d mailing list