www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Exception slipping through the catch block?

reply helxi <brucewayneshit gmail.com> writes:
How does exception work? I am inside a function that calls a 
constructor. Inside the constructor, an exception is thrown. 
However even though I have wrapped the body of the function 
inside a try/catch block, the program crashes from inside that 
constructor. Shouldn't the catch block in the function catch the 
exception?

Picture should clear it up a little bit:
https://i.imgur.com/3zinoZq.png

    // ui.d
    103	        deviceCombo.addOnChanged(delegate 
void(ComboBoxText _) {
    104	            try {
    105	                device = new 
Device(deviceCombo.getActiveText()); // <-- call sight
    106	                auto status = sanityCheck(device, 
fcb.getFilename);
    107	                if (status.pass)
    108	                    
triggerHboxRevealer.setRevealChild(true);
    109	                else {
    110	                    
deviceCombo.setTooltipText(status.reason);
    111	                    
triggerHboxRevealer.setRevealChild(false);
    112	                }
    113	            }
    114	            catch (Exception e) {
    115	                deviceCombo.setTooltipText("Cannot read 
device");
    116	                triggerHboxRevealer.setRevealChild(false);
    117	            }
    118	        });


     // backend.d
     34	    this(in string lsblkLine) {
     35	        auto lineSplit = lsblkLine.split(" ");
     36	        name = lineSplit.front();
     37	        sizePretty = lineSplit.back();
     38	        foreach (str; lineSplit.dropOne().dropBackOne()) 
// <-- throws exception
     39	            model ~= str ~ " ";
     40	        summary = format("%s\t%s\t%s", name, model, 
sizePretty);
     41	    }

// error message
core.exception.AssertError /usr/include/dlang/dmd/std/range/primitives.d(2340):
Assertion failure
----------------
??:? _d_assertp [0x4e81ee29]
/usr/include/dlang/dmd/std/range/primitives.d:2340 pure nothrow 
 nogc  safe void 
std.range.primitives.popBack!(immutable(char)[]).popBack(ref 
immutable(char)[][]) [0x4e5e417e]
/usr/include/dlang/dmd/std/range/package.d:3190 pure nothrow 
 nogc  safe immutable(char)[][] 
std.range.dropBackOne!(immutable(char)[][]).dropBackOne(immutable(char)[][])
[0x4e5e41d4]
source/backend.d:38 backend.Device 
backend.Device.__ctor(const(immutable(char)[])) [0x4e5e8e6d]
source/ui.d:105 void 
ui.PrimaryWindow.__ctor(gtk.Application.Application).__dgliteral4(gtk.ComboB
xText.ComboBoxText) [0x4e5ea17f]
../../../../.dub/packages/gtk-d-3.8.3/gtk-d/generated/gtkd/go
ject/DClosure.d:135 extern (C) void
gobject.DClosure.DClosure.d_closure_marshal!(void
delegate(gtk.ComboBoxText.ComboBoxText)).d_closure_marshal(gobjec
.c.types.GClosure*, gobject.c.types.GValue*, uint, gobject.c.types.GValue*,
void*, void*) [0x4e7bf095]
??:? g_closure_invoke [0x4f9573d4]
Program exited with code 1
Nov 08 2018
next sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Thursday, 8 November 2018 at 15:08:40 UTC, helxi wrote:
 Shouldn't the catch block in the function catch the exception?
You caught Exception, but it throws Error. They have separate inheritance trees. The common ancestor is actually Throwable, though note that there is no guarantee that Errors actually unwind the stack; with certain compile flags they might just abort the program.
Nov 08 2018
parent reply helxi <brucewayneshit gmail.com> writes:
On Thursday, 8 November 2018 at 15:41:11 UTC, Adam D. Ruppe wrote:
 On Thursday, 8 November 2018 at 15:08:40 UTC, helxi wrote:
 Shouldn't the catch block in the function catch the exception?
You caught Exception, but it throws Error. They have separate inheritance trees. The common ancestor is actually Throwable, though note that there is no guarantee that Errors actually unwind the stack; with certain compile flags they might just abort the program.
Thanks. Although it's pretty frustrating, isn't it? Now not only I have to think about catching exceptions but also about Errors, and have no guarantee that I have everything under control.
Nov 08 2018
next sibling parent Alex <sascha.orlov gmail.com> writes:
On Thursday, 8 November 2018 at 15:50:38 UTC, helxi wrote:
 Thanks.

 Although it's pretty frustrating, isn't it? Now not only I have 
 to think about catching exceptions but also about Errors, and 
 have no guarantee that I have everything under control.
Isn't it rather the case, that you have to think about errors while developing? ;)
Nov 08 2018
prev sibling next sibling parent Bauss <jj_1337 live.dk> writes:
On Thursday, 8 November 2018 at 15:50:38 UTC, helxi wrote:
 On Thursday, 8 November 2018 at 15:41:11 UTC, Adam D. Ruppe 
 wrote:
 On Thursday, 8 November 2018 at 15:08:40 UTC, helxi wrote:
 Shouldn't the catch block in the function catch the exception?
You caught Exception, but it throws Error. They have separate inheritance trees. The common ancestor is actually Throwable, though note that there is no guarantee that Errors actually unwind the stack; with certain compile flags they might just abort the program.
Thanks. Although it's pretty frustrating, isn't it? Now not only I have to think about catching exceptions but also about Errors, and have no guarantee that I have everything under control.
Just don't have errors thrown.
Nov 08 2018
prev sibling parent reply Mike Parker <aldacron gmail.com> writes:
On Thursday, 8 November 2018 at 15:50:38 UTC, helxi wrote:
 On Thursday, 8 November 2018 at 15:41:11 UTC, Adam D. Ruppe 
 wrote:
 On Thursday, 8 November 2018 at 15:08:40 UTC, helxi wrote:
 Shouldn't the catch block in the function catch the exception?
You caught Exception, but it throws Error. They have separate inheritance trees. The common ancestor is actually Throwable, though note that there is no guarantee that Errors actually unwind the stack; with certain compile flags they might just abort the program.
Thanks. Although it's pretty frustrating, isn't it? Now not only I have to think about catching exceptions but also about Errors, and have no guarantee that I have everything under control.
No, you should never catch Errors. They're separate for a reason.
Nov 08 2018
parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Thursday, 8 November 2018 at 16:13:55 UTC, Mike Parker wrote:
 On Thursday, 8 November 2018 at 15:50:38 UTC, helxi wrote:
 Although it's pretty frustrating, isn't it? Now not only I 
 have to think about catching exceptions but also about Errors, 
 and have no guarantee that I have everything under control.
No, you should never catch Errors. They're separate for a reason.
Never say never :) There are legitimate cases for catching an Error or even a Throwable (for example, error propagation in a multi-threaded environment). However, this is not one of such cases. helxi, an AssertError means there's a problem with your code, it needs to be dealt with by fixing the code, not swallowing the Error.
Nov 08 2018
next sibling parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Thursday, November 8, 2018 10:55:45 AM MST Stanislav Blinov via 
Digitalmars-d-learn wrote:
 On Thursday, 8 November 2018 at 16:13:55 UTC, Mike Parker wrote:
 On Thursday, 8 November 2018 at 15:50:38 UTC, helxi wrote:
 Although it's pretty frustrating, isn't it? Now not only I
 have to think about catching exceptions but also about Errors,
 and have no guarantee that I have everything under control.
