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