www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Collateral Exceptions, Throwable vs Exception

reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
TDPL code that should work:

class MyException : Exception
{
    this(string s)
    {
        super(s);
    }
}

void fun()
{
    try
    {
        throw new Exception("thrown from fun");
    }
    finally
    {
        gun(100);
    }
}

void gun(int x)
{
    try
    {

    }
    finally
    {
        if (x > 1 )
        {
            gun(x - 1);
        }
    }
}

import std.stdio, std.conv;

unittest
{
    try
    {
        fun();
    }
    catch (Exception e)
    {
        writeln("Primary exception: ", typeid(e), " ", e);
        while ((e = e.next) !is null)
        {
            writeln("Collateral exception: ", typeid(e), " ", e);
        }
    }
}

void main()
{
}

Errors out:
(49): Error: cannot implicitly convert expression (e.next) of type
object.Throwable to object.Exception

Why would the return type of e.next be Throwable if e is an Exception type?

I've tried casting e.next to type Exception, but that creates an entire mess.
Aug 19 2010
next sibling parent "Vladimir Panteleev" <vladimir thecybershadow.net> writes:
On Thu, 19 Aug 2010 22:43:59 +0300, Andrej Mitrovic  
<andrej.mitrovich gmail.com> wrote:

 TDPL code that should work:
That reminds me, the code on http://digitalmars.com/d/2.0/windows.html doesn't compile as well. You need to change the signature of the exceptionHandler function (replace Exception with Throwable) for it to compile. -- Best regards, Vladimir mailto:vladimir thecybershadow.net
Aug 19 2010
prev sibling next sibling parent reply Sean Kelly <sean invisibleduck.org> writes:
Andrej Mitrovic Wrote:

 TDPL code that should work:
...
 Errors out:
 (49): Error: cannot implicitly convert expression (e.next) of type
object.Throwable to object.Exception
 
 Why would the return type of e.next be Throwable if e is an Exception type?
Because e.next is a Throwable reference inherited from Throwable: class Throwable { Throwable next; } class Error : Throwable {} class Exception : Throwable {} Because collateral exceptions could be both Errors and Exceptions, I don't think the sample code can work exactly as written.
Aug 19 2010
next sibling parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
That goes against the statement in TDPL that user code should never catch
Throwable's, unless absolutely necessary. So in case of collateral
exceptions, we are forced to catch Throwable anyway?

On Thu, Aug 19, 2010 at 10:00 PM, Sean Kelly <sean invisibleduck.org> wrote:

 Andrej Mitrovic Wrote:

 TDPL code that should work:
...
 Errors out:
 (49): Error: cannot implicitly convert expression (e.next) of type
object.Throwable to object.Exception
 Why would the return type of e.next be Throwable if e is an Exception
type? Because e.next is a Throwable reference inherited from Throwable: class Throwable { Throwable next; } class Error : Throwable {} class Exception : Throwable {} Because collateral exceptions could be both Errors and Exceptions, I don't think the sample code can work exactly as written.
Aug 19 2010
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 08/19/2010 03:18 PM, Andrej Mitrovic wrote:
 That goes against the statement in TDPL that user code should never
 catch Throwable's, unless absolutely necessary. So in case of collateral
 exceptions, we are forced to catch Throwable anyway?