No, you should never catch Errors. They're separate for a reason.
Never say never :) There are legitimate cases for catching an Error or even a Throwable (for example, error propagation in a multi-threaded environment). However, this is not one of such cases.
Yeah, but basically, the rule of thumb is never. Errors are fatal error conditions which are supposed to terminate the program, and programs should not be trying to recover from them. No one should be attempting to catch them unless they know what they're doing, which honestly, probably isn't going to be very many people for something like this. Exceptions are for error conditions which are potentially recoverable. Program input or environmental state was bad (e.g. a missing file). They aren't necessarily indicative of bugs in the program, and the program can potentially recover from them and continue to function perfectly fine. Errors on the other hand, are used to indicate actual bugs in the program or fatal conditions with resources which cannot be recovered from (e.g. the GC running out of memory). They are intended simply to be informative and not to be caught. The stack is not necessarily properly unwound when they are thrown. Full exception handling code is not necessarily even in place when they are thrown, and they can be thrown from nothrow code. By definition, your program is in an invalid state when you catch an Error, and catching an Error to do much of anything is dangerous.
 helxi, an AssertError means there's a problem with your code, it
 needs to be dealt with by fixing the code, not swallowing the
 Error.
Specifically, AssertError means that either an assertion failed or something simulating an assertion failed (such as std.exception.assertThrown), so it's something in the code that quite specifically indicates that there's a bug in the code and that a condition that must be true for the code to be in a valid state was false. So, whatever it reported should be tracked down and fixed. In this particular case, it looks like a piece of code called dropBackOne on an empty range. - Jonathan M Davis
Nov 08 2018
prev sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, Nov 08, 2018 at 01:28:47PM -0700, Jonathan M Davis via
Digitalmars-d-learn wrote:
 On Thursday, November 8, 2018 10:55:45 AM MST Stanislav Blinov via 
 Digitalmars-d-learn wrote:
 On Thursday, 8 November 2018 at 16:13:55 UTC, Mike Parker wrote:
