June 06, 2012
On Jun 6, 2012, at 9:45 AM, deadalnix wrote:

> Le 05/06/2012 18:21, Sean Kelly a écrit :
>> On Jun 5, 2012, at 8:44 AM, Jonathan M Davis<jmdavisProg@gmx.com>  wrote:
>>> 
>>> In many cases, it's probably fine, but if the program is in a bad enough state
>>> that an Error is thrown, then you can't know for sure that any particular such
>>> block will execute properly (memory corruption being the extreme case), and if
>>> it doesn't run correctly, then it could make things worse (e.g. writing
>>> invalid data to a file, corrupting that file). Also, if the stack is not unwound
>>> perfectly (as nothrow prevents), then the program's state will become
>>> increasingly invalid the farther that the program gets from the throw point,
>>> which will increase the chances of cleanup code functioning incorrectly, as
>>> any assumptions that they've made about the program state are increasingly
>>> likely to be wrong (as well as it being increasingly likely that the variables
>>> that they operate on no longer being valid).
>> 
>> Then we should really just abort on Error. What I don't understand is the assertion that it isn't safe to unwind the stack on Error and yet that catch(Error) clauses should still execute. If the program state is really so bad that nothing can be done safely then why would the user attempt to log the error condition or anything else?
>> 
> 
> Yes, either we consider the environement may have been compromised and it don't even make sense to throw an Error, or we consider this environement is still consistent, and we have a logic bug. If so, scope (especially failure) should run when stack is unwinded.
> 
> As need depend on the software (an office suite should try its best to fail gracefully, a plane autpilot should crash ASAP and give control back to the pilot), what is needed here is a compiler switch.

I think a runtime hook is reasonable instead.  But the default case has to be the more permissive case.  It's safe to tighten the rules but never to loosen them, since external code will be written assuming the default behavior.
June 06, 2012
On 06.06.2012 20:53, Sean Kelly wrote:
> On Jun 6, 2012, at 9:45 AM, deadalnix wrote:
>
>> Le 05/06/2012 18:21, Sean Kelly a écrit :
>>> On Jun 5, 2012, at 8:44 AM, Jonathan M Davis<jmdavisProg@gmx.com>   wrote:
>>>>
>>>> In many cases, it's probably fine, but if the program is in a bad enough state
>>>> that an Error is thrown, then you can't know for sure that any particular such
>>>> block will execute properly (memory corruption being the extreme case), and if
>>>> it doesn't run correctly, then it could make things worse (e.g. writing
>>>> invalid data to a file, corrupting that file). Also, if the stack is not unwound
>>>> perfectly (as nothrow prevents), then the program's state will become
>>>> increasingly invalid the farther that the program gets from the throw point,
>>>> which will increase the chances of cleanup code functioning incorrectly, as
>>>> any assumptions that they've made about the program state are increasingly
>>>> likely to be wrong (as well as it being increasingly likely that the variables
>>>> that they operate on no longer being valid).
>>>
>>> Then we should really just abort on Error. What I don't understand is the assertion that it isn't safe to unwind the stack on Error and yet that catch(Error) clauses should still execute. If the program state is really so bad that nothing can be done safely then why would the user attempt to log the error condition or anything else?
>>>
>>
>> Yes, either we consider the environement may have been compromised and it don't even make sense to throw an Error, or we consider this environement is still consistent, and we have a logic bug. If so, scope (especially failure) should run when stack is unwinded.
>>
>> As need depend on the software (an office suite should try its best to fail gracefully, a plane autpilot should crash ASAP and give control back to the pilot), what is needed here is a compiler switch.
>
> I think a runtime hook is reasonable instead.  But the default case has to be the more permissive case.
>  It's safe to tighten the rules but never to loosen them, since external code will be written assuming the default behavior.

Yes, that's what I had in mind when I renamed topic to "runtime hook for Crash on Error". Default should be treat Error as just another type of throwable that is logically not caught by catch(Exception).

-- 
Dmitry Olshansky
June 06, 2012
On Wednesday, 6 June 2012 at 13:26:09 UTC, Steven Schveighoffer wrote:
> On Wed, 06 Jun 2012 05:13:39 -0400, Lars T. Kyllingstad <public@kyllingen.net> wrote:
>
>> On Friday, 1 June 2012 at 12:29:27 UTC, Steven Schveighoffer wrote:
>>> On Fri, 01 Jun 2012 04:48:27 -0400, Dmitry Olshansky <dmitry.olsh@gmail.com> wrote:
>>>
>>>> I don't agree that OutOfMemory is critical:
>>>> 	--> make it an exception ?
>>>
>>> No.  What we need is a non-throwing version of malloc that returns NULL.  (throwing version can wrap this).  If you want to throw an exception, then throw it there (or use enforce).
>>
>> With some sugar:
>>
>>     auto a = nothrow new Foo; // Returns null on OOM
>>
>> Then, ordinary new can be disallowed in nothrow code.
>
> That doesn't work, new conflates memory allocation with construction.  What if the constructor throws?

