February 17, 2015
On Tuesday, 17 February 2015 at 18:40:51 UTC, Matthias Bentrup wrote:
> On Tuesday, 17 February 2015 at 18:30:24 UTC, Jonathan Marler wrote:
>> I thought of the same thing but then realized that it would be impossible to ensure that the catch block wouldn't stomp on that memory.
>
> The catcher wouldn't stomp any more on the thrower's memory than a function stomps on the memory of its caller. All the data of the thrower is safe, because it is above the stack pointer. The unwinding hasn't been done at that point.

That would work if you didn't have to unwind the stack but unfortunately you do.  The catch block exists in the context of the function it is written in.  That means it assumes the stack pointer and stack variables are all in the context of it's defining function.  If you executed the catch code when the stack wasn't unwound, then it wouldn't know where any of the variables were.  Does that make sense?  Think about it for a minute.  You proposal suggests that the catch code can be executed no matter how many child functions have been added to the stack.  This is impossible since the catch code no longer knows where all of it's stack variables are.  Normally it uses an offset to the stack pointer but now it has been changed.  That's why you have to unwind the stack.

I like that you thought of this idea but you have to follow through with the details to see whether or not it would work.  I immediately thought of this idea and then realized that it was impossible.  A variation on the idea might work, but the idea as it is does not.  Keep thinking about it though...if nothing else it will give you a better understanding of the stack.  The more people that are intimate with the inner workings of how these things work the better.
February 17, 2015
On Tuesday, 17 February 2015 at 20:48:07 UTC, Jonathan Marler wrote:
> That would work if you didn't have to unwind the stack but unfortunately you do.  The catch block exists in the context of the function it is written in.  That means it assumes the stack pointer and stack variables are all in the context of it's defining function.  If you executed the catch code when the stack wasn't unwound, then it wouldn't know where any of the variables were.  Does that make sense?  Think about it for a minute.  You proposal suggests that the catch code can be executed no matter how many child functions have been added to the stack.  This is impossible since the catch code no longer knows where all of it's stack variables are.  Normally it uses an offset to the stack pointer but now it has been changed.  That's why you have to unwind the stack.

So the catcher would have to behave like a delegate.
February 17, 2015
On Tuesday, 17 February 2015 at 21:30:00 UTC, Matthias Bentrup wrote:
> On Tuesday, 17 February 2015 at 20:48:07 UTC, Jonathan Marler wrote:
>> That would work if you didn't have to unwind the stack but unfortunately you do.  The catch block exists in the context of the function it is written in.  That means it assumes the stack pointer and stack variables are all in the context of it's defining function.  If you executed the catch code when the stack wasn't unwound, then it wouldn't know where any of the variables were.  Does that make sense?  Think about it for a minute.  You proposal suggests that the catch code can be executed no matter how many child functions have been added to the stack.  This is impossible since the catch code no longer knows where all of it's stack variables are.  Normally it uses an offset to the stack pointer but now it has been changed.  That's why you have to unwind the stack.
>
> So the catcher would have to behave like a delegate.

Sure but then you're allocating GC memory again (compounding the problem you're trying to solve in the first place).  The best solution I can think of that should work in almost every case is to allocate the exception on the non-GC heap, then make the catcher cleans up the exception.  Simple, and makes sense when you think about the lifetime of an exception.  This would have been dangerous before but with the new scope semantics D can ensure that the exception does not escape the catch block (and therefore gets cleaned up properly).
February 17, 2015
On Tuesday, 17 February 2015 at 15:54:17 UTC, Andrei Alexandrescu wrote:
> On 2/16/15 3:17 PM, Jonathan Marler wrote:
>> Is there a proposal for how D will support throwing Exceptions in @nogc
>> code in the future?
>
> Nothing definite. We will get on to that right after 2.067. This is a good time to start discussions. -- Andrei

There are various problem with exception, and @nogc is only one of them.

The other big issue is that exceptions bypass the type qualifier system.

I want to hammer once more the owned solution.
February 18, 2015
On Tuesday, 17 February 2015 at 19:03:49 UTC, Chris Williams wrote:
> Every throwable function call could be assumed to have a typed result (even void functions) and if, after the return, the caller checks the type and detects that it was an error, bubbles that up, then eventually you get to wherever the catcher is.
>
> But so basically, the current ABI doesn't support it and there's no desire to change it? How do exceptions currently happen, if not via some official ABI declaration of how throwable methods interact with one another? The compiler/linker determines where the catcher is and inserts code to cut down the stack and perform a long jump all the way back? If so, how do scope statements work?

This kind of stunt is taxing on the fast path. It can be implemented as setjmp/longjmp but is more and more avoided in favor of libunwind based solutions for languages that have code running on unwinding.

libunwind based solution is slower to unwind, but is not very taxing in the fast path (only prevent some optimizations to be done like tail call).

