October 23, 2020
On 10/23/20 12:48 PM, Ola Fosheim Grøstad wrote:
> 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.

And when are those called? When the lifetime of the *object* is over, not the lifetime of the pointer to the object.

My point was that C++ doesn't have class references, they have class pointers, which is akin to D class references.

> 
>> 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.

I mean, if I compile this code for C++, does it store the pointer on the stack?

void foo()
{
   SomeClass* ptr = new SomeClass();
   ... // bunch of other code that never uses ptr
}

C++ doesn't have automatic management using pointers. So the answer is, the optimizer might just not store `ptr`. Just like it doesn't happen in D.

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

It doesn't have to even have a pointer if it's never used.

However, even though this case isn't "bad", there are bad cases that can be a problem:

auto c = storeWithC(new C);

If the function is something that squirrels away the parameter into a C-malloc'd block or C global (basically anything that is not scanned by the GC), and then returns the parameter, c is still not allocated on the stack, and the GC might collect it.

If DMD isn't smart enough to see that the reference may have escaped, then it shouldn't elide storage.

Under this view, I think actually the OP's case is a problem. Because Bar.create() could potentially be saving the result into a C heap block, in which case eliding the pointer storage is potentially going to cause memory corruption.

The optimizer is wrong, and should be changed.

Note that LDC does not do this optimization.

-Steve
October 23, 2020
On Fri, Oct 23, 2020 at 01:47:34PM -0400, Steven Schveighoffer via Digitalmars-d wrote: [...]
> If DMD isn't smart enough to see that the reference may have escaped, then it shouldn't elide storage.
> 
> Under this view, I think actually the OP's case is a problem. Because Bar.create() could potentially be saving the result into a C heap block, in which case eliding the pointer storage is potentially going to cause memory corruption.
> 
> The optimizer is wrong, and should be changed.
> 
> Note that LDC does not do this optimization.
[...]

Yet another reason for me to avoid DMD and use LDC instead...


T

-- 
This sentence is false.
October 23, 2020
On Friday, 23 October 2020 at 17:47:34 UTC, Steven Schveighoffer wrote:
> And when are those called? When the lifetime of the *object* is over, not the lifetime of the pointer to the object.

But the owning pointer is keeping the object alive.

A gc pointer is keeping the object alive, and is keeping it alive throughout its own lifetime. So yes, the lifetime of the object is supposed be at least as long as the owning pointer. And the lifetime lasts till the end of the scope. If there is a sideeffect then it should be considered to be _live_ until the end of the scope.

With the GC semantics you don't get a guarantee for when it will be destructed, but you should have strong guarantees for how long the lifetime at least can be expected to be.

Ideally you would also have guarantees for eventual destruction (i.e. that all objects are destroyed before the program terminates). With precise GC sans union, you should be able to get that.


> My point was that C++ doesn't have class references, they have class pointers, which is akin to D class references.

Well, "reference" and "pointer" are two words for the same thing, although D has some syntactical and semantic restrictions on class references.

That is pretty irrelevant though, as D class references with GC are owning pointers

C++ have owning pointers named unique_ptr and shared_ptr. Naked pointers are borrowing pointers, or for manual management. Forget about the mechanisms, conceptually that is the case.


> I mean, if I compile this code for C++, does it store the pointer on the stack?
>
> void foo()
> {
>    SomeClass* ptr = new SomeClass();
>    ... // bunch of other code that never uses ptr
> }
>
> C++ doesn't have automatic management using pointers. So the answer is, the optimizer might just not store `ptr`. Just like it doesn't happen in D.

But C++ compilers don't provide a GC, and naked pointers are not owning pointers.


>> _all_ GC pointers are conceptually owning pointers. They have to stay live throughout the whole scope.
>
> It doesn't have to even have a pointer if it's never used.

But a destructor with a side effect IS usage, and it should never be executed when there is a conceptually live pointer pointing to the object.

It is very simple, really.



October 23, 2020
On Friday, 23 October 2020 at 17:47:34 UTC, Steven Schveighoffer wrote:
> On 10/23/20 12:48 PM, Ola Fosheim Grøstad wrote:
>> On Friday, 23 October 2020 at 16:30:52 UTC, Steven Schveighoffer wrote:
>>> No, D classes map to C++ pointers to classes.

> It doesn't have to even have a pointer if it's never used.
> ...
> The optimizer is wrong, and should be changed.
>
> Note that LDC does not do this optimization.
>
> -Steve

So how can I avoid this issue with DMD? Disabling any optimization? In fact it does not occur with debug mode enabled.

scope doesn't change a thing.

Actually I do use the pointer in real code, (just one method call before a loop but still used).

