casting away const and then mutating
Jonathan M Davis via Digitalmars-d
digitalmars-d at puremagic.com
Thu Jul 23 20:58:04 PDT 2015
On Friday, 24 July 2015 at 03:02:30 UTC, Steven Schveighoffer
wrote:
> On 7/23/15 7:57 PM, Jonathan M Davis wrote:
>> If
>> you cast away const, it's up to you to guarantee that the data
>> being
>> referenced is not mutated, and if it is mutated, it's
>> undefined behavior.
>
> Still need a reference to the spec that says that. Note that
> the spec specifically says it's undefined to cast away and
> modify immutable, and is careful not to include const/mutable
> in that discussion.
It's come up a number of times in discussions on logical const,
including from Walter. For it to be otherwise would mean that
const is not actually physical const. I'm quite certain that the
spec is wrong in this case.
> OK, but the point is you have run an algorithm that gets a
> *piece* of x (pretend x is not just a simple int), which you
> know to be mutable because x is mutable. But you don't want the
> algorithm to mutate x.
>
> Basically, if we say this is undefined behavior, then inout is
> undefined behavior.
inout is done by the compiler. It knows that it's safe to cast
the return type to mutable (or immutable), because it knows that
the return value was either the argument that it passed in or
something constructed within the function and thus safe to cast.
The compiler knows what's going on, so it can ensure that it
doesn't violate the type system and is well-defined.
> An example of what the compiler can "start doing more than it
> does now" would be helpful. I can't see how it can do anything
> based on this.
Well, it could remove dead code. For instance, if you had
const(Foo) bar(T t)
{
const myFoo = getFoo(t);
auto value1 = pureFunc(myFoo);
auto value2 = pureFunc2(myFoo);
auto value3 = pureFunc3(value1, value2);
return myFoo;
}
All of the lines with pureFunc* could be removed outright,
because they're all pure function calls, and they can't possibly
have mutated myFoo. I wouldn't expect a lot of dead code like
that, and maybe something like that would never be implemented in
the compiler, but it could be as long as the compiler can
actually rely on const not being mutated.
But part of the problem with "start doing more than it does now"
is that that could easily depend on ideas that folks come up with
later. At some point in the future, someone might figure out how
const interacts with some other set of attributes and be able to
optimize based on that. So, if you're casting away const and
mutating, relying on no one coming up with new optimizations,
then you could be in trouble later when they do. And maybe they
won't, but we don't know.
> Specifically, I would say you can cast away const on a
> reference that you have created within your own function on a
> mutable piece of data (in other words, you control the mutable
> data), then you can mutate via the cast reference. Otherwise,
> the inout feature is invalid, and we should remove it from the
> language, because that's EXACTLY what it does.
>
> A simple example:
>
> struct Node
> {
> int val;
> Node *next;
> }
> const(Node) *find(const(Node)* n, int val)
> {
> while(n && n.val != val) n = n.next;
> return n;
> }
> Node *find(Node *n, int val)
> {
> const cn = n;
> return cast(Node *)find(cn, val);
> }
>
> Note that the mutable version of find doesn't mutate the node
> (checked by the compiler BTW), and it's signature doesn't allow
> any const optimizations -- it gets in a mutable and returns a
> mutable. This can be rewritten like this:
>
> inout(Node) *find(inout(Node)* n, int val)
> {
> while(n && n.val != val) n = n.next;
> return n;
> }
>
> But in the case of the PR in question, we can't do this,
> because we can't inout our range and have it continue to be a
> range. So we are mimicking the behavior of inout, and the
> compiler should be fine with this.
It sounds like what you really want is a tail-inout range or
somesuch, though since we can't even sort out tail-const ranges
properly at this point, I expect that tail-inout ranges are a bit
of a pipe dream.
In any case, regardless of whether what you're proposing defined
behavior or not, it'll work, because there's no way that the
compiler could do any optimizations based on const after the cast
is done, because it's only const within the function. It's when
you cast away const on something that you were given as const
that you have a real problem. e.g.
const(Foo) bar(const(Foo) foo)
{
auto f = cast(Foo)foo;
f.mutateMe();
return foo;
}
I agree that what you're asking for makes sense. If you pass in a
mutable object to a function, and you know that the const object
you get out is either the same object or a new one that is not
immutable, and no references to that object escaped the function,
then casting the return type to mutable should work, and I'm not
against that being well-defined, but as I understand it, it
technically isn't, because it involves casting away const and
then mutating the result. And if it is well-defined, then we'd
need clear way to describe the circumstances to separate it from
casting away const in general (even when the data itself is
actually mutable).
I'm am quite sure that it is undefined behavior to cast away
const and mutate, even if the spec doesn't say that, because it's
come up time and time again in discussions on logical const. And
in the general case, even without immutable, if it's
well-defined, then compiler can't assume that a const variable
isn't going to be mutated, even when it knows that no mutable
references could have mutated it, and it means that const really
isn't physical const anymore, because you would be free to cast
away const and mutate so long as the data wasn't immutable, thus
making const pretty meaningless as far as compiler guarantees go
(which is Walter's big beef with C++'s const). So, I don't see
how we could allow casting away const and mutating to be
well-defined aside from very specific cases like this one. But
since the spec doesn't actually seem to say anything one way or
the other (aside from with regards to immutable), I think that
Walter is going to have weigh in. Clearly, the the best that I
could do convince you otherwise would be to dig through all of
the old threads on const to find quotes from Walter.
- Jonathan M Davis
More information about the Digitalmars-d
mailing list