May 26, 2017
On Friday, 26 May 2017 at 19:31:26 UTC, Steven Schveighoffer wrote:
> Note that the implication in your strawman is that you need a special exception to be nogc. I'd rather leave the (de)allocation of the exception up to the thrower/catcher, and have the exception not care about its own location.

That the thrower should decide how to allocate, I agree with. Or at least have a sane default that can be optionally changed. But deallocation should just automagically work, otherwise people will forget to do it and end up with leaks at the very least.

Atila
May 26, 2017
On 5/19/17 11:45 AM, Mike Parker wrote:
> DIP 1008 is titled "Exceptions and @nogc".
>
> https://github.com/dlang/DIPs/blob/master/DIPs/DIP1008.md
>
> All review-related feedback on and discussion of the DIP should occur in
> this thread. The review period will end at 11:59 PM ET on June 2 (3:59
> AM GMT June 3), or when I make a post declaring it complete.
>
> At the end of Round 1, if further review is deemed necessary, the DIP
> will be scheduled for another round. Otherwise, it will be queued for
> the formal review and evaluation by the language authors.
>
> Extensive discussion of this DIP has already taken place in two threads,
> both linked from the document. You may find it beneficial to skim
> through those threads before posting any feedback here.
>
> Thanks in advance to all who participate.
>
> Destroy!

Some comments:

"The destructor for Throwable will, if the refcount is 1, call _d_delThrowable(e.next), i.e. on the head of the chained list of exceptions."

How does this work if the throwable is being destroyed by the GC? In this case, e.next may be a dangling pointer.

"it will call the new function _d_newThrowable() which will allocate E and intialize it for refcounting."

Where is it allocated? If on the GC heap, how can it be @nogc? If not, then how does it interact with GC pointers? e.g. the e.next pointer may point to a GC-allocated exception, how to stop the GC from collecting it early?

Part of this may mean clarifying what @nogc actually means. Does it mean no interaction with the GC system, or does it mean "cannot run a collection cycle"?

"In catch blocks, e is regarded as scope so that it cannot escape the catch block."

If e is scope, then the destructor is called. However, since e is a reference type, the data it points at isn't really stack allocated, and the scope-ness ends at the reference, no? So what if you have something like this?

void *foo;

try
{
  func();
}
catch(MyException e)
{
   foo = e.voidptr; // get pointer to some member
}

Should foo be allowed to capture pieces of e? How does the compiler stop that if not? What expectations can e assume in terms of its members that are references? Can it assume that it is in charge of the management of that memory if it's ref counted, or does it need to assume GC usage for those items?

-Steve
May 26, 2017
On 5/26/2017 11:58 AM, Atila Neves wrote:
> On Friday, 26 May 2017 at 16:36:16 UTC, Walter Bright wrote:
>> On 5/26/2017 4:50 AM, Steven Schveighoffer wrote:
>>> But to answer your question, don't mark the exception scope in that case. The compiler should complain if you copy it outside the scope, no?
>>
>> I suspect if you proceed with that line of reasoning, you'll wind up in the same place I did with DIP 1008.
> 
> Could you explain the line of reasoning that led you do dip1008? Thanks,

Follow every construct that enables a reference to an Exception to escape and figure out what you're going to do about it. After all, the call to free() must happen exactly once per Exception that was allocated with malloc(), and one must also deal with GC allocated Exceptions in the mix. A typical omission is not considering with the rethrow case nor the chaining case.

Pragmatically speaking:

Counter-proposals that don't do this are legion and I've critiqued a lot of them for missing crucial cases. I'm getting more reluctant to continue doing that, so I ask submitters of them to be both much more complete, and if they are complete solutions, provide a rationale as to why they are better than DIP 1008 rather than being arguably equivalent.

Also keep in mind that DIP 1008 has an implementation sitting in the PR queue, and it's fairly simple. Solutions that require redesigning D or the compiler or both are not likely to get much traction. The list of things I have to do next reminds me of trying to run up the down escalator while it constantly increases speed.
May 26, 2017
On 5/26/2017 2:31 PM, Steven Schveighoffer wrote:
> "The destructor for Throwable will, if the refcount is 1, call _d_delThrowable(e.next), i.e. on the head of the chained list of exceptions."
> 
> How does this work if the throwable is being destroyed by the GC? In this case, e.next may be a dangling pointer.

The GC calls a finalizer which calls _d_delThrowable on the e.next value.

> "it will call the new function _d_newThrowable() which will allocate E and intialize it for refcounting."
> 
> Where is it allocated?

My implementation uses malloc().

> If on the GC heap,

It isn't.

> how can it be @nogc?

And that's how it can be @nogc.

> If not, then how does it interact with GC pointers?

The refcount of 0 means it is GC allocated.

> e.g. the e.next pointer may point to a GC-allocated exception, how to stop the GC from collecting it early?

The usual way - register the roots with the GC. Take a look at the PRs for this, they do that.

> Part of this may mean clarifying what @nogc actually means. Does it mean no interaction with the GC system, or does it mean "cannot run a collection cycle"?

@nogc means no GC allocations occur in the @nogc code, the same as usual, no more, no less.