The constructor would have to be marked nothrow as well.  Actually, that is currently the case:

    class Foo
    {
        this() { }
    }

    void bar() nothrow
    {
        auto f = new Foo;
        // Error: constructor this is not nothrow
        // Error: function 'bar' is nothrow yet may throw
    }

The only difference between "new" and "nothrow new" is that the latter would return null on allocation failure instead of throwing an OutOfMemoryError.

Based on this discussion, I gather that one of the big problems is that the compiler is free to elide stack unwinding code for nothrow functions, despite the fact that they can in fact throw Errors.  One solution would therefore be to disallow *all* throwing from nothrow functions, including Errors.

Besides OutOfMemoryError, I can only think of two other Errors that would make this a hassle: AssertError and RangeError.  However, both of these signify problems with the program logic, and unwinding the stack is probably a bad idea anyway, so why not simply make these abort()?

-Lars

June 06, 2012
On Wednesday, 6 June 2012 at 09:38:35 UTC, Jonathan M Davis wrote:
> On Wednesday, June 06, 2012 11:13:39 Lars T. Kyllingstad wrote:
>> On Friday, 1 June 2012 at 12:29:27 UTC, Steven Schveighoffer
>> 
>> wrote:
>> > On Fri, 01 Jun 2012 04:48:27 -0400, Dmitry Olshansky
>> > 
>> > <dmitry.olsh@gmail.com> wrote:
>> >> I don't agree that OutOfMemory is critical:
>> >> 	--> make it an exception ?
>> > 
>> > No.  What we need is a non-throwing version of malloc that
>> > returns NULL.  (throwing version can wrap this).  If you want
>> > to throw an exception, then throw it there (or use enforce).
>> 
>> With some sugar:
>> 
>>      auto a = nothrow new Foo; // Returns null on OOM
>> 
>> Then, ordinary new can be disallowed in nothrow code.
>
> But then instead of getting a nice, clear, OutOfMemoryError, you get a
> segfault - and that's assuming that it gets dereferenced anywhere near where
> it's allocated. [...]

I agree; it would make nothrow an "advanced feature", which kind of sucks.

-Lars

June 06, 2012
Le 06/06/2012 18:56, Dmitry Olshansky a écrit :
> On 06.06.2012 20:53, Sean Kelly wrote:
>> On Jun 6, 2012, at 9:45 AM, deadalnix wrote:
>>
>>> Le 05/06/2012 18:21, Sean Kelly a écrit :
>>>> On Jun 5, 2012, at 8:44 AM, Jonathan M Davis<jmdavisProg@gmx.com>
>>>> wrote:
>>>>>
>>>>> In many cases, it's probably fine, but if the program is in a bad
>>>>> enough state
>>>>> that an Error is thrown, then you can't know for sure that any
>>>>> particular such
>>>>> block will execute properly (memory corruption being the extreme
>>>>> case), and if
>>>>> it doesn't run correctly, then it could make things worse (e.g.
>>>>> writing
>>>>> invalid data to a file, corrupting that file). Also, if the stack
>>>>> is not unwound
>>>>> perfectly (as nothrow prevents), then the program's state will become
>>>>> increasingly invalid the farther that the program gets from the
>>>>> throw point,
>>>>> which will increase the chances of cleanup code functioning
>>>>> incorrectly, as
>>>>> any assumptions that they've made about the program state are
>>>>> increasingly
>>>>> likely to be wrong (as well as it being increasingly likely that
>>>>> the variables
>>>>> that they operate on no longer being valid).
>>>>
>>>> Then we should really just abort on Error. What I don't understand
>>>> is the assertion that it isn't safe to unwind the stack on Error and
>>>> yet that catch(Error) clauses should still execute. If the program
>>>> state is really so bad that nothing can be done safely then why
>>>> would the user attempt to log the error condition or anything else?
>>>>
>>>
>>> Yes, either we consider the environement may have been compromised
>>> and it don't even make sense to throw an Error, or we consider this
>>> environement is still consistent, and we have a logic bug. If so,
>>> scope (especially failure) should run when stack is unwinded.
>>>
>>> As need depend on the software (an office suite should try its best
>>> to fail gracefully, a plane autpilot should crash ASAP and give
>>> control back to the pilot), what is needed here is a compiler switch.
>>
>> I think a runtime hook is reasonable instead. But the default case has
>> to be the more permissive case.
>> It's safe to tighten the rules but never to loosen them, since
>> external code will be written assuming the default behavior.
>
> Yes, that's what I had in mind when I renamed topic to "runtime hook for
> Crash on Error". Default should be treat Error as just another type of
> throwable that is logically not caught by catch(Exception).
>