This solution is a non starter perforamnce-wize for D.
February 18, 2015
On Wednesday, 18 February 2015 at 00:14:55 UTC, deadalnix wrote:
> On Tuesday, 17 February 2015 at 19:03:49 UTC, Chris Williams wrote:
>> Every throwable function call could be assumed to have a typed result (even void functions) and if, after the return, the caller checks the type and detects that it was an error, bubbles that up, then eventually you get to wherever the catcher is.
>>
>> But so basically, the current ABI doesn't support it and there's no desire to change it? How do exceptions currently happen, if not via some official ABI declaration of how throwable methods interact with one another? The compiler/linker determines where the catcher is and inserts code to cut down the stack and perform a long jump all the way back? If so, how do scope statements work?
>
> This kind of stunt is taxing on the fast path. It can be implemented as setjmp/longjmp but is more and more avoided in favor of libunwind based solutions for languages that have code running on unwinding.
>
> libunwind based solution is slower to unwind, but is not very taxing in the fast path (only prevent some optimizations to be done like tail call).
>
> This solution is a non starter perforamnce-wize for D.

I didn't mean it as a solution. As said, I was just looking for an intro to the topic, so that I (and others) could meaningfully contribute or at least understand the options. I'll look up libunwind and, if that has enough info for me to grok it, create a wiki page on the subject.
February 18, 2015
On Wednesday, 18 February 2015 at 00:54:37 UTC, Chris Williams wrote:
> I didn't mean it as a solution. As said, I was just looking for an intro to the topic, so that I (and others) could meaningfully contribute or at least understand the options. I'll look up libunwind and, if that has enough info for me to grok it, create a wiki page on the subject.

It is a horrible solution developed for the Itanium VLIW architecture which is very sensitive to branching. IRRC it basically works by looking at the return address on the stack, then picking up stack frame information in a global table to unwind. It is language agnostic and the language provides a "personality function" to unwind correctly in a language dependent manner...

AFAIK, C++ exceptions are copied from the stack to a special memory region when unwinding to prevent the memory issues D suffers from.

I agree that a fast unwind with stack pointer reset or multiple return paths would be much better, but you need to rewrite the backend to support it. That's the main issue... the "fast path" argument is just a sorry excuse that literally means that exceptions are avoided for common failures in C++. As a result you get APIs that are nonuniform.


February 18, 2015
On Wednesday, 18 February 2015 at 08:13:35 UTC, Ola Fosheim Grøstad wrote:
> On Wednesday, 18 February 2015 at 00:54:37 UTC, Chris Williams wrote:
>> I didn't mean it as a solution. As said, I was just looking for an intro to the topic, so that I (and others) could meaningfully contribute or at least understand the options. I'll look up libunwind and, if that has enough info for me to grok it, create a wiki page on the subject.
>
> It is a horrible solution developed for the Itanium VLIW architecture which is very sensitive to branching. IRRC it basically works by looking at the return address on the stack, then picking up stack frame information in a global table to unwind. It is language agnostic and the language provides a "personality function" to unwind correctly in a language dependent manner...
>
> AFAIK, C++ exceptions are copied from the stack to a special memory region when unwinding to prevent the memory issues D suffers from.
>
> I agree that a fast unwind with stack pointer reset or multiple return paths would be much better, but you need to rewrite the backend to support it. That's the main issue... the "fast path" argument is just a sorry excuse that literally means that exceptions are avoided for common failures in C++. As a result you get APIs that are nonuniform.

Windows SEH maintains a per-thread linked list of exception handlers, but the C++ runtime seems to install only one handler at the start of every function and resorts to lookup tables if there are multiply try{}s in the function.

If you want to avoid lookup tables, you can of course add/remove catchers dynamically whenever you enter/leave a try block, that would add a small cost to every try, but avoids the (larger) table lookup cost on the catch.
February 18, 2015
On Monday, 16 February 2015 at 23:17:03 UTC, Jonathan Marler wrote:
> Is there a proposal for how D will support throwing Exceptions in @nogc code in the future?  I've searched the forums and found different proposals that involve things like pre-allocated exceptions, non-gc heap allocated exceptions or even stack allocated exceptions.  I don't want to debate the details of each solution, I'd just like to know if any of these proposals are deemed a "good idea", or if any of them are currently being worked on.  I personally think that using non-gc heap allocated exceptions in combination with the new scope semantics would be a great solution in many cases, but I don't know if there is any consensus or if this topic is just on the back-burner. Thanks.

From my POV best proposal from last lengthy discussion was to enable reference-counted non-gc-heap Exceptions. But that needs a language change because RefCounted!T is a struct and thus neither can be thrown nor can be part of Throwable class hierarchy.

Any concept that implies that exceptions an be deallocated in `catch` block is absolutely unacceptable because it conflicts with exception propagation from fibers - a very important piece of functionality.
February 18, 2015
On Wednesday, 18 February 2015 at 08:13:35 UTC, Ola Fosheim Grøstad wrote:
> It is a horrible solution developed for the Itanium VLIW architecture which is very sensitive to branching. IRRC it basically works by looking at the return address on the stack, then picking up stack frame information in a global table to unwind. It is language agnostic and the language provides a "personality function" to unwind correctly in a language dependent manner...
>

Which is true, but would be as true without the horrible mention. Adjective do not constitute arguments.

> I agree that a fast unwind with stack pointer reset or multiple return paths would be much better, but you need to rewrite the backend to support it. That's the main issue... the "fast path" argument is just a sorry excuse that literally means that exceptions are avoided for common failures in C++. As a result you get APIs that are nonuniform.

What you agree with is irrelevant if it do not come backed by facts.

You can qualify thing as "horrible", "sorry excuses" and so on, the only thing it is telling us is that you seems incapable of forming a compelling argument and rely on smear instead.