www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - My solution to the GC problem

reply Menshikov <mensikovk817 gmail.com> writes:
``` D
/*
    I came up with an idea last night.
   If the storages (variables, function arguments, structure or 
class fields) store the type at compile time, why not store the 
allocator at compile time?

    That is, information about the allocator is stored neither in 
the type nor in the value, but as an attribute of the store.

    * First, it introduces new interesting idioms ('new void', 
'new __stack', for example).
    * Secondly, if possible, it does not generate a bunch of 
template code and does not take up memory in values
    * Third, it's readable
    * And the fattest plus is that in order to rewrite std and 
runtime for them,
   you just need to add the new attribute to the function 
arguments,
   and in half the possible cases, it will work!
    * I haven't seen it, in any language! Allocators are well 
developed in beeflang, but it does not store the allocator in 
storage, it is on the shoulders of the user.

    What do you think of it?
*/

/***SYNTAX***/

newAttribute:
   new aliasValue
   new extern aliasValue
   /*only in funcs*/
     new
     new: identifier


//alias aliasValue is either what alias can accept, or reserved 
allocators (__GC, __stack), or the void keyword



/***VARIABLES***/

/*new*/{
   string text new myAlloc;

   /*
     same as
     scope string text new extern myAlloc; // 'extern' need to 
don't generate scope(exit) and scope attr
     scope(exit) destroy(text);
   */

   text.length = 90; // ok

   string text2 new myAlloc;
   text2 = text; // error!
   text2 = new string(text); //ok
   text2 = move(text); //ok
}

/*new extern*/{
   string text new extern myAlloc;
   string textGC = text; //error: __GC != myAlloc
   /*
     string textGC new extern __GC;
   */

   string text2 new extern myAlloc;
   text2 = text; // OK!

   text2.length = text2.length + 5;
   assert(text2.ptr != text1.ptr);

   destroy(text2);
   destroy(text1);
}

/***STRUCTS***/

{
   struct Person{
     string name;
     int age;
   }

   Person likesGC;
   Preson hatesGC new myAlloc;

   //pseudocode
   static assert(__traits(isSame, allocof(hatesGC), 
allocof(hatesGC.name)));
   static assert(__traits(isSame, allocof(likesGC), 
allocof(likesGC.name)));
}

{
   struct Sample{
     IAllocator alloc;
     string text new alloc; // why not?
   }

   Sample instance;
}


{
   struct Sample{
     void method() {
       string text new allocof(this);
       pragma(msg, allocof(this)); //ERR: __alloc - rt function 
argument
     }
   }

   Sample sample new myAlloc;
   sample.method();
}

{
   struct Sample{
     void method() new thisAlloc{
       string text new thisAlloc;
       pragma(msg, thisAlloc); //OK
     }
   }

   Sample sample new myAlloc;
   sample.method();
}

/***FUNCTIONS***/

/*
   no additional code is generated, the only thing that happens is 
type checking
*/

void someAlloc(ref char[] name new){
   name.length = 4;
   name[] = "jack";

   pragma(msg, allocof(name)); // error! unknown in ct
}

void noAlloc(char[] new void){
   assert(name.length == 4);
   name[] = "jack";
}


//'new void' means that the type allacator is not known, and that 
the methods of this structure that require the structure's 
allacator cannot be called

void noAllocStruct(Person a new void){
   a.age = 0; //ok
   a.name = "Walter"; ///error: allocation
}

void multiArgs(string a new, string b new);

//something like a label is created and the caller checks the 
storage allocators
string new a1 multiArgsSameAllocator(string a new: a1, string b 
new: a1){
   string text new a1;
   pragma(msg, a1);//ERR a1 - AllacatorI
   pragma(msg, typeof(a1));//=> AllacatorI
   return a ~ b;
}

//known allocator at CT
//code generation!

void sample(alias Alloc)(string text new Alloc)
{
   pragma(msg, allocof(text)); //OK
}

//ABI

void fu_one(string a new, int n)
->
void fu_one(string a, int n, IAllocator __a_alloc)

void fu_multi(string a new, string b new, int n)
->
void fu_multi(string a, string b, int n, ushort __a_id, ushort 
__b_id, IAllocator[] __allocs...)


/***BONUS***/

{
   string dtext;
   char[] ctext new __stack = dtext.toStringz!__stack; //kinda 
alloca in c library
   ctext ~= "boo"; // error

   ubyte[] result = someCShit(ctext)[0..length];
   //PROFIT, no heap use
}
```
Jan 06 2023
next sibling parent "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
The problem isn't syntax.

Its semantics, lifetime analysis.

Being able to specify memory allocators has long been desired, but 
getting it actually working... that's the hard part. A large part of it 
is indirection issues in memory lifetime. Currently not a solved problem 
for us.
Jan 06 2023
prev sibling parent IGotD- <nise nise.com> writes:
On Saturday, 7 January 2023 at 00:18:31 UTC, Menshikov wrote:
   string text2 new myAlloc;
   text2 = text; // error!
   text2 = new string(text); //ok
   text2 = move(text); //ok
Ideas how to get of the dlang GC jail is always welcome. One question is how is the type information going to be stored when you use "string text2 new myAlloc" for example. What is the type of 'text2'? Since you want to prevent assignment of a type that is allocated differently that information must be stored somehow. BTW. Beef language looks interesting, haven't paid attention to it previously. Worth checking out.
Jan 07 2023