A better alternative is the condition system, inspired by LISP and proposed by H. S. Teoh . I definitively make sense in this case, and a better solution than both your and my proposal.
June 06, 2012
On Wednesday, 6 June 2012 at 09:38:35 UTC, Jonathan M Davis wrote:
> On Wednesday, June 06, 2012 11:13:39 Lars T. Kyllingstad wrote:
>> On Friday, 1 June 2012 at 12:29:27 UTC, Steven Schveighoffer
>> 
>> wrote:
>> > On Fri, 01 Jun 2012 04:48:27 -0400, Dmitry Olshansky
>> > 
>> > <dmitry.olsh@gmail.com> wrote:
>> >> I don't agree that OutOfMemory is critical:
>> >> 	--> make it an exception ?
>> > 
>> > No.  What we need is a non-throwing version of malloc that
>> > returns NULL.  (throwing version can wrap this).  If you want
>> > to throw an exception, then throw it there (or use enforce).
>> 
>> With some sugar:
>> 
>>      auto a = nothrow new Foo; // Returns null on OOM
>> 
>> Then, ordinary new can be disallowed in nothrow code.
>
> But then instead of getting a nice, clear, OutOfMemoryError, you get a
> segfault - and that's assuming that it gets dereferenced anywhere near where
> it's allocated.

"nothrow new" is easily greppable, though.  That would be the first course of action upon getting a segfault.

-Lars

June 06, 2012
On Wednesday, June 06, 2012 19:40:03 Lars T. Kyllingstad wrote:
> On Wednesday, 6 June 2012 at 09:38:35 UTC, Jonathan M Davis wrote:
> > On Wednesday, June 06, 2012 11:13:39 Lars T. Kyllingstad wrote:
> >> On Friday, 1 June 2012 at 12:29:27 UTC, Steven Schveighoffer
> >> 
> >> wrote:
> >> > On Fri, 01 Jun 2012 04:48:27 -0400, Dmitry Olshansky
> >> > 
> >> > <dmitry.olsh@gmail.com> wrote:
> >> >> I don't agree that OutOfMemory is critical:
> >> >> --> make it an exception ?
> >> > 
> >> > No. What we need is a non-throwing version of malloc that
> >> > returns NULL. (throwing version can wrap this). If you want
> >> > to throw an exception, then throw it there (or use enforce).
> >> 
> >> With some sugar:
> >> auto a = nothrow new Foo; // Returns null on OOM
> >> 
> >> Then, ordinary new can be disallowed in nothrow code.
> > 
> > But then instead of getting a nice, clear, OutOfMemoryError,
> > you get a
> > segfault - and that's assuming that it gets dereferenced
> > anywhere near where
> > it's allocated.
> 
> "nothrow new" is easily greppable, though. That would be the first course of action upon getting a segfault.

But unless you got a core dump, you have _no_ idea where in the program the segfault occurred. So, that really isn't helpful.

- Jonathan M Davis
June 06, 2012
On Wednesday, June 06, 2012 19:22:13 Lars T. Kyllingstad wrote:
> On Wednesday, 6 June 2012 at 09:38:35 UTC, Jonathan M Davis wrote:
> > On Wednesday, June 06, 2012 11:13:39 Lars T. Kyllingstad wrote:
> >> On Friday, 1 June 2012 at 12:29:27 UTC, Steven Schveighoffer
> >> 
> >> wrote:
> >> > On Fri, 01 Jun 2012 04:48:27 -0400, Dmitry Olshansky
> >> > 
> >> > <dmitry.olsh@gmail.com> wrote:
> >> >> I don't agree that OutOfMemory is critical:
> >> >> --> make it an exception ?
> >> > 
> >> > No. What we need is a non-throwing version of malloc that
> >> > returns NULL. (throwing version can wrap this). If you want
> >> > to throw an exception, then throw it there (or use enforce).
> >> 
> >> With some sugar:
> >> auto a = nothrow new Foo; // Returns null on OOM
> >> 
> >> Then, ordinary new can be disallowed in nothrow code.
> > 
> > But then instead of getting a nice, clear, OutOfMemoryError,
> > you get a
> > segfault - and that's assuming that it gets dereferenced
> > anywhere near where
> > it's allocated. [...]
> 
> I agree; it would make nothrow an "advanced feature", which kind of sucks.