October 23, 2020
On Friday, 23 October 2020 at 19:23:03 UTC, frame wrote:
> In fact it does not occur with debug mode enabled.
>

Correction: Issue disappears with -g flag, still exists with -debug flag



October 23, 2020
On Fri, Oct 23, 2020 at 07:23:03PM +0000, frame via Digitalmars-d wrote:
> On Friday, 23 October 2020 at 17:47:34 UTC, Steven Schveighoffer wrote:
[...]
> > The optimizer is wrong, and should be changed.
> > 
> > Note that LDC does not do this optimization.
[...]
> So how can I avoid this issue with DMD? Disabling any optimization? In fact it does not occur with debug mode enabled.
[...]

IMNSHO, drop dmd and use ldc2 instead.  Life is too short to have to deal with dmd backend bugs when I'm trying to get things *done*.  This isn't the first time the dmd backend has problems when optimization / inlining are enabled, and I fear it won't be the last.


T

-- 
MS Windows: 64-bit rehash of 32-bit extensions and a graphical shell for a 16-bit patch to an 8-bit operating system originally coded for a 4-bit microprocessor, written by a 2-bit company that can't stand 1-bit of competition.
October 23, 2020
On Friday, 23 October 2020 at 19:23:03 UTC, frame wrote:
> So how can I avoid this issue with DMD? Disabling any optimization? In fact it does not occur with debug mode enabled.
>

A dirty fix is to manually add the pointer as a GC root.

October 23, 2020
On 10/23/20 3:23 PM, frame wrote:
> On Friday, 23 October 2020 at 17:47:34 UTC, Steven Schveighoffer wrote:
>> On 10/23/20 12:48 PM, Ola Fosheim Grøstad wrote:
>>> On Friday, 23 October 2020 at 16:30:52 UTC, Steven Schveighoffer wrote:
>>>> No, D classes map to C++ pointers to classes.
> 
>> It doesn't have to even have a pointer if it's never used.
>> ...
>> The optimizer is wrong, and should be changed.
>>
>> Note that LDC does not do this optimization.
>>
> 
> So how can I avoid this issue with DMD? Disabling any optimization? In fact it does not occur with debug mode enabled.

I don't know if you need to do that. As much discussion as this post has generated, and even though the optimization is wrong, the specific circumstances that could cause a piece of memory to be collected before it's used is really unlikely. To spell it out again:

1. You generate a GC block (i.e. new class)
2. You store it as part of an initializer for a stack local
3. You never use it in the stack frame
4. But you also in that same initialization put it into a location that is not scanned by the GC (e.g. C-malloc'd data)

If you aren't doing all 4 of these things, then you don't have to worry.

> Actually I do use the pointer in real code, (just one method call before a loop but still used).
> 

So it shouldn't be collected then, even with optimizations.

-Steve
October 23, 2020
On Friday, 23 October 2020 at 19:39:52 UTC, Ola Fosheim Grøstad wrote:
> On Friday, 23 October 2020 at 19:23:03 UTC, frame wrote:
>> So how can I avoid this issue with DMD? Disabling any optimization? In fact it does not occur with debug mode enabled.
>>
>
> A dirty fix is to manually add the pointer as a GC root.

Another fix is to make the compiler think the end is reachable by warpping the infinitie loop in an if statements that tests something that the compiler cannot prove to be true. E.g.

if (alwaystrue()) inifinteloop

October 23, 2020
On Friday, 23 October 2020 at 19:39:52 UTC, Steven Schveighoffer wrote:
> On 10/23/20 3:23 PM, frame wrote:
>> On Friday, 23 October 2020 at 17:47:34 UTC, Steven Schveighoffer wrote:
>>> On 10/23/20 12:48 PM, Ola Fosheim Grøstad wrote:
>>>> On Friday, 23 October 2020 at 16:30:52 UTC, Steven Schveighoffer wrote:
>
>
> 1. You generate a GC block (i.e. new class)
> 2. You store it as part of an initializer for a stack local
> 3. You never use it in the stack frame
> 4. But you also in that same initialization put it into a location that is not scanned by the GC (e.g. C-malloc'd data)
>
> If you aren't doing all 4 of these things, then you don't have to worry.
>
>> Actually I do use the pointer in real code, (just one method call before a loop but still used).
>> 
>
> So it shouldn't be collected then, even with optimizations.
>
> -Steve

But I doesn't do 3. and it's collected :(
There is no special release optimization, I just call it via rdmd defaults.

Even if I do not run into memory problems, calling the destructor on a live object is an unepxected side effect which makes the dtor concept useless for me. It closes the remote connection the object hold (please don't ask about the meaning, it just the way it has to work) and this must not happen while the program is still running.