October 23, 2020
On Friday, 23 October 2020 at 13:34:56 UTC, Ola Fosheim Grøstad wrote:
> On Friday, 23 October 2020 at 13:23:18 UTC, Boris Carvajal wrote:
>> exit() is irrelevant, I checked the asm output.
>
> Ok, I guess GC.collect() is called after main() by the runtime then.

Just to clarify my point.
I was talking about 'scope foo = Bar.create();' that does nothing, neither the class is stack allocated nor the pointer is kept until the end of the function.

I think you are totally right about the optimizer vs D GC.
October 23, 2020
On Friday, 23 October 2020 at 13:48:52 UTC, Boris Carvajal wrote:
> On Friday, 23 October 2020 at 13:34:56 UTC, Ola Fosheim Grøstad wrote:
>> On Friday, 23 October 2020 at 13:23:18 UTC, Boris Carvajal wrote:
>>> exit() is irrelevant, I checked the asm output.
>>
>> Ok, I guess GC.collect() is called after main() by the runtime then.
>
> Just to clarify my point.
> I was talking about 'scope foo = Bar.create();' that does nothing, neither the class is stack allocated nor the pointer is kept until the end of the function.

I understand.

I guess the goal of @safe makes the expected semantics for scope (explicit destruction) impossible.

The compiler does not know if there are more references to the scope-pointed object. D would need an "isolated" pointer type for that.

I think an ARC implementation written for C++ shared_ptr would have been interesting for class-objects, but it is not on the table...

October 23, 2020
On Friday, 23 October 2020 at 13:58:04 UTC, Ola Fosheim Grøstad wrote:
> I guess the goal of @safe makes the expected semantics for scope (explicit destruction) impossible.

(I meant "deterministic destruction")

October 23, 2020
On 10/23/20 2:57 AM, Ola Fosheim Grøstad wrote:
> On Friday, 23 October 2020 at 06:37:43 UTC, Daniel Kozak wrote:
>> On Fri, Oct 23, 2020 at 8:20 AM frame via Digitalmars-d < digitalmars-d@puremagic.com> wrote:
>>
>>>  Even this is a "intelligent"
>>> feature by the compiler for non reachable code like that loop,
>>> it's still confusing. There are maybe situations while the object
>>> should stay in background, eg. socket related/event stuff. If
>>> there is a valid pointer, there is no excuse to reap it.
>>>
>>>
>> There is no use of foo anywhere so there is no reason to not collect it.
> 
> It is in use until the end of main. Think locking. Seems like the pointer is optimized away and then the GC collects. Makes RAII useless with GC.

But you don't get RAII with classes or pointers. Only with structs.

> 
> Basically a consequence of the optimizer assuming that there is no GC. A rather serious correctness issue.
> 
> 

I don't think so. A class reference itself doesn't have a destructor. The compiler is free to elide storage just like it was a pointer. One cannot expect any specific behavior from the GC in this case, as the correctness of the program doesn't depend on the object staying in place for the duration of the function. Here there is just a disconnect in how the OP expects the compiler to implement the machine code.

Use a type that *does* support RAII (i.e. struct with dtor), and it will be allocated and properly destroyed.

-Steve
October 23, 2020
On Friday, 23 October 2020 at 16:00:58 UTC, Steven Schveighoffer wrote:
> But you don't get RAII with classes or pointers. Only with structs.

But that is only a consequence of the optimizer not being written with a GC in mind. That is the point. Isn't D structs the same as C++ PODs? I thought C++ classes with virtuals/RTTI is supposed to map to D classes? Then RAII has to work.

> I don't think so. A class reference itself doesn't have a destructor. The compiler is free to elide storage just like it was a pointer. One cannot expect any specific behavior from the GC in this case, as the correctness of the program doesn't depend on the object staying in place for the duration of the function. Here there is just a disconnect in how the OP expects the compiler to implement the machine code.

That would be very surprising semantics as you expect a pointer to be LIVE throughout the scope in which it was instantiated. If it is LIVE then it cannot be collected by the GC.

This all boils down to the optimizer being GC ignorant. I understand that it is too much work to make the optimizer take that into consideration, but it most certainly goes against the expected norm for the semantics of BLOCKS that LIVE pointers are being collected.

October 23, 2020
On Friday, 23 October 2020 at 06:57:05 UTC, Ola Fosheim Grøstad wrote:
> It is in use until the end of main. Think locking. Seems like the pointer is optimized away and then the GC collects. Makes RAII useless with GC.
>
> Basically a consequence of the optimizer assuming that there is no GC. A rather serious correctness issue.