Which makes the suggestion DOA IMHO.

- Jonathan M Davis
June 06, 2012
On 06/06/2012 07:18 PM, Lars T. Kyllingstad wrote:
> On Wednesday, 6 June 2012 at 13:26:09 UTC, Steven Schveighoffer wrote:
>> On Wed, 06 Jun 2012 05:13:39 -0400, Lars T. Kyllingstad
>> <public@kyllingen.net> wrote:
>>
>>> On Friday, 1 June 2012 at 12:29:27 UTC, Steven Schveighoffer wrote:
>>>> On Fri, 01 Jun 2012 04:48:27 -0400, Dmitry Olshansky
>>>> <dmitry.olsh@gmail.com> wrote:
>>>>
>>>>> I don't agree that OutOfMemory is critical:
>>>>> --> make it an exception ?
>>>>
>>>> No. What we need is a non-throwing version of malloc that returns
>>>> NULL. (throwing version can wrap this). If you want to throw an
>>>> exception, then throw it there (or use enforce).
>>>
>>> With some sugar:
>>>
>>> auto a = nothrow new Foo; // Returns null on OOM
>>>
>>> Then, ordinary new can be disallowed in nothrow code.
>>
>> That doesn't work, new conflates memory allocation with construction.
>> What if the constructor throws?
>
> The constructor would have to be marked nothrow as well. Actually, that
> is currently the case:
>
> class Foo
> {
> this() { }
> }
>
> void bar() nothrow
> {
> auto f = new Foo;
> // Error: constructor this is not nothrow
> // Error: function 'bar' is nothrow yet may throw
> }
>
> The only difference between "new" and "nothrow new" is that the latter
> would return null on allocation failure instead of throwing an
> OutOfMemoryError.
>
> Based on this discussion, I gather that one of the big problems is that
> the compiler is free to elide stack unwinding code for nothrow
> functions, despite the fact that they can in fact throw Errors. One
> solution would therefore be to disallow *all* throwing from nothrow
> functions, including Errors.
>
> Besides OutOfMemoryError, I can only think of two other Errors that
> would make this a hassle: AssertError and RangeError. However, both of
> these signify problems with the program logic, and unwinding the stack
> is probably a bad idea anyway, so why not simply make these abort()?
>
> -Lars
>

In the current implementation, in contract checking may catch AssertErrors and resume the program normally.
June 06, 2012
On Wednesday, 6 June 2012 at 18:11:36 UTC, Jonathan M Davis wrote:
> On Wednesday, June 06, 2012 19:40:03 Lars T. Kyllingstad wrote:
>> On Wednesday, 6 June 2012 at 09:38:35 UTC, Jonathan M Davis wrote:
>> > On Wednesday, June 06, 2012 11:13:39 Lars T. Kyllingstad wrote:
>> >> On Friday, 1 June 2012 at 12:29:27 UTC, Steven Schveighoffer
>> >> 
>> >> wrote:
>> >> > On Fri, 01 Jun 2012 04:48:27 -0400, Dmitry Olshansky
>> >> > 
>> >> > <dmitry.olsh@gmail.com> wrote:
>> >> >> I don't agree that OutOfMemory is critical:
>> >> >> --> make it an exception ?
>> >> > 
>> >> > No. What we need is a non-throwing version of malloc that
>> >> > returns NULL. (throwing version can wrap this). If you want
>> >> > to throw an exception, then throw it there (or use enforce).
>> >> 
>> >> With some sugar:
>> >> auto a = nothrow new Foo; // Returns null on OOM
>> >> 
>> >> Then, ordinary new can be disallowed in nothrow code.
>> > 
>> > But then instead of getting a nice, clear, OutOfMemoryError,
>> > you get a
>> > segfault - and that's assuming that it gets dereferenced
>> > anywhere near where
>> > it's allocated.
>> 
>> "nothrow new" is easily greppable, though. That would be the
>> first course of action upon getting a segfault.
>
> But unless you got a core dump, you have _no_ idea where in the program the
> segfault occurred. So, that really isn't helpful.

Of course it's helpful.  If you were using nothrow new (or malloc, for that matter), you should *always* check its return value.  If you get a segfault, you simply locate all uses of nothrow new in your program and ensure you have checked the return value of each and every one of them.  If it turns out they are all checked, well, then the problem isn't OOM.

-Lars