> "In catch blocks, e is regarded as scope so that it cannot escape the catch block."
> 
> If e is scope, then the destructor is called. However, since e is a reference type, the data it points at isn't really stack allocated, and the scope-ness ends at the reference, no? So what if you have something like this?
> 
> void *foo;
> 
> try
> {
>    func();
> }
> catch(MyException e)
> {
>     foo = e.voidptr; // get pointer to some member
> }
> 
> Should foo be allowed to capture pieces of e? How does the compiler stop that if not?

That's what DIP 1000 addresses. DIP 1008 relies on DIP 1000.

> What expectations can e assume in terms of its members that are references? Can it assume that it is in charge of the management of that memory if it's ref counted, or does it need to assume GC usage for those items?

See DIP 1000.

May 26, 2017
what do you mean by this:

"Look into http: vs https: link handling. Some errors with https"

are you trying to publish https links or go to https://ink.vu links?


May 26, 2017
sorry i replied to the wrong email in my inbox and didn't notice until it was already sent

plz ignore me

May 27, 2017
On Saturday, 27 May 2017 at 01:27:24 UTC, Walter Bright wrote:
> On 5/26/2017 11:58 AM, Atila Neves wrote:
>> On Friday, 26 May 2017 at 16:36:16 UTC, Walter Bright wrote:
>>> On 5/26/2017 4:50 AM, Steven Schveighoffer wrote:
>>>> But to answer your question, don't mark the exception scope in that case. The compiler should complain if you copy it outside the scope, no?
>>>
>>> I suspect if you proceed with that line of reasoning, you'll wind up in the same place I did with DIP 1008.
>> 
>> Could you explain the line of reasoning that led you do dip1008? Thanks,
>
> Follow every construct that enables a reference to an Exception to escape and figure out what you're going to do about it.

The idea would be that this only happens with `catch(scope T ex)`, so that the exception _can't_ escape. It can't be stored somewhere else, it can't be rethrown. It's going away so it's ok to call the destructor on it.

> After all, the call to free() must happen exactly once per Exception that was allocated with malloc(), and one must also deal with GC allocated Exceptions in the mix.

GC exceptions would just not do any work in the destructor.

> A typical omission is not considering with the rethrow case nor the chaining case.

I omitted the chaining case in my example for brevity. A production-grade implementation would call the destructors of the exceptions in the chain. Sorry for not making that clear.

My idea is that every exception knows how to get rid of itself if by chance it's going away soon. And that if anyone wants to store a `@nogc` exception then that exception instance provides a `.dup` function.

I got to this idea by considering what it would like if we could catch smart pointers:

catch(RefCounted!Exception ex)

Then I made it simpler. TBH I'd rather be able to catch a smart pointer.

> Counter-proposals that don't do this are legion and I've critiqued a lot of them for missing crucial cases. I'm getting more reluctant to continue doing that, so I ask submitters of them to be both much more complete, and if they are complete solutions, provide a rationale as to why they are better than DIP 1008 rather than being arguably equivalent.


I understand that, I'd have the same attitude towards other proposals if I were you. I know and confess I tend to be overly optimistic and fail to consider everything that can go wrong. The devil is indeed in the details.

Why do I think this scheme is better?

1. The programmer controls how allocation is done
2. Less of a special case (I admit it's a bit "special-casey")
3. Doesn't require another 2 druntime functions
4. Arguably easier to explain how it works

>
> Also keep in mind that DIP 1008 has an implementation sitting in the PR queue, and it's fairly simple. Solutions that require redesigning D or the compiler or both are not likely to get much traction. The list of things I have to do next reminds me of trying to run up the down escalator while it constantly increases speed.

I could be completely wrong, but my intuition says that what I proposed wouldn't be too hard to implement either.

Atila

May 26, 2017
On 5/26/2017 11:51 AM, Atila Neves wrote:
> Since it's `scope`, where would it be copied to? This is assuming dip1000, of course.

The rethrow case must be allowed:

    throw ex;
May 27, 2017
On Saturday, 27 May 2017 at 02:40:47 UTC, Walter Bright wrote:
> On 5/26/2017 11:51 AM, Atila Neves wrote:
>> Since it's `scope`, where would it be copied to? This is assuming dip1000, of course.
>
> The rethrow case must be allowed:
>
>     throw ex;

Then either:

1. Elide the destructor call in that situation
2. Make the developer write `throw ex.dup`

I'd prefer #2, since it would make a lot more sense for `scope`. `throw` escapes the ex IMHO.

My $0.02

Atila
May 27, 2017
On 5/26/2017 11:50 PM, Atila Neves wrote:
> On Saturday, 27 May 2017 at 02:40:47 UTC, Walter Bright wrote:
>> On 5/26/2017 11:51 AM, Atila Neves wrote:
>>> Since it's `scope`, where would it be copied to? This is assuming dip1000, of course.
>>
>> The rethrow case must be allowed:
>>
>>     throw ex;
> 
> Then either:
> 
> 1. Elide the destructor call in that situation

That's Andrei's "move semantics" idea, and it involves major code effort in the compiler.

There's the "chain" case, too.

> 2. Make the developer write `throw ex.dup`

The point is to not require them to rewrite their code.


> I'd prefer #2, since it would make a lot more sense for `scope`. `throw` escapes the ex IMHO.
> 
> My $0.02
> 
> Atila