casting away const and then mutating

Steven Schveighoffer via Digitalmars-d digitalmars-d at puremagic.com
Fri Jul 24 07:07:48 PDT 2015


On 7/23/15 11:58 PM, Jonathan M Davis wrote:
> On Friday, 24 July 2015 at 03:02:30 UTC, Steven Schveighoffer wrote:
>> 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.

The compiler knows everything that is going on inside a function. It can 
see the cast and knows that it should execute it, and also that the 
original variable is mutable and could be the one being mutated. This 
isn't any different.

>> 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.

And my interpretation of the spec doesn't change this. You can still 
elide those calls as none of them should be casting away const and 
mutating internally.

> 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.

These kinds of "maybe someone someday can think of something" arguments 
are quite unconvincing.

>
> 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.

tail-const and tail-inout are the same problem, and will likely be 
solved at the same time. But yes, tail-inout would solve this problem 
nicely.

> 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;
> }

Right, I agree this can result in undefined behavior, and the compiler 
is free to assume foo isn't modified through this function (if it's pure).

-Steve


More information about the Digitalmars-d mailing list