Indirectly, yes (I'm not positive about this detail though): if an Exception is in flight but a Throwable was produced as collateral damage, the Throwable won't disobey normal scoping rules etc. so it won't be "special" anymore. Andrei
Aug 19 2010
prev sibling parent Sean Kelly <sean invisibleduck.org> writes:
Andrej Mitrovic Wrote:

 That goes against the statement in TDPL that user code should never catch
 Throwable's, unless absolutely necessary. So in case of collateral
 exceptions, we are forced to catch Throwable anyway?
You should avoid explicitly catching Throwable, but I can see what you're getting at: if a collateral exception is an Error then it should not be ignored. You could always do something like this: catch (Exception e) { for (Throwable t = e; t; t = t.next) { if (cast(Error) t) throw t; // collateral damage critical! writeln(t); } }
Aug 19 2010
prev sibling next sibling parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Actually, would this be safe to use? :

    try
    {
        fun();
    }
    catch (Exception e)
    {
        writeln("Primary exception: ", typeid(e));

        while ((e = cast(Exception)e.next) !is null)
        {
            writeln("Collateral exception: ", typeid(e));
        }
    }



On Thu, Aug 19, 2010 at 10:18 PM, Andrej Mitrovic <
andrej.mitrovich gmail.com> wrote:

 That goes against the statement in TDPL that user code should never catch
 Throwable's, unless absolutely necessary. So in case of collateral
 exceptions, we are forced to catch Throwable anyway?


 On Thu, Aug 19, 2010 at 10:00 PM, Sean Kelly <sean invisibleduck.org>wrote:

 Andrej Mitrovic Wrote:

 TDPL code that should work:
...
 Errors out:
 (49): Error: cannot implicitly convert expression (e.next) of type
object.Throwable to object.Exception
 Why would the return type of e.next be Throwable if e is an Exception
type? Because e.next is a Throwable reference inherited from Throwable: class Throwable { Throwable next; } class Error : Throwable {} class Exception : Throwable {} Because collateral exceptions could be both Errors and Exceptions, I don't think the sample code can work exactly as written.
Aug 19 2010
parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
Andrej Mitrovic wrote:
 Actually, would this be safe to use? :
 
     try
     {
         fun();
     }
     catch (Exception e)
     {
         writeln("Primary exception: ", typeid(e));
        
         while ((e = cast(Exception)e.next) !is null)
         {
             writeln("Collateral exception: ", typeid(e));
         } 
     }
 
I'm no expert, but why shouldn't? I'd propose a correction though: while ((e = e.next) !is null) { if (cast(Exception)e) { writeln(/*whatever*/); } }
Aug 19 2010
parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
The problem is in the e =3D e.next assignment, not in the code block below =
it.

On Thu, Aug 19, 2010 at 10:42 PM, Stanislav Blinov
<stanislav.blinov gmail.com> wrote:
 Andrej Mitrovic wrote:
 Actually, would this be safe to use? :

 =A0 =A0try
 =A0 =A0{
 =A0 =A0 =A0 =A0fun();
 =A0 =A0}
 =A0 =A0catch (Exception e)
 =A0 =A0{
 =A0 =A0 =A0 =A0writeln("Primary exception: ", typeid(e));
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 while ((e =3D cast(Exception)e.next) !is nul=
l)
 =A0 =A0 =A0 =A0{
 =A0 =A0 =A0 =A0 =A0 =A0writeln("Collateral exception: ", typeid(e));
 =A0 =A0 =A0 =A0} =A0 =A0 }
I'm no expert, but why shouldn't? I'd propose a correction though: while ((e =3D e.next) !is null) { =A0 =A0if (cast(Exception)e) =A0 =A0{ =A0 =A0 =A0 =A0writeln(/*whatever*/); =A0 =A0} }
Aug 19 2010
parent Stanislav Blinov <stanislav.blinov gmail.com> writes:
Andrej Mitrovic wrote:
 The problem is in the e = e.next assignment, not in the code block below it.
 
 On Thu, Aug 19, 2010 at 10:42 PM, Stanislav Blinov
 <stanislav.blinov gmail.com> wrote:
 Andrej Mitrovic wrote:
 Actually, would this be safe to use? :

    try
    {
        fun();
    }
    catch (Exception e)
    {
        writeln("Primary exception: ", typeid(e));
               while ((e = cast(Exception)e.next) !is null)
        {
            writeln("Collateral exception: ", typeid(e));
        }     }
I'm no expert, but why shouldn't? I'd propose a correction though: while ((e = e.next) !is null) { if (cast(Exception)e) { writeln(/*whatever*/); } }
*slap in the head* Silly me.
Aug 19 2010
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Damn gmail and formatting. I hope that last message wasn't garbled.

On Thu, Aug 19, 2010 at 10:25 PM, Andrej Mitrovic
<andrej.mitrovich gmail.com> wrote:
 Actually, would this be safe to use? :
Aug 19 2010
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 08/19/2010 03:00 PM, Sean Kelly wrote:
 Andrej Mitrovic Wrote:

 TDPL code that should work:
...
 Errors out:
 (49): Error: cannot implicitly convert expression (e.next) of type
object.Throwable to object.Exception

 Why would the return type of e.next be Throwable if e is an Exception type?
Because e.next is a Throwable reference inherited from Throwable: class Throwable { Throwable next; } class Error : Throwable {} class Exception : Throwable {} Because collateral exceptions could be both Errors and Exceptions, I don't think the sample code can work exactly as written.
Sean's right. And thanks Sean and Walter for implementing the exceptions spec properly, I believe it's very important to the success of D. Andrej... sounds like another errata entry, please :o). Andrei
Aug 19 2010
parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Yes, I've added it as Throwable. But there's one more problem with
your code, this:

catch (Throwable e)     // have to use Throwable for collateral exceptions
                            // or maybe use a cast like below
    {
        writeln("Primary exception: ", typeid(e), " ", e);

        while ((e =3D e.next) !is null)
        {
            writeln("Collateral exception: ", typeid(e), " ", e);
        }
    }

Will output ~5000 lines of "Exception .." stuff. Oh and in the book it
looks like you're counting from 100 to 1 (for the throws from gun),
which makes sense. Yet I'm getting back output from 1 to 100. Not sure
what's going on there..