[...]
 No, you should never catch Errors. They're separate for a
 reason.
Never say never :) There are legitimate cases for catching an Error or even a Throwable (for example, error propagation in a multi-threaded environment). However, this is not one of such cases.
Yeah, but basically, the rule of thumb is never. Errors are fatal error conditions which are supposed to terminate the program, and programs should not be trying to recover from them. No one should be attempting to catch them unless they know what they're doing, which honestly, probably isn't going to be very many people for something like this.
Recently I ran into a case where catching Throwable makes sense: I have an Android application where the main code interfacing with the Android OS is written in Java, but program logic is written in D, called via JNI. Since the JVM obviously cannot handle D exceptions, any unhandled D exception that makes it past the JNI boundary would cause the application to crash. So what I did was to have the JNI interfacing code (on the D side) catch *everything*, i.e., Throwable, marshall the error message into a Java object, then send it via JNI back to the Java code that then displays the error message before aborting the application. Extremely valuable in debugging, since otherwise I'd have to extract the stacktrace from the system logs, which is a pain.
 Exceptions are for error conditions which are potentially recoverable.
 Program input or environmental state was bad (e.g. a missing file).
 They aren't necessarily indicative of bugs in the program, and the
 program can potentially recover from them and continue to function
 perfectly fine.
 
 Errors on the other hand, are used to indicate actual bugs in the
 program or fatal conditions with resources which cannot be recovered
 from (e.g. the GC running out of memory). They are intended simply to
 be informative and not to be caught. The stack is not necessarily
 properly unwound when they are thrown. Full exception handling code is
 not necessarily even in place when they are thrown, and they can be
 thrown from nothrow code. By definition, your program is in an invalid
 state when you catch an Error, and catching an Error to do much of
 anything is dangerous.
[...] Agreed in general. In my case, though, being able to catch an Error makes it possible to pass on the error message to the Java code and displayed on the screen, instead of the whole thing crashing and burning with no indication of what went wrong. T -- Bare foot: (n.) A device for locating thumb tacks on the floor.
Nov 08 2018
prev sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Thursday, November 8, 2018 2:34:38 PM MST H. S. Teoh via Digitalmars-d-
learn wrote:
 On Thu, Nov 08, 2018 at 01:28:47PM -0700, Jonathan M Davis via 
Digitalmars-d-learn wrote:
 On Thursday, November 8, 2018 10:55:45 AM MST Stanislav Blinov via

 Digitalmars-d-learn wrote:
 On Thursday, 8 November 2018 at 16:13:55 UTC, Mike Parker wrote:
[...]
 No, you should never catch Errors. They're separate for a
 reason.
