digitalmars.D.learn - Exception slipping through the catch block?
- helxi (61/61) Nov 08 2018 How does exception work? I am inside a function that calls a
- Adam D. Ruppe (6/7) Nov 08 2018 You caught Exception, but it throws Error. They have separate
- helxi (5/12) Nov 08 2018 Thanks.
- Alex (3/7) Nov 08 2018 Isn't it rather the case, that you have to think about errors
- Bauss (2/18) Nov 08 2018 Just don't have errors thrown.
- Mike Parker (2/18) Nov 08 2018 No, you should never catch Errors. They're separate for a reason.
- Stanislav Blinov (8/14) Nov 08 2018 Never say never :) There are legitimate cases for catching an
- Jonathan M Davis (27/42) Nov 08 2018 Yeah, but basically, the rule of thumb is never. Errors are fatal error
- H. S. Teoh (21/53) Nov 08 2018 Recently I ran into a case where catching Throwable makes sense: I have
- Jonathan M Davis (9/40) Nov 08 2018 Digitalmars-d-learn wrote:
- Neia Neutuladh (25/30) Nov 08 2018 I really don't believe it's that rare or that fraught, most of the time.
- H. S. Teoh (34/43) Nov 08 2018 If we wanted to be *completely* safe according to Walter's definition,
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
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
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: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.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
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
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:Just don't have errors thrown.On Thursday, 8 November 2018 at 15:08:40 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.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
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:No, you should never catch Errors. They're separate for a reason.On Thursday, 8 November 2018 at 15:08:40 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.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
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: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.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
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: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.On Thursday, 8 November 2018 at 15:50:38 UTC, helxi wrote: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.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.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
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: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.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.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.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
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 viaDigitalmars-d-learn 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. - Jonathan M DavisOn 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: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.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.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.
Nov 08 2018
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
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