Hello everyone,
Lately, I've been looking at the __metadata/__mutable discussions. For reference, here is the material I have so far:
-
Forum discussion: https://forum.dlang.org/thread/3f1f9263-88d8-fbf7-a8c5-b3a2a5224ce0@erdani.org?page=1
-
Timon's original DIP: https://github.com/RazvanN7/DIPs/blob/Mutable_Dip/DIPs/timon_dip.md
-
My updated DIP which is based on Timon's: https://github.com/RazvanN7/DIPs/blob/Mutable_Dip/DIPs/DIP1xxx-rn.md
We need this to be able to implement generic reference counting. So our main
problem is how do we reference count immutable/const objects. Timon's original
proposal tried to implement __metadata in a way that does not affect purity
based optimizations. I think that this could be done:
- A strongly pure function (that return types without indirections) will return the same result when applied to the same immutable arguments.
Fixed by rewritting
auto a = foo(arg) // foo -> strongly pure
aubo b = foo(arg)
to
auto a = foo(arg)
auto b = a
This is taken from Timon DIP.
- The set of references returned from strongly pure functions can be safely converted to immutable or shared.
This is no affected by the introduction of __metadata.
- A strongly pure function whose result is not used may be safely elided.
struct S{
private int __metadata x;
}
void foo(immutable ref S s)pure{
s.x += 1;
}
void main(){
immutable S s;
foo(s); // there is no reason for this call to happen
assert(s.x==1); // can't rely on this, it might also be 0
}
Essentially, if foo
is strongly pure, then the compiler can optimize away
the call to it and your reference count is blown away. If we look at it this
way, the problem seems to be unsolvable. However, the idea of __metadata is to
be used solely by library developers and even they should take extra care.
As such, I would propose that __metadata
can only be accessed from inside
the aggregate that defines it (private
here means Java private
) and methods
that access __metadata directly need to also private. I think that this makes sense since the reference is updated only when you call the copy constructor and the assignment operator. These methods should be public and they can call the incRef, decRef that are mandatory private. This way, it becomes impossible to access a __metadata
field without going through the object methods. This makes sense, since the object is the only one that should manage the __metadata
.
Now, if we do it like this, then foo
will not have any means of accessing x
apart from assigning s, passing s to a function or copy constructing from s.
However, whatever happens to s, once the execution of foo
is over, the reference
count at the call site is going to be the same as when foo
was called. Why?
Because there is no way you can escape any reference to s outside of a strongly pure function (other than returning it, but that case is taken care of at point 1.).
- If we have two subsequent pure function invocations foo(args1...) and bar(args2...) where data transitively reachable from args1 and args2 only overlaps in immutable and const data (this includes data accessed through __mutable fields of const and immutable objects), the two invocations may safely swap their order (of course, this only applies if none of the two functions takes arguments)
This should be un-affected.
- A strongly pure function invocation can always exchange order with an adjacent impure function invocation.
This should be un-affected.
What do you think? Am I missing anything? If you think this could fly, I could update the DIP and submit it.
Best regards,
RazvanN