Re: Destructors can't be @nogc?

2021-07-25 Thread Tejas via Digitalmars-d-learn

On Friday, 23 July 2021 at 20:24:02 UTC, Jim wrote:

Hello,

I've been playing with D and trying to understand how to work 
with @nogc. I must be doing something wrong, because even 
though I tagged the destructor for my class `@nogc`, I'm 
getting the following error: `.\min.d(27): Error: "@nogc" 
function "D main" cannot call non-@nogc function 
"object.destroy!(true, TestClass).destroy`


```D
import std.stdio : printf;
import core.lifetime : emplace;
import core.stdc.stdlib : malloc, free;

class TestClass {
int x;

this(int x) @nogc {
printf("TestClass's constructor called\n");
this.x = x;
}


~this() @nogc {
printf("TestClass's destructor called\n");
}
}

@nogc void
main() {
auto size = __traits(classInstanceSize, TestClass);
auto memory = malloc(size)[0..size];
TestClass x = emplace!(TestClass)(memory, 1);

printf("TestClass.x = %d\n", x.x);

destroy(x);
free(cast(void*)x);
}

```

What is the problem here? Should I not call `destroy`? If so, 
what should I call instead?


Try using the ```scope``` storage class. You will lose the 
ability to explicitly call the destructor, but maybe it is good 
enough for your purposes.


The following works:

```d
import std.stdio : printf;
import core.lifetime : emplace;
import core.stdc.stdlib : malloc, free;

class TestClass {
int x;

this(int x) @nogc {
printf("TestClass's constructor called\n");
this.x = x;
}


~this() @nogc {
printf("TestClass's destructor called\n");
}
}

@nogc void
main() {
//auto size = __traits(classInstanceSize, TestClass);
//auto memory = malloc(size)[0..size];
scope /*notice the scope storage class*/TestClass x =  
new TestClass(1);// emplace!(TestClass)(memory, 1);


printf("TestClass.x = %d\n", x.x);

//destroy(x);
//free(cast(void*)x);
}
```

If you absolutely want to be able to explicitly call the 
destructor, then custom functions are the only option, 
unfortunately.


Re: Destructors can't be @nogc?

2021-07-24 Thread vit via Digitalmars-d-learn

On Friday, 23 July 2021 at 20:24:02 UTC, Jim wrote:

Hello,

I've been playing with D and trying to understand how to work 
with @nogc. I must be doing something wrong, because even 
though I tagged the destructor for my class `@nogc`, I'm 
getting the following error: `.\min.d(27): Error: "@nogc" 
function "D main" cannot call non-@nogc function 
"object.destroy!(true, TestClass).destroy`


```D
import std.stdio : printf;
import core.lifetime : emplace;
import core.stdc.stdlib : malloc, free;

class TestClass {
int x;

this(int x) @nogc {
printf("TestClass's constructor called\n");
this.x = x;
}


~this() @nogc {
printf("TestClass's destructor called\n");
}
}

@nogc void
main() {
auto size = __traits(classInstanceSize, TestClass);
auto memory = malloc(size)[0..size];
TestClass x = emplace!(TestClass)(memory, 1);

printf("TestClass.x = %d\n", x.x);

destroy(x);
free(cast(void*)x);
}

```

What is the problem here? Should I not call `destroy`? If so, 
what should I call instead?


You can call function like `destroy` with right attributes for 
class variable if you known exact type of the class (no base 
class referece or interface):


https://github.com/atilaneves/automem/blob/master/source/automem/utils.d


Re: Destructors can't be @nogc?

2021-07-24 Thread Mike Parker via Digitalmars-d-learn

On Sunday, 25 July 2021 at 00:15:30 UTC, Jim wrote:



But I think `destroy()` not being @nogc even though the 
destructor is tagged @nogc is a bug, and it should be fixed. 
Because it is problematic behaviour even if we limit the usage 
of @nogc.


It's not a bug. The destructor being tagged has nothing to do 
with it. destroy can call @nogc destructors just fine. It just 
can't itself be called in @nogc functions.


As Paul described, the root reason is down to rt_finalize. Were 
it marked @nogc, then you could call destroy from @nogc code, but 
you wouldn't be able to call destructors that aren't @nogc---an 
even worse situation, IMO.


I think it's worth looking into how we can make destroy (or an 
alternative) available in @nogc functions as an enhancement. It's 
possible for extern(C) functions to be templated, so that might 
be a potential path. With a templated rt_finalize (or an 
alternative) then perhaps attribute inference could kick in and 
make destroy (or an alternative) callable in both @nogc functions 
and non.





Re: Destructors can't be @nogc?

2021-07-24 Thread user1234 via Digitalmars-d-learn

