On Thu, 23 May 2013 16:42:30 -0400, Artur Skawina <[email protected]>
wrote:
On 05/23/13 18:26, Steven Schveighoffer wrote:
On Thu, 23 May 2013 11:36:00 -0400, Artur Skawina <[email protected]>
wrote:
If it wasn't clear - it is about the _language_, not what some compiler
currently happens to do. Being able to mutate /initialized/ immutables
is a bad idea. IOW you should not be able to modify 'Packet.type'
above.
The immutable isn't initialized. The memory it happens to be using
before initialization happens to have the '7' bit pattern in it.
Once it is initialized, I agree it should be immutable from that point
on.
It's all about the definition. Again, I'll point out that the code that
we're
talking about here *can not* exist right now - until now the compiler
has not
allowed mutation.
compiles:
struct S
{
const int x;
this(int n)
{
x = n;
}
}
So this is about a /change/ to the language, and isn't really
related to fixing that implicit-static bug - it's just that once that
change
is made it then becomes possible to support the in-ctor re-initialization
of immutable fields. Which isn't really all that useful, but carries a
cost.
The change to the language allows something that wasn't easily allowed
before -- defining the pre-initialization bit pattern that is blitted to
the const member. Until now, the only way to have a const member that is
ctor-initialized is to make it have the default bit pattern for that type.
Hm... I just figured out something interesting. We can mimic the "new"
behavior in old compilers by creating a type that has a different default
initialization:
import std.stdio;
struct myInt
{
int x = 7;
}
struct S
{
const myInt m;
this(int n)
{
m.x = n;
}
}
void main()
{
S s;
auto s2 = S(5);
writefln("%d %d", s.m.x, s2.m.x");
}
Output on 2.061:
7 5
Keep in mind that modifying Packet.type is illegal /right now/. Even
from
a ctor or static-ctor. This does not need to change when such fields
are
no longer always implicitly static. While allowing re-initialization
of immutables from a ctor is possible, it does not really give you
much,
while weakening const. (eg by making CT evaluation of such fields
impossible).
That's an issue of where Packet.type lives. It doesn't live inside an
instance right now, in the new version it does.
If Packet.type is not given an initializer, it's inside the instance
and it (correctly IMO) can be modified inside a ctor until it is used.
These rules are perfectly consistent.
I don't see how they make CT evaluation impossible.
The old way meant that the value was statically known and could be
accessed at CT.
And it still can, as long as you *properly* declare it as static (as it
should have been).
Allowing ctors to modify the fields means that the compiler can not make
any
assumptions about the value. [1] Which affects CT and constant
folding/propagation
etc.
And it shouldn't be able to on a value that is different for every
instance, but constant for that instance. However, it can make
assumptions based on the fact that it can't change. For example:
writeln(x.constmember);
....
auto n = x.constmember + 5; // can assume constmember is the same as
before.
For example you couldn't then do this:
struct Packet(uint TY) { /*...*/immutable uint type=TY; immutable ubyte
len=PLen(TY); /*...*/ }
auto PProcess(PT)(PT* p) { static if (p.type<128) if (p.type==42)
sendp(p, p.len*4); }
static if(TY < 128) if(TY == 42) ....
Even w/o the static-if it would be much less efficient. Yes, there are
other
ways to achieve a similar effect, but they are significantly more
complicated.
No, you are using the WRONG tool for the job. If you want to make
compile-time decisions, don't use run-time constants.
The most conservative approach is to initially disallow mutation from
ctors - this
restriction can always be lifted later and doing so does not affect any
existing
program.
Treating 'const' differently from 'immutable' is also a possibility, and
could
be a future option.
It's already allowed. Disallowing it would break code.
Then there's the issue of 'immutable one=1;' being very misleading; it
certainly
would be misinterpreted often enough (by assuming that ODR applies here).
use enum for compile-time constants, or static if you need an addressable
constant. There is no loss of functionality here, only gains.
[1] BTW, I'm wondering if these changes also affect module-level
const/immutable
members... No DMD here to test, though.
Of course not, there is no changing of what is const and what is not. All
that is changing is whether a default-initialized member becomes a static
member or not. The original rules as to when a const value can be
initialized still apply.
-Steve