Memory Safety without a GC or Ref Counting

F i L witte2008 at gmail.com
Fri Jan 25 04:05:40 PST 2013


On Friday, 25 January 2013 at 11:28:16 UTC, TommiT wrote:
> On Monday, 21 January 2013 at 17:37:38 UTC, F i L wrote:
>>    type Foo
>>    {
>>        var bar = 0
>>    }
>>
>>    func main
>>    {
>>        ref f : Foo
>>        ref b : int
>>
>>        scope
>>        {
>>            var foo = Foo # allocates Foo
>>
>>            f => foo      # f points to foo
>>            f.bar = 1     # foo.bar = 1
>>
>>            b => f.bar    # b points to foo.bar
>>            b = 3         # foo.bar = 3
>>
>>            # auto 'clean up code' at end of scope:
>>            #
>>            #     Memory.delete(foo)
>>            #     f => null
>>            #
>>        }
>>
>>        f.bar = 4 # ERROR: f is null
>
> I think there should be also b => null in the implicit 'auto 
> clean up' phase.

b didn't need to be assigned to null because the compiler could 
determine it wasn't being directly accessed after that scope (it 
was assigned to a new var first). It was intentionally left out 
to illustrate how the compiler could optimize special cases like 
that. I wrote that in my original post a bit after that code.


> I think it's good to note whether errors are compilation errors 
> or runtime errors. I assume f.bar = 4 in the end there would be 
> a compilation error?

If the compiler could determine that a ref was being used 
directly after being cleaned up, then yes, the compiler could 
throw an error. But that can't happen in every case, so when it 
can't you'll get a runtime error. Sorry I wasn't clear about that.


> On Friday, 25 January 2013 at 02:12:56 UTC, F i L wrote:
>>    var nums = int[] # DynamicArray(int)
>>    ref a, b : int
>>
>>    func main
>>    {
>>        nums += 1 # allocate
>>        nums += 2 # allocate
>>        nums += 3 # allocate
>>
>>        a => nums[2] # nums.=>(2, a)
>>        b => a       # nums.=>(2, b)
>
> What if we now (after the code snippet above) write:
>
> nums += 4
>
> And nums has run out of room in its place in the heap and needs 
> to re-allocate its data (4 ints now). I guess that would mean 
> that the call += should nullify all refs pointing to nums (a 
> and b). If we now write:
>
> a = -3
>
> ...that can't be a compilation error. Because it's not known at 
> compile-time whether or not nums += 4 forces a re-allocation. 
> Wouldn't this mean then, that all accesses through references 
> would require a runtime penalty for checking whether the ref is 
> null or not?

The specific code you're talking about is a demonstration of an 
area where the compiler can't (like you said) determine at 
compile-time the clean-up code, and therefor has to use a runtime 
approach to nullifying references.

The issue you raise isn't really a problem though. If the memory 
gets rearranged then the runtime could inform the DynamicArray, 
and it, in turn, could reassign those refs to the new memory 
address.

The idea here is that if you're writing something complex like a 
Container, you may need to use the unmanaged 'ptr' type to get 
the job done. However, such containers could still make safety 
guarantees to their users. So if the standard libs provided 
enough basic building blocks, and the compiler supported advanced 
meta-programming features (like in D), then programmers would 
rarely, if ever, need to touch a 'ptr' to make applications.


More information about the Digitalmars-d mailing list