Never say never :) There are legitimate cases for catching an Error or even a Throwable (for example, error propagation in a multi-threaded environment). However, this is not one of such cases.
Yeah, but basically, the rule of thumb is never. Errors are fatal error conditions which are supposed to terminate the program, and programs should not be trying to recover from them. No one should be attempting to catch them unless they know what they're doing, which honestly, probably isn't going to be very many people for something like this.
Recently I ran into a case where catching Throwable makes sense: I have an Android application where the main code interfacing with the Android OS is written in Java, but program logic is written in D, called via JNI. Since the JVM obviously cannot handle D exceptions, any unhandled D exception that makes it past the JNI boundary would cause the application to crash. So what I did was to have the JNI interfacing code (on the D side) catch *everything*, i.e., Throwable, marshall the error message into a Java object, then send it via JNI back to the Java code that then displays the error message before aborting the application. Extremely valuable in debugging, since otherwise I'd have to extract the stacktrace from the system logs, which is a pain.
You ran into one of the rare cases where it makes sense catch an Error or a Throwable, and you're one of the few people who understands the situation well enough to deal with it properly. The vast majority of D programmers don't. Certainly, anyone who has to ask about the differences between Throwable, Error, and Exception doesn't. - Jonathan M Davis
Nov 08 2018
parent reply Neia Neutuladh <neia ikeran.org> writes:
On Thu, 08 Nov 2018 17:27:40 -0700, Jonathan M Davis wrote:
 You ran into one of the rare cases where it makes sense catch an Error
 or a Throwable, and you're one of the few people who understands the
 situation well enough to deal with it properly. The vast majority of D
 programmers don't. Certainly, anyone who has to ask about the
 differences between Throwable, Error, and Exception doesn't.
I really don't believe it's that rare or that fraught, most of the time. If an error happens, it's better for most programs to try to leave behind enough artifacts for you to start debugging the problem. Most of the time, a stacktrace on stderr is good enough and still possible. And that's what the runtime does. This isn't, strictly speaking, safe. Your program detected an error, and in Walter's book, that means you can't trust the program to do *anything*. Unwinding the stack, formatting a stacktrace, writing to stderr, this is all Dangerous Stuff You Shouldn't Be Doing. Even sending your process sigabrt to trigger a core dump is potentially dangerous. But the utility is greater than the likely harm, which is why druntime will unwind the stack, format a stacktrace, and write to stderr when an Error is thrown and not caught. Similarly, if your program logs to a file normally and you use that logfile for most of your debugging, it's sensible to try to log the error to that file and then exit. Especially if, like with H. S. Teoh's case, it's difficult or impossible for you to access the process's stderr. This is still *slightly* fraught. If your logging system is responsible for that Error being thrown, you probably won't succeed in logging the Error. If you have custom Error classes that do weird things, those things are more likely to break. Your application might be out of memory, so the allocations involved in logging could also fail. So, moderately fraught, but I don't think it's "here be dragons" any more than usual.
Nov 08 2018
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Nov 09, 2018 at 01:14:08AM +0000, Neia Neutuladh via
Digitalmars-d-learn wrote:
[...]
 This isn't, strictly speaking, safe. Your program detected an error,
 and in Walter's book, that means you can't trust the program to do
 *anything*.  Unwinding the stack, formatting a stacktrace, writing to
 stderr, this is all Dangerous Stuff You Shouldn't Be Doing. Even
 sending your process sigabrt to trigger a core dump is potentially
 dangerous.
If we wanted to be *completely* safe according to Walter's definition, we'd have to completely decouple Error handling from the process that reaches a `throw new Error(...)` (or any of its derived classes thereof). You'd have a monitor running in a completely separate process from the main program, that regularly checks up on the health of the latter. When the latter encounters an Error, it should immediately execute a 'hlt' instruction, or enter a 1-instruction empty infinite loop, and the monitor process (which, presumably, has not had its logic compromised) would detect this and kill / restart / extract core dump info from the failed process. Anything inside the failed process itself would not be trustworthy. (Conceivably, for example, a remote attacker may have triggered a buffer overflow that overwrote, say, the variables holding the file descriptors for the logfile, so as to redirect any logged messages to a publicly-readable place, and may have further corrupted the logging module's state so that instead of writing the stacktrace or error message to the file, it writes the contents of /etc/passwd or some such sensitive file instead. All bets are off once an assertion fails. Or worse yet, the assertion failure may have been caused by some internal code gone wrong that has overwritten some critical function pointers so that calling a dynamically-linked druntime function accidentally ends up invoking the function to increase laser output intensity 100x instead, thereby killing the patient undergoing surgery by the machine controlled by the code.)
 But the utility is greater than the likely harm, which is why druntime
 will unwind the stack, format a stacktrace, and write to stderr when
 an Error is thrown and not caught.
Yes, the utility outweighs the potential dangers, especially during development when you want maximum information about what went wrong, so it makes sense to catch Errors for that purpose. Catching Errors and then continuing to execute application logic as if nothing happened, though, is a big no-no. T -- Caffeine underflow. Brain dumped.
Nov 08 2018