Head Const

Jonathan M Davis via Digitalmars-d digitalmars-d at puremagic.com
Tue Feb 16 02:09:19 PST 2016


On Tuesday, 16 February 2016 at 09:45:34 UTC, ZombineDev wrote:
> On Tuesday, 16 February 2016 at 08:03:31 UTC, Jonathan M Davis 
> wrote:
>> ...
>> Given that very few optimizations can be made based solely on 
>> const (and I'm not sure that the compiler currently does any 
>> of them) ...
>
> I think that the fact that we have shared can help a lot in the 
> area of optimization.
> Taking a non-shared const parameter should be as good as taking 
> an immutable one (if the function pure-like - only does 
> computation based on the parameter and doesn't escape it).
> Or am I missing something?

The fact that variables are thread-local by default in D 
definitely helps, but it's still far from sufficient. If you have

     auto result = func(foo);

where foo is const, for the compiler to know that foo was not 
mutated by the call to func, it has to know that there's no way 
that func could have accessed a mutable reference to the object 
that foo refers to. The fact that we have shared means that it 
doesn't have to worry about other, arbitrary code mutating foo 
while func is called, but it still has to be able to determine 
that neither func no anything that it accesses is able to mutate 
foo.

If func is pure, that gets us a good chunk of the way, because 
then the only way that func can access a mutable reference to foo 
is via its arguments. If it's just foo, then - unless I'm missing 
something - that should be enough, since foo would not be able to 
access a mutable reference to itself except via a non-pure 
function, which wouldn't be callable from func if it's pure. But 
if func is a member function, or if it took addition, mutable 
arguments, e.g.

     auto result = func(foo, bar);

then the compiler has to know that func can't access a mutable 
foo via the other arguments - and if they're not const, that's 
hard to do.

In general, I think that the compiler pretty much has to be able 
to see where foo was created and that no mutable references _can_ 
exist that func would have access to. e.g.

     const foo = new Foo(42);
     auto result = func(foo, bar);

or

     auto mutableFoo = new Foo(42);
     const foo = mutableFoo;
     auto result = func(foo, bar);

would be enough. But without a lot of code flow analysis, it 
doesn't take much for it to be too much for the compiler to 
figure out, and unless it has access to the bodies of the 
functions that are being called and does deep inspection of what 
they do and call, simply calling a function (especially a 
non-pure one) very quickly makes it so that the compiler doesn't 
know enough to guarantee that func can't possibly access foo via 
a mutable reference to the same data. e.g. in

     auto mutableFoo = new Foo(42);
     auto result1 = otherFunc(mutableFoo, bar);
     const foo = mutableFoo;
     auto result2 = func(foo, bar);

otherFunc might have given bar access to mutableFoo which it then 
accessed inside of func.

So, yes, the combination of non-shared, pure, and const does 
allow for some optimizations, but it really doesn't take much to 
make it so that the compiler can't guarantee that const object 
hasn't been changed by a function call without it doing a lot of 
deep analysis that compilers just don't normally do.

- Jonathan M Davis


More information about the Digitalmars-d mailing list