On Friday, 23 July 2021 at 20:24:02 UTC, Jim wrote:

Hello,

I've been playing with D and trying to understand how to work 
with @nogc. I must be doing something wrong, because even 
though I tagged the destructor for my class `@nogc`, I'm 
getting the following error: `.\min.d(27): Error: "@nogc" 
function "D main" cannot call non-@nogc function 
"object.destroy!(true, TestClass).destroy`


[...]
What is the problem here? Should I not call `destroy`? If so,


`destroy()` uses the runtime types informations to detect the 
most derived destructor (as you might cast from `TestClass` to 
least specialized, i.e `Object`). The type infos for classes 
stores the destructor in form of a function pointer that has not 
`@nogc` as part of its attributes.



what should I call instead?


get rid of `destroy()` and instead use your own allocator, 
virtual constructor (instead of `this(){}`) and virtual 
destructor (instead of `~this(){}`) in a base class.


e.g

```d
class Base
{
static auto make(T : Base, Args...)(Args args)
{
auto size = __traits(classInstanceSize, T);
auto memory = malloc(size)[0..size];
T t = emplace!(T)(memory);
t.construct(args); // call the most derived
return t;
}

void construct() @nogc
{
printf("Base constructor called\n");
}

void destruct() @nogc
{
printf("Base destructor called\n");
free(cast(void*) this);
}
}

class Derived : Base
{
override void construct() @nogc
{
super.construct(); // construct from base to most derived
printf("Derived constructor called\n");
}

override void destruct() @nogc
{
printf("Derived destructor called\n");
super.destruct(); // destruct from derived to base
  // finishing with the deallocation
}
}

void main(string[] args) @nogc
{
Base b = Base.make!(Derived);
b.destruct();
}
```

things get called correctly


Base constructor called

Derived constructor called
Derived destructor called
Base destructor called

That bypasses the runtime mechanism for classes construction and 
destruction.
But `new` and `destroy` must not be called anymore, you must get 
stick with your own system.




Re: Destructors can't be @nogc?

2021-07-24 Thread user1234 via Digitalmars-d-learn

On Sunday, 25 July 2021 at 01:17:06 UTC, user1234 wrote:
That bypasses the runtime mechanism for classes construction 
and destruction.
But `new` and `destroy` must not be called anymore, you must 
get stick with your own system.


and of course `cast(Object)` should be prohibited (maybe `opCast` 
overload can enforce that).





Re: Destructors can't be @nogc?

2021-07-24 Thread Jim via Digitalmars-d-learn

On Saturday, 24 July 2021 at 10:15:50 UTC, Mike Parker wrote:

Personally, I think `@nogc` on main is a bad idea. `@nogc` 
should be used as far down the call stack as you can put it. 
The higher it is, the more difficulty you're going to run into. 
I recommend you apply it only on functions that run in the 
hottest parts of a program. Those are the areas where GC 
allocations triggering collections can obviously become an 
issue. So start there.


Alright, I see where you're coming from, and I'm inclined to 
agree.
I'll look into constraining @nogc to performance-critical parts 
of the program.


But I think `destroy()` not being @nogc even though the 
destructor is tagged @nogc is a bug, and it should be fixed. 
Because it is problematic behaviour even if we limit the usage of 
@nogc.


Re: Destructors can't be @nogc?

2021-07-24 Thread Mike Parker via Digitalmars-d-learn

On Saturday, 24 July 2021 at 09:45:01 UTC, Jim wrote:



In that case, what should we use to check functions called from 
`main` are not using the garbage collector?


When compiling, you can pass -vgc to dmd and it will tell you 
where GC allocations are possible.




Because it seems like we can't use classes with `@nogc`. We can 
write `.deinit()` for all our classes but what about the 
existing ones?


You're trying to shoehorn a GC-based feature into a no-GC world. 
It's possible, but it takes consideration of your overall 
architecture. If you're wanting to prevent GC usage in the entire 
program, then the easiest way is to avoid features that normally 
rely on the GC.


Consider if you really need classes. Can you just use malloced 
structs instead? If you do need classes, perhaps for inheritance, 
then scoped classes can be allocated on the stack where possible; 
then you get automatic destruction like structs. If you want to 
allocate them from the heap as in your example, then you can wrap 
instances in a ref-counted struct that calls a custom destructor 
(like your .deinit). You could also try marking your classes as 
extern(C++), then the GC isn't involved anyway.


There are many options, but in bypassing D's built-in automatic 
memory management, you're going to have to work for each of them 
to one degree or another.


