www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Should all enums be immutable?

reply Jonathan M Davis <jmdavisProg gmx.com> writes:
Enum values cannot be altered. It is an error to try and assign a value to an 
enum. However, the value of an enum isn't really const or immutable. It's 
copied every time that the enum is used. This is fine in cases where the enum 
is a value type, but it's problematic when the enum is a reference type. I 
believe that the classic example is if you have an AA which is an enum. You 
copy _the entire_ AA every time it's referenced. But it's not just AA's. 
Normal arrays have the problem too.

Given that the value of enum must be known at compile time and given that it 
cannot be changed, why aren't enums all immutable? What's gained by making so 
that every reference to an enum is replaced by its value rather than actually 
referencing an immutable value? In most cases, it could still be replaced by 
the value (since it's a constant) if that's more efficient. And in the case of 
reference types, it would actually act like a reference type.

So, I ask, should enums just all be made automatically immutable instead of 
having the current replace when referenced semantics? Is there a good reason 
_not_ to make all enums immutable?

- Jonathan M Davis
Apr 04 2011
next sibling parent reply Don <nospam nospam.com> writes:
Jonathan M Davis wrote:
 Enum values cannot be altered. It is an error to try and assign a value to an 
 enum. However, the value of an enum isn't really const or immutable. It's 
 copied every time that the enum is used. This is fine in cases where the enum 
 is a value type, but it's problematic when the enum is a reference type. I 
 believe that the classic example is if you have an AA which is an enum. You 
 copy _the entire_ AA every time it's referenced. But it's not just AA's. 
 Normal arrays have the problem too.
 
 Given that the value of enum must be known at compile time and given that it 
 cannot be changed, why aren't enums all immutable? What's gained by making so 
 that every reference to an enum is replaced by its value rather than actually 
 referencing an immutable value? In most cases, it could still be replaced by 
 the value (since it's a constant) if that's more efficient. And in the case of 
 reference types, it would actually act like a reference type.
 
 So, I ask, should enums just all be made automatically immutable instead of 
 having the current replace when referenced semantics? Is there a good reason 
 _not_ to make all enums immutable?

Yes. The ONLY reason those manifest constants exist at all, is so that they don't exist at run time. They have no address. Any case where taking a reference to them works, is a compiler bug. Almost all existing enum arrays or AAs are bugs. For example, if you have a lookup table which should be used at runtime, it should ALWAYS be defined as const or immutable, not as an enum.
Apr 04 2011
next sibling parent reply Rainer Schuetze <r.sagitario gmx.de> writes:
Don wrote:
 Jonathan M Davis wrote:
 Enum values cannot be altered. It is an error to try and assign a 
 value to an enum. However, the value of an enum isn't really const or 
 immutable. It's copied every time that the enum is used. This is fine 
 in cases where the enum is a value type, but it's problematic when the 
 enum is a reference type. I believe that the classic example is if you 
 have an AA which is an enum. You copy _the entire_ AA every time it's 
 referenced. But it's not just AA's. Normal arrays have the problem too.

 Given that the value of enum must be known at compile time and given 
 that it cannot be changed, why aren't enums all immutable? What's 
 gained by making so that every reference to an enum is replaced by its 
 value rather than actually referencing an immutable value? In most 
 cases, it could still be replaced by the value (since it's a constant) 
 if that's more efficient. And in the case of reference types, it would 
 actually act like a reference type.

 So, I ask, should enums just all be made automatically immutable 
 instead of having the current replace when referenced semantics? Is 
 there a good reason _not_ to make all enums immutable?

Yes. The ONLY reason those manifest constants exist at all, is so that they don't exist at run time. They have no address. Any case where taking a reference to them works, is a compiler bug. Almost all existing enum arrays or AAs are bugs. For example, if you have a lookup table which should be used at runtime, it should ALWAYS be defined as const or immutable, not as an enum.

Unfortunately, you currently get the same performance penalty with const or immutable arrays as with enum: http://d.puremagic.com/issues/show_bug.cgi?id=4298
Apr 04 2011
next sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Rainer Schuetze" <r.sagitario gmx.de> wrote in message 
news:ind21j$1si5$1 digitalmars.com...
 Don wrote:
 Jonathan M Davis wrote:
 Enum values cannot be altered. It is an error to try and assign a value 
 to an enum. However, the value of an enum isn't really const or 
 immutable. It's copied every time that the enum is used. This is fine in 
 cases where the enum is a value type, but it's problematic when the enum 
 is a reference type. I believe that the classic example is if you have 
 an AA which is an enum. You copy _the entire_ AA every time it's 
 referenced. But it's not just AA's. Normal arrays have the problem too.

 Given that the value of enum must be known at compile time and given 
 that it cannot be changed, why aren't enums all immutable? What's gained 
 by making so that every reference to an enum is replaced by its value 
 rather than actually referencing an immutable value? In most cases, it 
 could still be replaced by the value (since it's a constant) if that's 
 more efficient. And in the case of reference types, it would actually 
 act like a reference type.

 So, I ask, should enums just all be made automatically immutable instead 
 of having the current replace when referenced semantics? Is there a good 
 reason _not_ to make all enums immutable?

Yes. The ONLY reason those manifest constants exist at all, is so that they don't exist at run time. They have no address. Any case where taking a reference to them works, is a compiler bug. Almost all existing enum arrays or AAs are bugs. For example, if you have a lookup table which should be used at runtime, it should ALWAYS be defined as const or immutable, not as an enum.

Unfortunately, you currently get the same performance penalty with const or immutable arrays as with enum: http://d.puremagic.com/issues/show_bug.cgi?id=4298

/me looks through all his D code for any enum/immutable/const arrays... Even once it gets fixed for immutable/const, I hope it still gets fixed for enum, too. It'd be a real pain to have to think about "enum or immutable?" every time I need a fixed named value. And I can imagine that also causing problems for metaprogramming: template foo(T, alias val) { // Oops!! Sometimes needs to be immutable! // Gotta split this into two template overloads... enum T foo = val; } My understanding was that the point of enum (the manifest constant enum) was to serve as *the* de facto way to do fixed named values because of a particular limitation of trying to do the same with immutable.
Apr 05 2011
parent reply Don <nospam nospam.com> writes:
Nick Sabalausky wrote:
 "Rainer Schuetze" <r.sagitario gmx.de> wrote in message 
 news:ind21j$1si5$1 digitalmars.com...
 Don wrote:
 Jonathan M Davis wrote:
 Enum values cannot be altered. It is an error to try and assign a value 
 to an enum. However, the value of an enum isn't really const or 
 immutable. It's copied every time that the enum is used. This is fine in 
 cases where the enum is a value type, but it's problematic when the enum 
 is a reference type. I believe that the classic example is if you have 
 an AA which is an enum. You copy _the entire_ AA every time it's 
 referenced. But it's not just AA's. Normal arrays have the problem too.

 Given that the value of enum must be known at compile time and given 
 that it cannot be changed, why aren't enums all immutable? What's gained 
 by making so that every reference to an enum is replaced by its value 
 rather than actually referencing an immutable value? In most cases, it 
 could still be replaced by the value (since it's a constant) if that's 
 more efficient. And in the case of reference types, it would actually 
 act like a reference type.

 So, I ask, should enums just all be made automatically immutable instead 
 of having the current replace when referenced semantics? Is there a good 
 reason _not_ to make all enums immutable?

they don't exist at run time. They have no address. Any case where taking a reference to them works, is a compiler bug. Almost all existing enum arrays or AAs are bugs. For example, if you have a lookup table which should be used at runtime, it should ALWAYS be defined as const or immutable, not as an enum.

or immutable arrays as with enum: http://d.puremagic.com/issues/show_bug.cgi?id=4298

/me looks through all his D code for any enum/immutable/const arrays... Even once it gets fixed for immutable/const, I hope it still gets fixed for enum, too. It'd be a real pain to have to think about "enum or immutable?" every time I need a fixed named value. And I can imagine that also causing problems for metaprogramming: template foo(T, alias val) { // Oops!! Sometimes needs to be immutable! // Gotta split this into two template overloads... enum T foo = val; } My understanding was that the point of enum (the manifest constant enum) was to serve as *the* de facto way to do fixed named values because of a particular limitation of trying to do the same with immutable.

No. It's just a workaround for an optlink bug: manifest constants that are never used, still get put into the executable. By default, you should use const, not enum. You should forget that enum manifest constants even exist.
Apr 06 2011
parent Don <nospam nospam.com> writes:
Trass3r wrote:
 Am 07.04.2011, 18:03 Uhr, schrieb Simen kjaeraas <simen.kjaras gmail.com>:
 
 On Wed, 06 Apr 2011 21:42:35 +0200, Trass3r <un known.com> wrote:

 Am 06.04.2011, 20:40 Uhr, schrieb Simen kjaeraas 
 <simen.kjaras gmail.com>:
 Yup. In theory, any immutable Foo declared at module scope (and likely
 also those at static class/struct scope) could be elided by the linker
 if it not used anywhere, but Optlink does not do that.

Ok, I guess the hack works by replacing every occurrence of the constant identifier in the AST with the initializer part of "enum foo = initializer;"? So there would be no reason to keep enum manifest constants if this Optlink bug was fixed?

Correct on both counts.

I'm still confused about this. Don't we need enum to have things like PI not consume memory?

No, the only thing it does it make it not take up space in the executable if it isn't used. And this is a linker issue.
 Which directly brings me to the following:
 http://www.digitalmars.com/d/2.0/enum.html states that enum ident = val; 
 is just shorthand for enum {ident = val}.
 Yet it suggests these are different things by calling only these special 
 cases "manifest constants" and stating that SUCH declarations are not 
 lvalues. Even though normal enums aren't lvalues either:
 
 enum {a = 5, b}
 pragma(msg, &b);
 Error: constant 6 is not an lvalue
 
 And also dmd handles them specially by treating enum as a storage class 
 (STC_manifest) in this case.

The terminology stuff is just because they're not enums. There is nothing about them which would justify them being called an "enumerated type". But that's true of anonymous enums, enum { ident = val } as well.
Apr 08 2011
prev sibling parent "Nick Sabalausky" <a a.a> writes:
"Rainer Schuetze" <r.sagitario gmx.de> wrote in message 
news:ind21j$1si5$1 digitalmars.com...
 Don wrote:
 Jonathan M Davis wrote:
 Enum values cannot be altered. It is an error to try and assign a value 
 to an enum. However, the value of an enum isn't really const or 
 immutable. It's copied every time that the enum is used. This is fine in 
 cases where the enum is a value type, but it's problematic when the enum 
 is a reference type. I believe that the classic example is if you have 
 an AA which is an enum. You copy _the entire_ AA every time it's 
 referenced. But it's not just AA's. Normal arrays have the problem too.

 Given that the value of enum must be known at compile time and given 
 that it cannot be changed, why aren't enums all immutable? What's gained 
 by making so that every reference to an enum is replaced by its value 
 rather than actually referencing an immutable value? In most cases, it 
 could still be replaced by the value (since it's a constant) if that's 
 more efficient. And in the case of reference types, it would actually 
 act like a reference type.

 So, I ask, should enums just all be made automatically immutable instead 
 of having the current replace when referenced semantics? Is there a good 
 reason _not_ to make all enums immutable?

Yes. The ONLY reason those manifest constants exist at all, is so that they don't exist at run time. They have no address. Any case where taking a reference to them works, is a compiler bug. Almost all existing enum arrays or AAs are bugs. For example, if you have a lookup table which should be used at runtime, it should ALWAYS be defined as const or immutable, not as an enum.

Unfortunately, you currently get the same performance penalty with const or immutable arrays as with enum: http://d.puremagic.com/issues/show_bug.cgi?id=4298

What about "enum string x = ..." or "immutable string x = ..."? Anyone know if those exhibit the same problem?
Apr 05 2011
prev sibling parent reply Stewart Gordon <smjg_1998 yahoo.com> writes:
On 04/04/2011 10:32, Don wrote:
<snip>
 Yes. The ONLY reason those manifest constants exist at all, is so that they
don't exist at
 run time. They have no address. Any case where taking a reference to them
works, is a
 compiler bug.

Eh what? In the case of simple value-type constants, this makes sense. But when they're reference types, many copies exist at run time, rather than only one. This is certainly not an advantage on any level. A reasonable way of doing it is such that every instance of the same enum value has the same bit pattern. For pure value types (primitive types, static arrays and structs/unions consisting only of pure value types) this is straightforward. If a reference type is involved, put the contents in the static data segment and have the enum value referencing this one instance. I presume this can be done...? Stewart.
Apr 05 2011
parent reply Don <nospam nospam.com> writes:
Stewart Gordon wrote:
 On 04/04/2011 10:32, Don wrote:
 <snip>
 Yes. The ONLY reason those manifest constants exist at all, is so that 
 they don't exist at
 run time. They have no address. Any case where taking a reference to 
 them works, is a
 compiler bug.

Eh what? In the case of simple value-type constants, this makes sense. But when they're reference types, many copies exist at run time, rather than only one. This is certainly not an advantage on any level.

No. NONE exist at run time. That is the whole point. No enum should ever exist in the compiler. That's the only difference between immutable and enum.
 A reasonable way of doing it is such that every instance of the same 
 enum value has the same bit pattern.  For pure value types (primitive 
 types, static arrays and structs/unions consisting only of pure value 
 types) this is straightforward.  If a reference type is involved, put 
 the contents in the static data segment and have the enum value 
 referencing this one instance.  I presume this can be done...?
 
 Stewart.

Apr 06 2011
parent reply Stewart Gordon <smjg_1998 yahoo.com> writes:
On 06/04/2011 16:24, Don wrote:
<snip>
 No. NONE exist at run time. That is the whole point. No enum should ever exist
in the
 compiler. That's the only difference between immutable and enum.

So how, exactly, does the runtime get at data that doesn't exist? Stewart.
Apr 09 2011
parent Stewart Gordon <smjg_1998 yahoo.com> writes:
On 09/04/2011 22:58, Jonathan M Davis wrote:
<snip>
 So how, exactly, does the runtime get at data that doesn't exist?

Every time that you use an enum, it's replaced with the enum's value. So, it's like you put a literal there which was identtical to the enum's value. So,

That's what I'd made out people were saying. But Don seemed to be claiming that the [1, 2, 3, 4] doesn't exist at runtime.
 If a had been an immutable variable, then a would not have been replaced with
 its value in the writeln call, but rather a would be passed to writeln.

Exactly. What I'm claiming ought to happen is for a to be replaced at compiletime with the (length, address) tuple pointing to [1, 2, 3, 4]. Am I imagining it, or is this basically the way string literals work? Stewart.
Apr 09 2011
prev sibling next sibling parent Stewart Gordon <smjg_1998 yahoo.com> writes:
On 04/04/2011 10:07, Jonathan M Davis wrote:
 Enum values cannot be altered. It is an error to try and assign a value to an
 enum. However, the value of an enum isn't really const or immutable. It's
 copied every time that the enum is used. This is fine in cases where the enum
 is a value type, but it's problematic when the enum is a reference type. I
 believe that the classic example is if you have an AA which is an enum. You
 copy _the entire_ AA every time it's referenced. But it's not just AA's.
 Normal arrays have the problem too.

There are a few bug reports about this: http://d.puremagic.com/issues/show_bug.cgi?id=2237 http://d.puremagic.com/issues/show_bug.cgi?id=2331 Stewart.
Apr 05 2011
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 05 Apr 2011 19:53:35 -0400, Nick Sabalausky <a a.a> wrote:


 What about "enum string x = ..." or "immutable string x = ..."? Anyone  
 know
 if those exhibit the same problem?

No, strings are generated at compile time via a literal or CTFE. -Steve
Apr 05 2011
prev sibling next sibling parent Trass3r <un known.com> writes:
Am 06.04.2011, 17:27 Uhr, schrieb Don <nospam nospam.com>:
 No. It's just a workaround for an optlink bug: manifest constants that  
 are never used, still get put into the executable.

What?
 By default, you should use const, not enum. You should forget that enum  
 manifest constants even exist.

Don't you mean immutable in this case?
Apr 06 2011
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
On Wed, 06 Apr 2011 18:30:43 +0200, Trass3r <un known.com> wrote:

 Am 06.04.2011, 17:27 Uhr, schrieb Don <nospam nospam.com>:
 No. It's just a workaround for an optlink bug: manifest constants that  
 are never used, still get put into the executable.

What?

Yup. In theory, any immutable Foo declared at module scope (and likely also those at static class/struct scope) could be elided by the linker if it not used anywhere, but Optlink does not do that. That means of course, that pointer manipulation to get from one manifest constant to another declared right after is not supported (nor should it be *shudder*). -- Simen
Apr 06 2011
prev sibling next sibling parent Trass3r <un known.com> writes:
Am 06.04.2011, 20:40 Uhr, schrieb Simen kjaeraas <simen.kjaras gmail.com>:
 Yup. In theory, any immutable Foo declared at module scope (and likely
 also those at static class/struct scope) could be elided by the linker
 if it not used anywhere, but Optlink does not do that.

Ok, I guess the hack works by replacing every occurrence of the constant identifier in the AST with the initializer part of "enum foo = initializer;"? So there would be no reason to keep enum manifest constants if this Optlink bug was fixed?
Apr 06 2011
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
On Wed, 06 Apr 2011 21:42:35 +0200, Trass3r <un known.com> wrote:

 Am 06.04.2011, 20:40 Uhr, schrieb Simen kjaeraas  
 <simen.kjaras gmail.com>:
 Yup. In theory, any immutable Foo declared at module scope (and likely
 also those at static class/struct scope) could be elided by the linker
 if it not used anywhere, but Optlink does not do that.

Ok, I guess the hack works by replacing every occurrence of the constant identifier in the AST with the initializer part of "enum foo = initializer;"? So there would be no reason to keep enum manifest constants if this Optlink bug was fixed?

Correct on both counts. -- Simen
Apr 07 2011
prev sibling next sibling parent Trass3r <un known.com> writes:
Am 07.04.2011, 18:03 Uhr, schrieb Simen kjaeraas <simen.kjaras gmail.com>:

 On Wed, 06 Apr 2011 21:42:35 +0200, Trass3r <un known.com> wrote:

 Am 06.04.2011, 20:40 Uhr, schrieb Simen kjaeraas  
 <simen.kjaras gmail.com>:
 Yup. In theory, any immutable Foo declared at module scope (and likely
 also those at static class/struct scope) could be elided by the linker
 if it not used anywhere, but Optlink does not do that.

Ok, I guess the hack works by replacing every occurrence of the constant identifier in the AST with the initializer part of "enum foo = initializer;"? So there would be no reason to keep enum manifest constants if this Optlink bug was fixed?

Correct on both counts.

I'm still confused about this. Don't we need enum to have things like PI not consume memory? Which directly brings me to the following: http://www.digitalmars.com/d/2.0/enum.html states that enum ident = val; is just shorthand for enum {ident = val}. Yet it suggests these are different things by calling only these special cases "manifest constants" and stating that SUCH declarations are not lvalues. Even though normal enums aren't lvalues either: enum {a = 5, b} pragma(msg, &b); Error: constant 6 is not an lvalue And also dmd handles them specially by treating enum as a storage class (STC_manifest) in this case.
Apr 07 2011
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
 On 06/04/2011 16:24, Don wrote:
 <snip>
 
 No. NONE exist at run time. That is the whole point. No enum should ever
 exist in the compiler. That's the only difference between immutable and
 enum.

<snip> So how, exactly, does the runtime get at data that doesn't exist?

Every time that you use an enum, it's replaced with the enum's value. So, it's like you put a literal there which was identtical to the enum's value. So, enum a = [1, 2, 3, 4]; void main() { writeln(a); } would become void main() { writeln([1, 2, 3, 4]); } If a had been an immutable variable, then a would not have been replaced with its value in the writeln call, but rather a would be passed to writeln. - Jonathan M Davis
Apr 09 2011