On 21 June 2015 at 09:31, Edmund Smith via Digitalmars-d < digitalmars-d@puremagic.com> wrote:
First post, so let's see how this goes: > > On Saturday, 20 June 2015 at 00:07:12 UTC, Andrei Alexandrescu wrote: > >> 1. Reference counting: it's mutation underneath an immutable appearance. >> For a good while I'd been uncomfortable about that, until I figured that >> this is the "systems" part of D. Other languages do use reference counting, >> but at the compiler level which allows cheating the type system. It is >> somewhat fresh to attempt a principled implementation of both reference >> counting and safe functional collections, simultaneously and at library >> level. >> >> My conclusion is that changing the reference count in an otherwise >> immutable structure is an entirely reasonable thing to want do. We need a >> way to explain the type system "even though the payload is const or >> immutable, I'll change this particular uint so please don't do any >> optimizations that would invalidate the cast". The user is responsible for >> e.g. atomic reference counting in immutable data etc. >> > > This idea makes sense with the type checker, although I'm not sure how it > fits D's current memory model. On the page detailing const and immutable ( > dlang.org/const3.html) it mentions that immutable data 'can be placed in > ROM (Read Only Memory) or in memory pages marked by the hardware as read > only', which may be problematic if an immutable's mutable reference counter > is placed in a read-only page. Would this potentially be changed? It > suggests that the compiler needs a way of telling if the struct is a > typical immutable struct or is privately-mutable, publicly-immutable. > > As for the language level, it helps to look at how an immutable value > propagates. It can be passed by value or by reference, passed between > threads without issue and its value never changes. The thread safety > strongly implies that what isn't immutable is shared, so that every field > in an immutable struct is still thread-safe. Its exposed value never > changing is also crucial IMO, to the point where I think it should be a > compiler error if the 'facade' of immutability collapses (e.g. mutable data > is public or modifies a public function's result). > > Passing copies also raises a point about the current state of 'immutable > struct S', which has a few things different to normal structs (and block > anything but POD types. Is this intentional?). > Currently, D lacks destructors and postblit constructors on immutable > structs, giving the error 'Error: immutable method SomeStruct.__postblit is > not callable using a mutable object'. Throwing an immutable qualifier makes > it no longer recognisable as a postblit constructor, so that doesn't work > either. This makes perfect sense under the current assumption that > 'immutable struct' implies it is a POD struct, but if/when under-the-bonnet > mutability goes ahead, this assumption will no longer hold (and postblit > constructors are pretty useful with refcounting). The same goes for > destructors, too. Without these, passing immutable refcounts by copy isn't > safe - the copy keeps the pointer, but doesn't increase the counter. This > leads to a potential use-after-free if the copy outlives every 'proper' > reference. > I'm not sure whether this is all necessary, however - it is possible to > just ignore 'immutable struct' and use 'alias MyStruct = > immutable(MyActualStruct);', in which case the previous paragraph can be > ignored. > > After playing around with examples of how to do proper immutable reference > counting (e.g. static opCall factory) I think the smallest change large > enough to work with would be to allow transitive immutability to not > transfer immutability to 'private shared' variables (or behave this way in > the memory model, so that casting to mutable is safe but the type system > remains entirely transitive), while simultaneously making it a compile-time > error to read or write to these variables from exposed (public) functions. > This may want to be loosened or have exceptions (debug builds etc.), > depending on other use-cases. > > 'shared' seems to be used at the moment for a few different hacks (e.g. > GDC having 'shared' and 'volatile' behave identically for a while) and > nothing particularly concrete, save some library functions and inter-thread > message passing. It also seems suitable for avoiding overeager compiler > optimisations, since the 'shared' qualifier already shows that typical > assumptions can't be made about it. Also, most existing scenarios I can > think of for a private shared variable in an immutable object are rather > contrived. Finally, it should push the use of atomic code, or at least > basic multithreading good practices. > > Shared was not well documented (nor were there any strong library support) when it was initially conceived. It's purpose has become clearer with use: http://forum.dlang.org/thread/xhlbenngkdpueqnhd...@forum.dlang.org?page=1