Personally, I think `@nogc` on main is a bad idea. `@nogc` should 
be used as far down the call stack as you can put it. The higher 
it is, the more difficulty you're going to run into. I recommend 
you apply it only on functions that run in the hottest parts of a 
program. Those are the areas where GC allocations triggering 
collections can obviously become an issue. So start there.


If, down the road, you find that you've got some GC performance 
troubles outside of @nogc code, -dvst can help you find areas 
where you may be calling it unexpectedly and you can refactor 
those parts as needed and still do so without applying @nogc.


You can also look into disabling the GC at specific points and 
renabling later, or manually run collections at certain points. 
These are tools that are available that may or may not help.


You might get some ideas from the GC series on the blog:

https://dlang.org/blog/the-gc-series/

But if you really want to zap GC from the entire program, you're 
better of with -betterC. You aren't going to be using any 
GC-based features anyway, so why worry about @nogc? Everything is 
@nogc by default in betterC as the GC doesn't exist.





Re: Destructors can't be @nogc?

2021-07-24 Thread Guillaume via Digitalmars-d-learn

On Saturday, 24 July 2021 at 02:48:51 UTC, Paul Backus wrote:
Which raises the question, *why* is `destroy` not `@nogc` when 
the destructor is `@nogc`? And it turns out the answer is that 
[it calls `rt_finalize`][1], which [takes its argument as a 
`void*`][2] and therefore has to assume that any destructor it 
calls *might* use the GC.




Technically, they might indeed call the GC (if called 
deterministically, that is another topic).


If you know it won't happen, you can break the type system like 
this:

https://dplug.dpldocs.info/source/dplug.core.nogc.d.html#L80

Which is useful if you are running with some kind of DasWorseD 
(no runtime, disabled runtime, or custom runtime usw)


Re: Destructors can't be @nogc?

2021-07-24 Thread Jim via Digitalmars-d-learn

On Saturday, 24 July 2021 at 02:02:00 UTC, Mike Parker wrote:
The problem is that you've marked main as `@nogc`, and 
`destroy` is not `@nogc`. Remove the annotation from main and 
it will compile.


In that case, what should we use to check functions called from 
`main` are not using the garbage collector?


Because it seems like we can't use classes with `@nogc`. We can 
write `.deinit()` for all our classes but what about the existing 
ones?





Re: Destructors can't be @nogc?

2021-07-23 Thread Paul Backus via Digitalmars-d-learn

On Saturday, 24 July 2021 at 02:02:00 UTC, Mike Parker wrote:

On Friday, 23 July 2021 at 20:24:02 UTC, Jim wrote:

What is the problem here? Should I not call `destroy`? If so, 
what should I call instead?


The problem is that you've marked main as `@nogc`, and 
`destroy` is not `@nogc`. Remove the annotation from main and 
it will compile.


Which raises the question, *why* is `destroy` not `@nogc` when 
the destructor is `@nogc`? And it turns out the answer is that 
[it calls `rt_finalize`][1], which [takes its argument as a 
`void*`][2] and therefore has to assume that any destructor it 
calls *might* use the GC.


[1]: 
https://github.com/dlang/druntime/blob/v2.097.1/src/object.d#L3970
[2]: 
https://github.com/dlang/druntime/blob/v2.097.1/src/rt/lifetime.d#L1438


Re: Destructors can't be @nogc?

2021-07-23 Thread Mike Parker via Digitalmars-d-learn

On Friday, 23 July 2021 at 20:24:02 UTC, Jim wrote:

What is the problem here? Should I not call `destroy`? If so, 
what should I call instead?


The problem is that you've marked main as `@nogc`, and `destroy` 
is not `@nogc`. Remove the annotation from main and it will 
compile.


Destructors can't be @nogc?

2021-07-23 Thread Jim via Digitalmars-d-learn

Hello,

I've been playing with D and trying to understand how to work 
with @nogc. I must be doing something wrong, because even though 
I tagged the destructor for my class `@nogc`, I'm getting the 
following error: `.\min.d(27): Error: "@nogc" function "D main" 
cannot call non-@nogc function "object.destroy!(true, 
TestClass).destroy`


```D
import std.stdio : printf;
import core.lifetime : emplace;
import core.stdc.stdlib : malloc, free;

class TestClass {
int x;

this(int x) @nogc {
printf("TestClass's constructor called\n");
this.x = x;
}


~this() @nogc {
printf("TestClass's destructor called\n");
}
}

@nogc void
main() {
auto size = __traits(classInstanceSize, TestClass);
auto memory = malloc(size)[0..size];
TestClass x = emplace!(TestClass)(memory, 1);

printf("TestClass.x = %d\n", x.x);

destroy(x);
free(cast(void*)x);
}

```

What is the problem here? Should I not call `destroy`? If so, 
what should I call instead?