logical const is a subset of transitive const
Steven Schveighoffer
schveiguy at yahoo.com
Fri Sep 14 21:00:27 PDT 2007
"Bill Baxter" wrote
> Steven Schveighoffer wrote:
>>
>> IAANACW (I am also not a compiler writer).
>>
>> You are wrong in your assumption. Even in a single thread, I can
>> circumvent the constness by having a global non-const pointer to my
>> object, and using that object. Very bad implementation, but it would not
>> break the rules. For example:
>>
>> X mutableinstance;
>>
>> class X
>> {
>> int bar;
>> }
>>
>> func(const X myref)
>> {
>> mutableinstance.bar++; // I can do this because I'm not changing myref
>> }
>>
>> myfoolishfunc()
>> {
>> X foo = new X;
>> mutableinstance = foo;
>> foo.bar = 10;
>> func(foo); // increments foo.bar because mutableinstance is foo
>>
>> int biff = foo.bar; // biff now should be 11.
>> }
>
> Yeh, ok. It's perverse, but it does show clearly that even transitive
> const is not enough to guarantee anything. But that example is
> definitely teetering on the edge of undefined behavior. It depends on
> whether you take "const X myref" to mean "this function will not modify
> the contents of myref" vs "this function will not modify myref via the
> myref handle". It might be reasonable to say that if you alias a const
> argument then you're in undefined behavior territory.
My example is perfectly defined and legal, and should behave the same way
every time. But it's not likely to be coded this way :) This kind of thing
CAN happen in real code, it's just usually that your function calls a global
function, which calls another function, which adds an object to a queue,
which uses some mutable reference to your object, and bingo (or even more
crazy stuff). The point is that you cannot guarantee thread safety in the
language without proper thread tools such as mutexes. Const/invariant is
not thread safety.
>
>> (BTW you cannot assume the register is still valid after calling func
>> because func is allowed to change registers, even if it is pure).
>
> Well, if so, that rather pokes a hole in my example. So I have no clue
> what great optimizations Walter has in mind. He should just tell us.
This is just basic assembly. Every function is free to use registers in the
processor. There are clearly defined rules as to what registers mean what
when calling a function and when returning (this is why you have all those
stupid modifiers for Microsoft Windows, like STDCALL, and CDECL, these are
defining different ways functions are called). All the other registers are
fair game. If the compiler wants to save those registers, it must push them
on the stack, then pop them after the return (which it could do as an
optimization, I suppose, it depends on what's faster, loading a constant
into a register, or pushing/popping a value from the stack).
That doesn't mean that optimizations can't be made by the compiler. In some
cases you need to declare variables volatile (does D have this concept?) to
prevent the compiler from optimizing out 2 different references to the
variable, like so:
if(x == 5)
{
y = 2;
if(x == 5 && n == 3) // x == 5 could be optimized out by the compiler
unless x is volatile.
>
>> However, in multithreaded land, it is questionable whether you can assume
>> that foo will not change even with pure functions because another thread
>> could conceivably come along and wreck foo while you are calling your
>> pure function.
>
> Isn't that "you're on your own" territory there? Sure some other thread
> could be changing foo, but if you're using that sort of threading then it
> was up to you to properly mutex protect foo. I think the purpose of pure
> is more to support constructs with more limited scope, like parallel
> foreach loops.
The purpose of pure, if I understand it correctly is to save time calling
functions that will return exactly the same value and have no impact on
other data. For example, if I have a function f() which is pure, it will
return the same value each time, so a statement like f() * f() will only
call f() once. The compiler could also call execute two different pure
functions at once through 2 cores without fear that one would interfere with
the other.
But pure does not guarantee that another thread can't change any state in
your program. That is what mutex locks are for.
-Steve
More information about the Digitalmars-d
mailing list