On Thu, Aug 19, 2010 at 10:32 PM, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:
 On 08/19/2010 03:00 PM, Sean Kelly wrote:
 Andrej Mitrovic Wrote:

 TDPL code that should work:
...
 Errors out:
 (49): Error: cannot implicitly convert expression (e.next) of type
 object.Throwable to object.Exception

 Why would the return type of e.next be Throwable if e is an Exception
 type?
Because e.next is a Throwable reference inherited from Throwable: class Throwable { =A0 =A0 Throwable next; } class Error : Throwable {} class Exception : Throwable {} Because collateral exceptions could be both Errors and Exceptions, I don=
't
 think the sample code can work exactly as written.
Sean's right. And thanks Sean and Walter for implementing the exceptions spec properly, I believe it's very important to the success of D. Andrej... sounds like another errata entry, please :o). Andrei
Aug 19 2010
next sibling parent reply Sean Kelly <sean invisibleduck.org> writes:
Andrej Mitrovic Wrote:

 Yes, I've added it as Throwable. But there's one more problem with
 your code, this:
 
 catch (Throwable e)     // have to use Throwable for collateral exceptions
                             // or maybe use a cast like below
     {
         writeln("Primary exception: ", typeid(e), " ", e);
 
         while ((e = e.next) !is null)
         {
             writeln("Collateral exception: ", typeid(e), " ", e);
         }
     }
 
 Will output ~5000 lines of "Exception .." stuff. Oh and in the book it
 looks like you're counting from 100 to 1 (for the throws from gun),
 which makes sense. Yet I'm getting back output from 1 to 100. Not sure
 what's going on there..
Are these stack trace lines or actual distinct exceptions?
Aug 19 2010
parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
I think I found the problem. e contains all the collateral exceptions
(in a linked list according to TDPL), and printing out e will print
out each and every collateral exception.

So if that's the case, there's no point to using that while loop. But
if we can't print out only one exception from a linked list, then what
is the point of the .next property?

This prints out the all the collateral exceptions:

    try
    {
        fun();
    }
    catch (Throwable e)
    {
        writeln(e);
    }



On Thu, Aug 19, 2010 at 11:11 PM, Sean Kelly <sean invisibleduck.org> wrote=
:
 Andrej Mitrovic Wrote:

 Yes, I've added it as Throwable. But there's one more problem with
 your code, this:

 catch (Throwable e) =A0 =A0 // have to use Throwable for collateral exce=
ptions
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 // or maybe use =
a cast like below
 =A0 =A0 {
 =A0 =A0 =A0 =A0 writeln("Primary exception: ", typeid(e), " ", e);

 =A0 =A0 =A0 =A0 while ((e =3D e.next) !is null)
 =A0 =A0 =A0 =A0 {
 =A0 =A0 =A0 =A0 =A0 =A0 writeln("Collateral exception: ", typeid(e), " "=
, e);
 =A0 =A0 =A0 =A0 }
 =A0 =A0 }

 Will output ~5000 lines of "Exception .." stuff. Oh and in the book it
 looks like you're counting from 100 to 1 (for the throws from gun),
 which makes sense. Yet I'm getting back output from 1 to 100. Not sure
 what's going on there..
Are these stack trace lines or actual distinct exceptions?
Aug 19 2010
parent reply Sean Kelly <sean invisibleduck.org> writes:
The .next property is to allow chaining.  However, it may be that the current
behavior of toString is incorrect, or that a new function should be added that
only returns a string describing the current exception and not the entire chain.

Andrej Mitrovic Wrote:

 I think I found the problem. e contains all the collateral exceptions
 (in a linked list according to TDPL), and printing out e will print
 out each and every collateral exception.
 
 So if that's the case, there's no point to using that while loop. But
 if we can't print out only one exception from a linked list, then what
 is the point of the .next property?
 
 This prints out the all the collateral exceptions:
 
     try
     {
         fun();
     }
     catch (Throwable e)
     {
         writeln(e);
     }
 
 
 
 On Thu, Aug 19, 2010 at 11:11 PM, Sean Kelly <sean invisibleduck.org> wrote:
 Andrej Mitrovic Wrote:

 Yes, I've added it as Throwable. But there's one more problem with
 your code, this:

 catch (Throwable e)     // have to use Throwable for collateral exceptions
                             // or maybe use a cast like below
     {
         writeln("Primary exception: ", typeid(e), " ", e);

         while ((e = e.next) !is null)
         {
             writeln("Collateral exception: ", typeid(e), " ", e);
         }
     }

 Will output ~5000 lines of "Exception .." stuff. Oh and in the book it
 looks like you're counting from 100 to 1 (for the throws from gun),
 which makes sense. Yet I'm getting back output from 1 to 100. Not sure
 what's going on there..