Your C++ glasses are too thick, the snippet uses reference type, there's no RAII there.
October 23, 2020
On 10/23/20 12:14 PM, Ola Fosheim Grøstad wrote:
> On Friday, 23 October 2020 at 16:00:58 UTC, Steven Schveighoffer wrote:
>> But you don't get RAII with classes or pointers. Only with structs.
> 
> But that is only a consequence of the optimizer not being written with a GC in mind. That is the point. Isn't D structs the same as C++ PODs? I thought C++ classes with virtuals/RTTI is supposed to map to D classes? Then RAII has to work.

No, D classes map to C++ pointers to classes.

How does C++ RAII work for class pointers that aren't used?

> 
>> I don't think so. A class reference itself doesn't have a destructor. The compiler is free to elide storage just like it was a pointer. One cannot expect any specific behavior from the GC in this case, as the correctness of the program doesn't depend on the object staying in place for the duration of the function. Here there is just a disconnect in how the OP expects the compiler to implement the machine code.
> 
> That would be very surprising semantics as you expect a pointer to be LIVE throughout the scope in which it was instantiated. If it is LIVE then it cannot be collected by the GC.

Expectation for what purpose?

I think of code being executed in the way I write it. We all know that optimizers don't do that. As long as it's not changing the semantic meaning of the program, I'm fine with it.

If you look at this code:

void main()
{
   int i;
   foo();
}

Does i even exist? Does it exist after the first line? Is it important if the compiler allocates space for it on the stack? Does it get initialized to 0?

All of these things are items that have no bearing on the execution of the program, so it's fine for the optimizer to just completely get rid of the variable storage. This is no different.

> 
> This all boils down to the optimizer being GC ignorant. I understand that it is too much work to make the optimizer take that into consideration, but it most certainly goes against the expected norm for the semantics of BLOCKS that LIVE pointers are being collected.
> 

I think the optimizer is correct. There are no live pointers, because the object is not used, ever.

How many times has someone posted code touting a benchmark of something and the optimizer gets rid of the entire program? How is this any different?

-Steve
October 23, 2020
On Friday, 23 October 2020 at 16:27:53 UTC, Kagamin wrote:
> Your C++ glasses are too thick, the snippet uses reference type, there's no RAII there.

"Resource acquisition is initialization" is typically done with reference types. Doesn't matter whether you name them "struct" or "class". So what? It is a completely bogus argument. Since D now has a precise GC you should get reliable destruction if you avoid unions.

October 23, 2020
On 10/23/20 12:33 PM, Ola Fosheim Grøstad wrote:
> On Friday, 23 October 2020 at 16:27:53 UTC, Kagamin wrote:
>> Your C++ glasses are too thick, the snippet uses reference type, there's no RAII there.
> 
> "Resource acquisition is initialization" is typically done with reference types. Doesn't matter whether you name them "struct" or "class". So what? It is a completely bogus argument. Since D now has a precise GC you should get reliable destruction if you avoid unions.
> 

There's no RAII for classes or structs allocated on the heap in C++. You have to manually manage the lifetime (either by manually freeing it or encapsulating it into an RAII structure like shared_ptr). You only get RAII for items allocated on the stack.

But the *resource* is likely a reference. The *owner* of the resource is the struct that lives on the stack. A class reference is simply a pointer, not an RAII-enabled structure.

-Steve
October 23, 2020
On Friday, 23 October 2020 at 16:30:52 UTC, Steven Schveighoffer wrote:
> No, D classes map to C++ pointers to classes.

Huh? D has to be able to call virtual destructors for C++ objects.

> How does C++ RAII work for class pointers that aren't used?

What do you mean? The pointer is valid until the end of the scope. Then the object is destructed if it is a sole owning pointer.

_all_ GC pointers are conceptually owning pointers. They have to stay live throughout the whole scope.


> Expectation for what purpose?

Basic CS conventions?


> I think of code being executed in the way I write it. We all know that optimizers don't do that. As long as it's not changing the semantic meaning of the program, I'm fine with it.

But it did! That was the point!


>
> If you look at this code:
>
> void main()
> {
>    int i;
>    foo();
> }
>
> Does i even exist? Does it exist after the first line? Is it important if the compiler allocates space for it on the stack? Does it get initialized to 0?

What do you mean? There are no side effects? Do D allow side effects in destructors? If yes, then you cannot cap the lifetime of the object at your own will.

> just completely get rid of the variable storage. This is no different.

It clearly is. The order of the writeln() execution was wrong.

> I think the optimizer is correct. There are no live pointers, because the object is not used, ever.

Of course it is used. It has a destructor with a side effect.