Memory Safety without a GC or Ref Counting

F i L witte2008 at gmail.com
Mon Jan 21 09:37:36 PST 2013


So I've been thinking about this problem off and on for awhile, 
and I think I have a rather simple solution. I highly doubt I'm 
the first to think of it, but haven't found any info about a 
similar idea on the net. So in the interest of science I'm 
posting it here so anyone interested can attempt to poke holes in 
the idea, which you folks have been so good at in the past.

This isn't really D related, but I usually like the feedback I 
get from this community, plus, if there's any value in the idea, 
maybe it would be a worth while discussion for future directions 
D might make use of.

I'll be illustrating with Pseudo-code. The basic idea spawns from 
conceptually separating variables as either "memory owners" or 
"memory references". Basically, the pseudo-code has three types:

var - Memory Owner, never null, deleted at end of scope**
ref - Memory Reference, can't "own" memory
ptr - Unsafe manually managed memory pointer (like C pointers)

So, 'ptr's aside for now, 'var's would be the only way to 
allocate memory, and memory is automatically allocated to them at 
their definition**. Vars would be deleted at the end of their 
scope (or when they're removed from dynamic-arrays, etc) unless 
they're returned, in which case they're "injected" into the 
callers scope, and deleted at the end of that scope. Without too 
much lengthy explanation, here's some code:

     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

         var n = 6

         b => n
         b = 7     # n = 7

         # No clean-up code needed, 'n' is stack-allocated
         #
         # Also, notice we didn't need to set 'b' to 'null' at
         # the end of the scope, since it wasn't accessed
         # directly after the end of the scope.
     }

So this is sort of like ref-counted scopes (smart scopes), except 
the compiler has a higher degree of guarantee on the lifetime of 
memory, and there's a lot of static analysis that can happen to 
optimize the clean up code (like how we didn't need to assign 'b' 
to null in the example above). All "ref counting" would be done 
through static-analysis at compile-time, and injected by the 
compiler, so it wouldn't have the runtime overhead of 
ref-counting, nor would there be a need for "weak references" 
since references are guaranteed to never "own" memory.

There's more to it of course. For instance, in order to have 
expanding memory, you'd need the concepts of a dynamic-array and 
type inheritance. You'd also need to not clean up memory that was 
returned... so...

     type Foo
     {
         var name : text
         init { name = "Foo" }
         func write { Console.write(name) }
     }

     type Bar : Foo
     {
         init { name = "Bar" }
     }

     ref global_foo : Foo

     func main
     {
         var foos = Foo[]

         foos += Foo     # allocation
         foos += Foo     # allocation
         foos += Bar     # allocation
         foos[].write()  # prints: 'Foo', 'Foo', 'Bar'

         foos -= foos[1] # deallocation
         foos[].write()  # prints: 'Foo', 'Bar'

         # ---

         func getFoo
         {
             var f = Foo
             f.name = "Fip"
             return f
             # no cleanup, since 'f' is returned
         }

         global_foo = getFoo()
         global_foo.write() # prints: 'Fip'

         # auto cleanup:
         #
         #    Memory.delete(foos)
         #    Memory.delete(global_foo)
         #    global_foo => null
         #
     }

Okay, so with that you could have global var arrays, and assign 
global refs to function return values, etc. If you need extra 
control, there's also 'ptr' types, which would require manual 
clean-up. However, 'ptr's would be automatically cleaned when the 
program exist, so they would be safe to use for global references 
which only sometimes needed to point to usable memory.

Anyways.. comments and criticisms would be great. Sorry this 
isn't more D related. Like I said, I hope it does have value D 
might make use of at some point.


More information about the Digitalmars-d mailing list