Are these stack trace lines or actual distinct exceptions?
Aug 19 2010
parent reply Mike Parker <aldacron gmail.com> writes:
Sean Kelly wrote:
 The .next property is to allow chaining.  However, it may be that the current
behavior of toString is incorrect, or that a new function should be added that
only returns a string describing the current exception and not the entire chain.
 
IMO, it should be the opposite. I noticed the current behavior the other day and was surprised. I would expect toString to return only the message from the Throwable I called it on. A concatenated string of messages from the whole chain would be the special case to be separated into a convenience function. Then again, I don't see the convenience function being necessary, as it's trivial to roll your own for the cases when you want it -- which is what I was doing when I discovered the current behavior and, like Andrej, was confronted by a long list of exception messages.
Aug 19 2010
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 08/19/2010 10:14 PM, Mike Parker wrote:
 Sean Kelly wrote:
 The .next property is to allow chaining. However, it may be that the
 current behavior of toString is incorrect, or that a new function
 should be added that only returns a string describing the current
 exception and not the entire chain.
IMO, it should be the opposite. I noticed the current behavior the other day and was surprised. I would expect toString to return only the message from the Throwable I called it on. A concatenated string of messages from the whole chain would be the special case to be separated into a convenience function. Then again, I don't see the convenience function being necessary, as it's trivial to roll your own for the cases when you want it -- which is what I was doing when I discovered the current behavior and, like Andrej, was confronted by a long list of exception messages.
My opinion: Throwable should have a property called printingDepth or something. It should default to e.g. 3 (or conservatively at 1) and limit the number of exceptions in a chain formatted in toString(). Andrei
Aug 19 2010
parent Mike Parker <aldacron gmail.com> writes:
Andrei Alexandrescu wrote:
 On 08/19/2010 10:14 PM, Mike Parker wrote:
 Sean Kelly wrote:
 The .next property is to allow chaining. However, it may be that the
 current behavior of toString is incorrect, or that a new function
 should be added that only returns a string describing the current
 exception and not the entire chain.
IMO, it should be the opposite. I noticed the current behavior the other day and was surprised. I would expect toString to return only the message from the Throwable I called it on. A concatenated string of messages from the whole chain would be the special case to be separated into a convenience function. Then again, I don't see the convenience function being necessary, as it's trivial to roll your own for the cases when you want it -- which is what I was doing when I discovered the current behavior and, like Andrej, was confronted by a long list of exception messages.
My opinion: Throwable should have a property called printingDepth or something. It should default to e.g. 3 (or conservatively at 1) and limit the number of exceptions in a chain formatted in toString(). Andrei
Yes, that's better. +1.
Aug 20 2010
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 08/19/2010 03:45 PM, Andrej Mitrovic wrote:
 Yes, I've added it as Throwable. But there's one more problem with
 your code, this:

 catch (Throwable e)     // have to use Throwable for collateral exceptions
                              // or maybe use a cast like below
      {
          writeln("Primary exception: ", typeid(e), " ", e);

          while ((e = e.next) !is null)
          {
              writeln("Collateral exception: ", typeid(e), " ", e);
          }
      }

 Will output ~5000 lines of "Exception .." stuff. Oh and in the book it
 looks like you're counting from 100 to 1 (for the throws from gun),
 which makes sense. Yet I'm getting back output from 1 to 100. Not sure
 what's going on there..
The reverse ordering seems to be a bug in the implementation. Andrei
Aug 19 2010
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 19 Aug 2010 18:48:15 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 On 08/19/2010 03:45 PM, Andrej Mitrovic wrote:
 Yes, I've added it as Throwable. But there's one more problem with
 your code, this:

 catch (Throwable e)     // have to use Throwable for collateral  
 exceptions
                              // or maybe use a cast like below
      {
          writeln("Primary exception: ", typeid(e), " ", e);

          while ((e = e.next) !is null)
          {
              writeln("Collateral exception: ", typeid(e), " ", e);
          }
      }

 Will output ~5000 lines of "Exception .." stuff. Oh and in the book it
 looks like you're counting from 100 to 1 (for the throws from gun),
 which makes sense. Yet I'm getting back output from 1 to 100. Not sure
 what's going on there..
The reverse ordering seems to be a bug in the implementation.
Don't have the book in front of me, but it is important that collateral exceptions be ordered in reverse order of insertion, otherwise, adding a collateral exception involves a linear search to the end of the linked list. -Steve
Aug 23 2010
prev sibling parent Kagamin <spam here.lot> writes:
Andrei Alexandrescu Wrote:

 Indirectly, yes (I'm not positive about this detail though): if an 
 Exception is in flight but a Throwable was produced as collateral 
 damage, the Throwable won't disobey normal scoping rules etc. so it 
 won't be "special" anymore.
Can it be fixed with compiler calling, say, Throwable Runtime.compose(Throwable flying, Throwable thrown) ?
Aug 19 2010