Allocator-aware @safe reference counting is still not possible

Timon Gehr timon.gehr at gmx.ch
Mon Jan 30 23:14:57 UTC 2023


On 1/30/23 18:36, Paul Backus wrote:
> 
> As far as I am aware, it is impossible to have all three of the following:
> 
> 1. @safe containers.
> 2. User-supplied allocators.
> 3. No language changes.

Well, we have @system variables now, so we can have poor man's typestate 
together with poor man's move semantics [1], like already proposed by 
ntrel and Dukc.

Why is this scheme not workable? Isn't this exactly the kind of problem 
(non-trivial memory safety invariant) we invented `@system` variables to 
solve?

(With sumtype, I guess you can even move the flags to runtime (at the 
cost of template bloat exponential in the number of flags) to get poor 
man's dependent type state.)

[1]:
import core.stdc.stdlib;


void main(){
     import std.stdio;
     void foo()@safe{
         auto ptr0=fancyMalloc(16);
         writeln(ptr0.borrow((scope ptr){
             return cast(int)ptr;
         }));
         fancyFree(ptr0);
     }
     foo();
     void bar()@safe{
         auto ptr0=fancyMalloc(16);
         auto ptr1=ptr0.withAliasing.leak;
         // ok, leaking is safe
         writeln(ptr1);
     }
     bar();
     void baz()@safe{
         auto ptr0=fancyMalloc(16);
         auto ptr1=ptr0.withAliasing;
         // fancyFree(ptr1); // error, not isolated
     }
     baz();
     void qux()@safe{
         auto ptr0=fancyMalloc(16);
         auto ptr1=ptr0.withAliasing;
         // auto ptr2=ptr1.unsafeAddFlags!(PointerFlags.isolated); // 
error, unsafe
         // fancyFree(ptr2); // (ok)
     }
     qux();
     void flarp()@trusted{
         auto ptr0=fancyMalloc(16);
         auto ptr1=ptr0.withAliasing;
         auto ptr2=ptr1.unsafeAddFlags!(PointerFlags.isolated); // ok, 
and we can check it is fine
         fancyFree(ptr2); // (ok)
     }
     flarp();
     void bongo()@safe{
         auto ptr1=function()@trusted{
             auto ptr0=malloc(16);
             return 
ptr0.unsafeAddFlags!(PointerFlags.mallocd|PointerFlags.isolated); // ok, 
we can tell it is mallocd and isolated
         }();
         fancyFree(ptr1); // ok
     }
     bongo();
}

enum PointerFlags{
     none,
     mallocd=1,
     isolated=2,
}

struct Pointer(T,PointerFlags flags){
     private @system T* ptr;

     Pointer!(T,flags&~PointerFlags.isolated) withAliasing()@trusted{
         auto result=ptr;
         ptr=null;
         return typeof(return)(result);
     }
     static if(!(flags&PointerFlags.isolated)){
         T* leak()@trusted{
             auto result=ptr;
             ptr=null;
             return result;
         }
     }
     auto borrow(R)(scope R delegate(scope T*)@safe dg)@trusted{
         scope local=ptr;
         ptr=null;
         scope(exit) ptr=local;
         return dg(local);
     }
     auto borrow(R)(scope R delegate(scope T*)@system dg)@system{
         scope local=ptr;
         ptr=null;
         scope(exit) ptr=local;
         return dg(ptr);
     }
}
Pointer!(T,flags) unsafeAddFlags(PointerFlags flags,T)(ref T* ptr)@system{
     auto result=ptr;
     ptr=null;
     return typeof(return)(result);
}
Pointer!(T,newFlags|oldFlags) unsafeAddFlags(PointerFlags 
newFlags,T,PointerFlags oldFlags)(ref Pointer!(T,oldFlags) ptr)@system{
     auto result=ptr.ptr;
     ptr.ptr=null;
     return unsafeAddFlags!(newFlags|oldFlags)(result);
}

Pointer!(void, PointerFlags.mallocd|PointerFlags.isolated) 
fancyMalloc(size_t size)@trusted{
     return typeof(return)(malloc(size));
}
void fancyFree(ref Pointer!(void, 
PointerFlags.mallocd|PointerFlags.isolated) ptr)@trusted{
     if(!ptr.ptr) return;
     free(ptr.ptr);
     ptr.ptr=null;
}




More information about the Digitalmars-d mailing list