digitalmars.D - assert(0) behavior
- Steven Schveighoffer (28/28) Aug 03 2015 If you compile and run the following code, what happens?
- Dicebot (6/7) Aug 03 2015 Because all asserts must be completely removed in -release
- Steven Schveighoffer (10/13) Aug 03 2015 1. They aren't removed, they are replaced with a nearly useless segfault...
- Dicebot (11/28) Aug 03 2015 Now, they are completely removed. There is effectively no
- Steven Schveighoffer (11/40) Aug 03 2015 I actually don't care if stack is unwound (it's not guaranteed by
- Walter Bright (7/10) Aug 03 2015 Not useless at all:
- Steven Schveighoffer (51/64) Aug 03 2015 You can run some code that is fresh, i.e. don't *continue* running
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (9/11) Aug 03 2015 Agreed.
- Walter Bright (4/8) Aug 03 2015 No.
- Steven Schveighoffer (5/16) Aug 04 2015 Any assert(0) in druntime or phobos with a message printed is an error.
- Steven Schveighoffer (3/6) Aug 04 2015 First stab: https://github.com/D-Programming-Language/druntime/pull/1337
- Walter Bright (4/6) Aug 04 2015 When you're debugging phobos, you aren't compiling in release mode.
- deadalnix (5/13) Aug 04 2015 Would you be of the opinion that assert should be a statement
- deadalnix (2/6) Aug 04 2015 Ping ? Walter ?
- Walter Bright (3/8) Aug 04 2015 If you want it as its own statement, write it that way. I don't see a ne...
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (2/16) Aug 05 2015 I suppose he's asking as maintainer of SDC.
- Steven Schveighoffer (14/21) Aug 04 2015 Unless you aren't debugging it, but an assert is still triggering. I
- Steven Schveighoffer (5/9) Aug 04 2015 In that vein, would it be possible to make this a warning? i.e. if you
- Jonathan M Davis (25/36) Aug 04 2015 I fail to see why it's a problem if assert(0) has a message. It's
- Steven Schveighoffer (19/33) Aug 04 2015 Seeing an assert with a message gives the impression that you will see
- Jonathan M Davis (23/67) Aug 04 2015 It stills indicate why the assertion is there, which can be
- Steven Schveighoffer (21/24) Aug 04 2015 For instance:
- Walter Bright (2/6) Aug 04 2015 That's a bug in core/time.d, NOT a bug with asserts.
- Walter Bright (2/12) Aug 04 2015 https://issues.dlang.org/show_bug.cgi?id=14870
- Jonathan M Davis (10/40) Aug 04 2015 It was never expected that a user to see any of those messages
- Jonathan M Davis (9/56) Aug 04 2015 Maybe in this case, it would have made more sense to simply throw
- Walter Bright (3/4) Aug 04 2015 fprintf(stderr, "You left the headlights on.");
- deadalnix (4/8) Aug 04 2015 It seems that on american car, you bip as annoyingly as possible
- Walter Bright (4/6) Aug 04 2015 It turns out you can make quite understandable speech simply by connecti...
- Steven Schveighoffer (5/9) Aug 04 2015 Going under the assumption that the program isn't fully "with it", I
- Walter Bright (3/5) Aug 04 2015 writeln("Somebody forgot to turn off the water.");
- Meta (3/7) Aug 03 2015 Why not just " stderr.writeln(errMsg); assert(0);"?
- Walter Bright (7/19) Aug 03 2015 The answer is the code is misusing asserts to check for environmental er...
- Steven Schveighoffer (8/10) Aug 04 2015 enforce isn't available in druntime. In this case, we don't want to
- Walter Bright (3/5) Aug 04 2015 Print an error message and exit via abort() or whatever. No need to gene...
- Joseph Rushton Wakeling (6/8) Aug 05 2015 Only if you compile in -release mode. Without wanting to get
- Jonathan M Davis (15/23) Aug 05 2015 That's arguably true, but historically, release builds are where
- Iain Buclaw via Digitalmars-d (12/27) Aug 05 2015 I think as a guideline, there are really only be two places where using ...
- Jonathan M Davis (29/38) Aug 05 2015 I think that it makes perfect sense to use assertions inside of
- Steven Schveighoffer (10/16) Aug 05 2015 No, the reason is, if you want to print something before halting, and
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (3/10) Aug 03 2015 It isn't free. The point of having unreachable as primitive is
- Dicebot (5/17) Aug 03 2015 "free compared to throwing exception" would be a better wording
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (22/24) Aug 03 2015 Yes. I think a lot of this confusion could have been avoided by
- Walter Bright (5/6) Aug 04 2015 Sure it does. If your user ever sees an assert failure message, your pro...
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (12/19) Aug 04 2015 The input/code distinction is too simplistic.
- John Colvin (6/27) Aug 04 2015 I'm 90% on Walter's side here as he's right for the majority of
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (20/24) Aug 04 2015 Well, the principled difference is that asserts are
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (3/4) Aug 05 2015 Err... Verification, usually not validation.
- Dicebot (3/3) Aug 03 2015 General advice - simply don't ever use -release unless you are
- Steven Schveighoffer (3/6) Aug 03 2015 So in other words, only release code that has no bugs. Got it ;)
- Nick Sabalausky (16/19) Aug 04 2015 This is very true. I never disable asserts or bounds checking for
- Dicebot (7/30) Aug 04 2015 Recently we had quite a lengthy discussion at work regarding
- Walter Bright (3/8) Aug 04 2015 That's worthy of more than just a newsgroup post. How about an article o...
If you compile and run the following code, what happens? void main() { assert(0, "error message"); } answer: it depends. On OSX, if you compile this like so: dmd testassert.d ./testassert You get this message + stack trace: core.exception.AssertError testassert.d(3): error message Not bad. But assert(0) is special in that it is always enabled, even in release mode. So let's try that: dmd -release testassert.d ./testassert Segmentation fault: 11 WAT. The explanation is, assert(0) is translated in release mode to a HLT instruction. on X86, this results in a segfault. But a seg fault is tremendously less useful. Let's say you are running a 10k line program, and you see this. Compared with seeing the assert message and stack trace, this is going to cause hours of extra debugging. Why do we do this? I'm really not sure. Note that "error message" is essentially not used if we have a seg fault. Throwing an assert error shouldn't cause any issues with stack unwinding performance, since this can be done inside a nothrow function. And when you throw an AssertError, it shouldn't be caught anyway. Why can't assert(0) throw an assert error in release mode instead of segfaulting? What would it cost to do this instead? -Steve
Aug 03 2015
On Monday, 3 August 2015 at 14:34:52 UTC, Steven Schveighoffer wrote:Why do we do this?Because all asserts must be completely removed in -release Yet assert(0) effectively mean "unreachable code" (it is actually defined that way in spec) and thus it is possible to ensure extra "free" bit of safety by crashing the app.
Aug 03 2015
On 8/3/15 11:18 AM, Dicebot wrote:On Monday, 3 August 2015 at 14:34:52 UTC, Steven Schveighoffer wrote:1. They aren't removed, they are replaced with a nearly useless segfault. 2. If we are going to put something in there instead of "assert", why not just throw an error? Effectively: assert(0, msg) becomes a fancy way of writing (in any mode, release or otherwise): throw new AssertError(msg); This is actually the way I thought it was done. -SteveWhy do we do this?Because all asserts must be completely removed in -release
Aug 03 2015
On Monday, 3 August 2015 at 15:50:56 UTC, Steven Schveighoffer wrote:On 8/3/15 11:18 AM, Dicebot wrote:Now, they are completely removed. There is effectively no AssertError present in -release (it is defined but compiler is free to assume it never happens). I'd expect any reasonable compiler to not even emit stack unwinding code for functions with assert(0) (and no other throwables are present). assert(0) is effectively same as gcc __builtin_unreachable with all consequences for optimization - with only difference that latter won't even insert HLT but just continue executing corrupted program.On Monday, 3 August 2015 at 14:34:52 UTC, Steven Schveighoffer wrote:1. They aren't removed, they are replaced with a nearly useless segfault. 2. If we are going to put something in there instead of "assert", why not just throw an error? Effectively: assert(0, msg) becomes a fancy way of writing (in any mode, release or otherwise): throw new AssertError(msg); This is actually the way I thought it was done. -SteveWhy do we do this?Because all asserts must be completely removed in -release
Aug 03 2015
On 8/3/15 11:57 AM, Dicebot wrote:On Monday, 3 August 2015 at 15:50:56 UTC, Steven Schveighoffer wrote:I actually don't care if stack is unwound (it's not guaranteed by language anyway). Just segfault is not useful at all to anyone who doesn't have core dump enabled, and even if they do enable it, it's not trivial to get at the real cause of the error. I'd settle for calling: __onAssertZero(__FILE__, __LINE__, message); Which can do whatever we want, print a message, do HLT, throw AssertError, etc.On 8/3/15 11:18 AM, Dicebot wrote:Now, they are completely removed. There is effectively no AssertError present in -release (it is defined but compiler is free to assume it never happens). I'd expect any reasonable compiler to not even emit stack unwinding code for functions with assert(0) (and no other throwables are present).On Monday, 3 August 2015 at 14:34:52 UTC, Steven Schveighoffer wrote:1. They aren't removed, they are replaced with a nearly useless segfault. 2. If we are going to put something in there instead of "assert", why not just throw an error? Effectively: assert(0, msg) becomes a fancy way of writing (in any mode, release or otherwise): throw new AssertError(msg); This is actually the way I thought it was done.Why do we do this?Because all asserts must be completely removed in -releaseassert(0) is effectively same as gcc __builtin_unreachable with all consequences for optimization - with only difference that latter won't even insert HLT but just continue executing corrupted program.No reason this would change. Compiler can still consider this unreachable code for optimization purposes. -Steve
Aug 03 2015
On 8/3/2015 8:50 AM, Steven Schveighoffer wrote:1. They aren't removed, they are replaced with a nearly useless segfault.Not useless at all: 1. The program does not continue running after it has failed. (Please, let's not restart that debate. 2. Running it under a debugger, the location of the fault will be identified.2. If we are going to put something in there instead of "assert", why not just throw an error?Because they are expensive. To keep asserts in all their glory in released code, do not use the -release switch.
Aug 03 2015
On 8/3/15 6:54 PM, Walter Bright wrote:On 8/3/2015 8:50 AM, Steven Schveighoffer wrote:You can run some code that is fresh, i.e. don't *continue* running failing code, but it's ok, for example, to run a signal handler, or call a pure function. My thought is that you should print the file location and exit (perhaps with segfault).1. They aren't removed, they are replaced with a nearly useless segfault.Not useless at all: 1. The program does not continue running after it has failed. (Please, let's not restart that debate.2. Running it under a debugger, the location of the fault will be identified.Yeah, the real problem is when you are not running under a debugger. If you have an intermittent bug, or one that is in the field, a stack trace of the problem is worth 100x more than a segfault and trying to talk a customer through setting up a debugger, or even setting up their system to do a core dump for you to debug. They aren't always interested in that enough to care. But also, a segfault has a specific meaning. It means, you tried to access memory you don't have access to. This clearly is not that, and it sends the wrong message -- memory corruption. This is not memory corruption, it's a program error.They are expensive when? 2 microseconds before the program ends? At that point, I'd rather you spend as much resources telling me what went wrong as possible. More info the better. Now, if it's expensive to include the throw vs. not including it (I don't know), then that's a fair point. If it means an extra few instructions to set up the throw (which isn't run at all when the assert(0) line isn't run), that's not convincing.2. If we are going to put something in there instead of "assert", why not just throw an error?Because they are expensive.To keep asserts in all their glory in released code, do not use the -release switch.OK, this brings up another debate. The thing that triggered all this is an issue with core.time, see issue https://issues.dlang.org/show_bug.cgi?id=14863 Essentially, we wrote code to get all the clock information at startup on a posix system that supports clock_gettime, which is immutable and can be read easily at startup. However, how we handled the case where a clock could not be fetched is to assert(0, "don't support clock " ~ clockname). The clear expectation was that the message will be printed (along with file/line number), and we can go fix the issue. On Linux 2.6.32 - a supported LTS release I guess - one of these clocks is not supported. This means running a simple "hello world" program crashes at startup in a segfault, not a nice informative message. So what is the right answer here? We want an assert to trigger for this, but the only assert that stays in is assert(0). However, that's useless if all someone sees is "segfuault". "some message" never gets printed, because druntime is compiled in release mode. I'm actually quite thrilled that someone figured this all out -- one person analyzed his core dump to narrow down the function, and happened to include his linux kernel version, and another person had the arcane knowledge that some clock wasn't supported in that version. Is there a better mechanism we should be using in druntime that is clearly going to be compiled in release mode? Should we just throw an assert error directly? Clearly the code is not "corrupted" at this point, it's just an environmental issue. But we don't want to continue execution. What is the right call? At the very least, assert(0, "message") should be a compiler error, the message is unused information. -Steve
Aug 03 2015
On 08/03/2015 04:57 PM, Steven Schveighoffer wrote:At the very least, assert(0, "message") should be a compiler error, the message is unused information.Agreed. How about dumping the message to stderr as a best effort if the message is a literal? Hm... On the other hand, undefined behavior means that even trying that can cause harm like radiating a human with too much medical radiation. :( Perhaps, if it is a complicated expression like calling format(), then it should be an error? Ali
Aug 03 2015
On 8/3/2015 5:25 PM, Ali Çehreli wrote:On 08/03/2015 04:57 PM, Steven Schveighoffer wrote: > At the very least, assert(0, "message") should be a compiler error, the > message is unused information. Agreed.No. 1. If you want the message, do not use -release. 2. Do not use asserts to issue error messages for input/environment errors.
Aug 03 2015
On 8/3/15 9:13 PM, Walter Bright wrote:On 8/3/2015 5:25 PM, Ali Çehreli wrote:Any assert(0) in druntime or phobos with a message printed is an error. It's compiled in release mode. I'll look through and find them, and rework them. -SteveOn 08/03/2015 04:57 PM, Steven Schveighoffer wrote: > At the very least, assert(0, "message") should be a compiler error, the > message is unused information. Agreed.No. 1. If you want the message, do not use -release. 2. Do not use asserts to issue error messages for input/environment errors.
Aug 04 2015
On 8/4/15 7:53 AM, Steven Schveighoffer wrote:Any assert(0) in druntime or phobos with a message printed is an error. It's compiled in release mode. I'll look through and find them, and rework them.First stab: https://github.com/D-Programming-Language/druntime/pull/1337 -Steve
Aug 04 2015
On 8/4/2015 4:53 AM, Steven Schveighoffer wrote:Any assert(0) in druntime or phobos with a message printed is an error. It's compiled in release mode.When you're debugging phobos, you aren't compiling in release mode. But I've also been of the opinion, not widely shared, that any programmer supplied messages in asserts are pointless.
Aug 04 2015
On Tuesday, 4 August 2015 at 20:23:53 UTC, Walter Bright wrote:On 8/4/2015 4:53 AM, Steven Schveighoffer wrote:Would you be of the opinion that assert should be a statement rather than an expression ? unreadability in the middle of expressions, especially when evaluation order is not well defined, is close to impossible to get right.Any assert(0) in druntime or phobos with a message printed is an error. It's compiled in release mode.When you're debugging phobos, you aren't compiling in release mode. But I've also been of the opinion, not widely shared, that any programmer supplied messages in asserts are pointless.
Aug 04 2015
On Tuesday, 4 August 2015 at 20:40:47 UTC, deadalnix wrote:Would you be of the opinion that assert should be a statement rather than an expression ? unreadability in the middle of expressions, especially when evaluation order is not well defined, is close to impossible to get right.Ping ? Walter ?
Aug 04 2015
On 8/4/2015 8:26 PM, deadalnix wrote:On Tuesday, 4 August 2015 at 20:40:47 UTC, deadalnix wrote:If you want it as its own statement, write it that way. I don't see a need to change the language, nor would the breakage it would cause be justified.Would you be of the opinion that assert should be a statement rather than an expression ? unreadability in the middle of expressions, especially when evaluation order is not well defined, is close to impossible to get right.Ping ? Walter ?
Aug 04 2015
On Wednesday, 5 August 2015 at 06:37:21 UTC, Walter Bright wrote:On 8/4/2015 8:26 PM, deadalnix wrote:I suppose he's asking as maintainer of SDC.On Tuesday, 4 August 2015 at 20:40:47 UTC, deadalnix wrote:If you want it as its own statement, write it that way. I don't see a need to change the language, nor would the breakage it would cause be justified.Would you be of the opinion that assert should be a statement rather than an expression ? unreadability in the middle of expressions, especially when evaluation order is not well defined, is close to impossible to get right.Ping ? Walter ?
Aug 05 2015
On 8/4/15 4:23 PM, Walter Bright wrote:On 8/4/2015 4:53 AM, Steven Schveighoffer wrote:Unless you aren't debugging it, but an assert is still triggering. I want to stress, the bug that was found in core.time was environmental, and without the end user debugging it for us, we would never have known what this was, because the message wasn't printed. I should say, any assert(0) with a message printed that is possible to trigger in release mode is an error. If you can ensure it's only possible to trigger when compiled in debug mode (or for unit tests), then assert(0, msg) is fine.Any assert(0) in druntime or phobos with a message printed is an error. It's compiled in release mode.When you're debugging phobos, you aren't compiling in release mode.But I've also been of the opinion, not widely shared, that any programmer supplied messages in asserts are pointless.They can be helpful for context if it's not obvious what should have triggered the assert. for example: assert(x > y, "x: " ~ x.toString() ~ ", y: " ~ y.toString()); -Steve
Aug 04 2015
On 8/4/15 4:43 PM, Steven Schveighoffer wrote:I should say, any assert(0) with a message printed that is possible to trigger in release mode is an error. If you can ensure it's only possible to trigger when compiled in debug mode (or for unit tests), then assert(0, msg) is fine.In that vein, would it be possible to make this a warning? i.e. if you compile a file in -release mode, and the compiler encounters an assert(0, msg), it emits a warning? -Steve
Aug 04 2015
On Tuesday, 4 August 2015 at 20:48:52 UTC, Steven Schveighoffer wrote:On 8/4/15 4:43 PM, Steven Schveighoffer wrote:I fail to see why it's a problem if assert(0) has a message. It's exactly the same as any other assertion except that it stays even with -release, and you don't get the message then (whereas with a normal assertion, you wouldn't get anything). Not being able to have a message for assert(0) would be just as bad as not having a message for any other assertion. By arguing that assert(0) should only have a message in debug mode, you'd be arguing for something like static if(assert) assert(0, msg); else assert(0); which is pointless IMHO. I can definitely see an argument that we should be printing out something before the HLT instruction in -release mode, but as it stands, it's part of the spec that assert(0) just becomes a HLT instruction and so there's no message, and I don't think that should be a surprise given that the fact that assert(0) becomes a HLT instruction with -release is the key difference between it and other assertions. You don't get a warning about assertions having messages either, and their messages don't show up with -release. Instead, you get undefined behavior if they would have failed. - Jonathan M DavisI should say, any assert(0) with a message printed that is possible to trigger in release mode is an error. If you can ensure it's only possible to trigger when compiled in debug mode (or for unit tests), then assert(0, msg) is fine.In that vein, would it be possible to make this a warning? i.e. if you compile a file in -release mode, and the compiler encounters an assert(0, msg), it emits a warning?
Aug 04 2015
On 8/4/15 4:56 PM, Jonathan M Davis wrote:On Tuesday, 4 August 2015 at 20:48:52 UTC, Steven Schveighoffer wrote:Seeing an assert with a message gives the impression that you will see the message if you get there. I get that normal asserts may not even trigger. But the concept of "oh, if I see that message I know what happened" really goes out the window if you are never going to see it. As I said earlier, any line in druntime that has an assert(0, msg) is a bug, because the message is never seen. Doing the static if thing isn't the right answer either. The right answer is to choose one or the other (and by choosing to print a message, you could either throw an assert error directly, or print the message specifically and then assert(0) ). So basically: assert(0, msg); becomes printSomehow(msg); assert(0); With druntime it's difficult to actually print the message (and my PR is trying to fix that). -SteveOn 8/4/15 4:43 PM, Steven Schveighoffer wrote:I fail to see why it's a problem if assert(0) has a message. It's exactly the same as any other assertion except that it stays even with -release, and you don't get the message then (whereas with a normal assertion, you wouldn't get anything).I should say, any assert(0) with a message printed that is possible to trigger in release mode is an error. If you can ensure it's only possible to trigger when compiled in debug mode (or for unit tests), then assert(0, msg) is fine.In that vein, would it be possible to make this a warning? i.e. if you compile a file in -release mode, and the compiler encounters an assert(0, msg), it emits a warning?
Aug 04 2015
On Tuesday, 4 August 2015 at 21:07:20 UTC, Steven Schveighoffer wrote:On 8/4/15 4:56 PM, Jonathan M Davis wrote:It stills indicate why the assertion is there, which can be helpful to maintainers, and it _is_ possible to build druntime in debug mode. It's just not released that way (though it's been argued before that druntime/Phobos should be released with both debug and release builds). And arguing against assert(0) with a message in druntime because it's not going to show the message normally would basically be the same as arguing against having any assertions in there at all, because they wouldn't show up in a typical build either. But that doesn't mean that they shouldn't be there, just that they're not as useful as they might otherwise be, because druntime is almost always built in release mode.On Tuesday, 4 August 2015 at 20:48:52 UTC, Steven Schveighoffer wrote:Seeing an assert with a message gives the impression that you will see the message if you get there. I get that normal asserts may not even trigger. But the concept of "oh, if I see that message I know what happened" really goes out the window if you are never going to see it. As I said earlier, any line in druntime that has an assert(0, msg) is a bug, because the message is never seen.On 8/4/15 4:43 PM, Steven Schveighoffer wrote:I fail to see why it's a problem if assert(0) has a message. It's exactly the same as any other assertion except that it stays even with -release, and you don't get the message then (whereas with a normal assertion, you wouldn't get anything).I should say, any assert(0) with a message printed that is possible to trigger in release mode is an error. If you can ensure it's only possible to trigger when compiled in debug mode (or for unit tests), then assert(0, msg) is fine.In that vein, would it be possible to make this a warning? i.e. if you compile a file in -release mode, and the compiler encounters an assert(0, msg), it emits a warning?Doing the static if thing isn't the right answer either. The right answer is to choose one or the other (and by choosing to print a message, you could either throw an assert error directly, or print the message specifically and then assert(0) ). So basically: assert(0, msg); becomes printSomehow(msg); assert(0); With druntime it's difficult to actually print the message (and my PR is trying to fix that).I really so no difference between having a message with an assert(0) as with any other assertion. You're not going to see either with -release. It's just that assert(0) kills your program instead of giving you undefined behavior. I'm certainly not opposed to have a message be printed before the HLT instruction with assert(0), but I don't at all agree that the fact that the message is not seen in -release is a reason not to have a message. - Jonathan M Davis
Aug 04 2015
On 8/4/15 5:39 PM, Jonathan M Davis wrote:I'm certainly not opposed to have a message be printed before the HLT instruction with assert(0), but I don't at all agree that the fact that the message is not seen in -release is a reason not to have a message.For instance: https://github.com/D-Programming-Language/druntime/blob/master/src/core/time.d#L2283 This makes it seem like a message will be printed in the case where ticksPerSecond was 0. but in reality it simply throws a segfault. Whether this happens or not in debug mode is pretty much irrelevant -- druntime is used in release mode by the vast majority of all developers, and this passes unit tests for us. It's the whole impetus for this thread, because someone actually did find a case where it gets there. So why have a message with the clock name that failed? Why not just assert(0)? The only purpose I see for such a message is to trick the reviewer into accepting it (not that this was the intention of course) as being sufficiently explanatory when an error occurs. We should always review such code with the view that when it *doesn't* print the message, is the error sufficient to a user such that they know where to look. I find it hard to believe it's *ever* sufficient, if you needed to have a message in the first place. We can look at it this way -- if you need to add a message to an assert(0) for it to make sense, you should find a different way to communicate that. -Steve
Aug 04 2015
On 8/4/2015 3:13 PM, Steven Schveighoffer wrote:For instance: https://github.com/D-Programming-Language/druntime/blob/master/src/core/time.d#L2283 This makes it seem like a message will be printed in the case where ticksPerSecond was 0. but in reality it simply throws a segfault.That's a bug in core/time.d, NOT a bug with asserts.
Aug 04 2015
On 8/4/2015 4:37 PM, Walter Bright wrote:On 8/4/2015 3:13 PM, Steven Schveighoffer wrote:https://issues.dlang.org/show_bug.cgi?id=14870For instance: https://github.com/D-Programming-Language/druntime/blob/master/src/core/time.d#L2283 This makes it seem like a message will be printed in the case where ticksPerSecond was 0. but in reality it simply throws a segfault.That's a bug in core/time.d, NOT a bug with asserts.
Aug 04 2015
On Tuesday, 4 August 2015 at 22:13:40 UTC, Steven Schveighoffer wrote:On 8/4/15 5:39 PM, Jonathan M Davis wrote:It was never expected that a user to see any of those messages anyway. The idea was that if they failed, there was something seriously wrong, and the program needed to be killed. If the message prints when it fails, great. It's more explanatory that way, but it's a bonus (and one that you do get if druntime is build in debug mode). The purpose was to kill the program, because it was in an invalid state. - Jonathan M DavisI'm certainly not opposed to have a message be printed before the HLT instruction with assert(0), but I don't at all agree that the fact that the message is not seen in -release is a reason not to have a message.For instance: https://github.com/D-Programming-Language/druntime/blob/master/src/core/time.d#L2283 This makes it seem like a message will be printed in the case where ticksPerSecond was 0. but in reality it simply throws a segfault. Whether this happens or not in debug mode is pretty much irrelevant -- druntime is used in release mode by the vast majority of all developers, and this passes unit tests for us. It's the whole impetus for this thread, because someone actually did find a case where it gets there. So why have a message with the clock name that failed? Why not just assert(0)? The only purpose I see for such a message is to trick the reviewer into accepting it (not that this was the intention of course) as being sufficiently explanatory when an error occurs. We should always review such code with the view that when it *doesn't* print the message, is the error sufficient to a user such that they know where to look. I find it hard to believe it's *ever* sufficient, if you needed to have a message in the first place. We can look at it this way -- if you need to add a message to an assert(0) for it to make sense, you should find a different way to communicate that.
Aug 04 2015
On Wednesday, 5 August 2015 at 02:52:40 UTC, Jonathan M Davis wrote:On Tuesday, 4 August 2015 at 22:13:40 UTC, Steven Schveighoffer wrote:Maybe in this case, it would have made more sense to simply throw an Error rather than use assert(0), since it inadvertently ended up depending on the system's environment, even though it was supposed to be guaranteed to work (differences in kernel versions was not taken into account). And I assume that if we threw an Error, then we'd get a message. - Jonathan M DavisOn 8/4/15 5:39 PM, Jonathan M Davis wrote:It was never expected that a user to see any of those messages anyway. The idea was that if they failed, there was something seriously wrong, and the program needed to be killed. If the message prints when it fails, great. It's more explanatory that way, but it's a bonus (and one that you do get if druntime is build in debug mode). The purpose was to kill the program, because it was in an invalid state.I'm certainly not opposed to have a message be printed before the HLT instruction with assert(0), but I don't at all agree that the fact that the message is not seen in -release is a reason not to have a message.For instance: https://github.com/D-Programming-Language/druntime/blob/master/src/core/time.d#L2283 This makes it seem like a message will be printed in the case where ticksPerSecond was 0. but in reality it simply throws a segfault. Whether this happens or not in debug mode is pretty much irrelevant -- druntime is used in release mode by the vast majority of all developers, and this passes unit tests for us. It's the whole impetus for this thread, because someone actually did find a case where it gets there. So why have a message with the clock name that failed? Why not just assert(0)? The only purpose I see for such a message is to trick the reviewer into accepting it (not that this was the intention of course) as being sufficiently explanatory when an error occurs. We should always review such code with the view that when it *doesn't* print the message, is the error sufficient to a user such that they know where to look. I find it hard to believe it's *ever* sufficient, if you needed to have a message in the first place. We can look at it this way -- if you need to add a message to an assert(0) for it to make sense, you should find a different way to communicate that.
Aug 04 2015
On 8/4/2015 2:07 PM, Steven Schveighoffer wrote:With druntime it's difficult to actually print the messagefprintf(stderr, "You left the headlights on."); assert(0);
Aug 04 2015
On Tuesday, 4 August 2015 at 23:33:17 UTC, Walter Bright wrote:On 8/4/2015 2:07 PM, Steven Schveighoffer wrote:It seems that on american car, you bip as annoyingly as possible instead of announcing what's wrong. Preferably for anything and its reverse.With druntime it's difficult to actually print the messagefprintf(stderr, "You left the headlights on."); assert(0);
Aug 04 2015
On 8/4/2015 4:56 PM, deadalnix wrote:It seems that on american car, you bip as annoyingly as possible instead of announcing what's wrong. Preferably for anything and its reverse.It turns out you can make quite understandable speech simply by connecting an on/off I/O port to a speaker. So there is no excuse for using tones instead of "You left the headlights on, you ******* ****!"
Aug 04 2015
On 8/4/15 7:33 PM, Walter Bright wrote:On 8/4/2015 2:07 PM, Steven Schveighoffer wrote:Going under the assumption that the program isn't fully "with it", I don't want to use such complicated libraries as stdio. In any case, the PR I have does it with system calls only. -SteveWith druntime it's difficult to actually print the messagefprintf(stderr, "You left the headlights on."); assert(0);
Aug 04 2015
On 8/4/2015 1:56 PM, Jonathan M Davis wrote:I can definitely see an argument that we should be printing out something before the HLT instruction in -release mode,writeln("Somebody forgot to turn off the water."); assert(0);
Aug 04 2015
On Monday, 3 August 2015 at 23:57:36 UTC, Steven Schveighoffer wrote:OK, this brings up another debate. The thing that triggered all this is an issue with core.time, see issue https://issues.dlang.org/show_bug.cgi?id=14863 [...]Why not just " stderr.writeln(errMsg); assert(0);"?
Aug 03 2015
On 8/3/2015 4:57 PM, Steven Schveighoffer wrote:OK, this brings up another debate. The thing that triggered all this is an issue with core.time, see issue https://issues.dlang.org/show_bug.cgi?id=14863 Essentially, we wrote code to get all the clock information at startup on a posix system that supports clock_gettime, which is immutable and can be read easily at startup. However, how we handled the case where a clock could not be fetched is to assert(0, "don't support clock " ~ clockname). The clear expectation was that the message will be printed (along with file/line number), and we can go fix the issue. On Linux 2.6.32 - a supported LTS release I guess - one of these clocks is not supported. This means running a simple "hello world" program crashes at startup in a segfault, not a nice informative message. So what is the right answer here?The answer is the code is misusing asserts to check for environmental errors. I cannot understand how I consistently fail at explaining this. There have been many thousand message threads on exactly this topic. Asserts are for CHECKING FOR PROGRAM BUGS. enforce(), etc., are for CHECKING FOR INPUT ERRORS. Environmental errors are input errors, not programming bugs.
Aug 03 2015
On 8/3/15 9:11 PM, Walter Bright wrote:enforce(), etc., are for CHECKING FOR INPUT ERRORS. Environmental errors are input errors, not programming bugs.enforce isn't available in druntime. In this case, we don't want to throw an exception anyways, or we would have to remove nothrow from core.time. Technically, usage of this unsupported clock will ALWAYS throw, or NEVER throw. That's difficult to encode into the system. The only answer I can think of is to write a message to stderr, and then assert(0) as meta suggested. -Steve
Aug 04 2015
On 8/4/2015 4:58 AM, Steven Schveighoffer wrote:The only answer I can think of is to write a message to stderr, and then assert(0) as meta suggested.Print an error message and exit via abort() or whatever. No need to generate a halt - the program hasn't entered an unknown state.
Aug 04 2015
On Monday, 3 August 2015 at 23:57:36 UTC, Steven Schveighoffer wrote:At the very least, assert(0, "message") should be a compiler error, the message is unused information.Only if you compile in -release mode. Without wanting to get into a bikeshedding debate, I think that flag name may be mislead: it's not about release vs. development, it's about the tradeoff you are making between speed and safety.
Aug 05 2015
On Wednesday, 5 August 2015 at 08:09:49 UTC, Joseph Rushton Wakeling wrote:On Monday, 3 August 2015 at 23:57:36 UTC, Steven Schveighoffer wrote:That's arguably true, but historically, release builds are where you turn off assertions and turn on optimizations. So, it does pretty much what most folks would expect from a release build (though some folks might assume that it turns on optimizations as well, which it doesn't). Now, there are good reasons to leave assertions in in release builds, but then you're arguably just using your debug builds in production (probably because you don't need the efficiency gain and are too paranoid to risk turning the assertions off). So, you have a good point, but really, -release does with assertions what's normally expected of release builds, so I don't think that it's actually misleading at all. - Jonathan M DavisAt the very least, assert(0, "message") should be a compiler error, the message is unused information.Only if you compile in -release mode. Without wanting to get into a bikeshedding debate, I think that flag name may be mislead: it's not about release vs. development, it's about the tradeoff you are making between speed and safety.
Aug 05 2015
On 4 August 2015 at 00:54, Walter Bright via Digitalmars-d < digitalmars-d puremagic.com> wrote:On 8/3/2015 8:50 AM, Steven Schveighoffer wrote:I think as a guideline, there are really only be two places where using an assert makes sense in druntime/phobos. 1. Internal modules, such as rt and core.internal. 2. Inside contracts (in, out, invariant) or unittests. Everywhere else - *especially* if it's part of a public API like core.time - you should throw an Exception or Error based on what you see fit. Because of this view, I'm not really in agreement with the addition of a core.abort module. Regards Iain1. They aren't removed, they are replaced with a nearly useless segfault.Not useless at all: 1. The program does not continue running after it has failed. (Please, let's not restart that debate. 2. Running it under a debugger, the location of the fault will be identified. 2. If we are going to put something in there instead of "assert", why notjust throw an error?Because they are expensive. To keep asserts in all their glory in released code, do not use the -release switch.
Aug 05 2015
On Wednesday, 5 August 2015 at 10:31:40 UTC, Iain Buclaw wrote:I think as a guideline, there are really only be two places where using an assert makes sense in druntime/phobos. 1. Internal modules, such as rt and core.internal. 2. Inside contracts (in, out, invariant) or unittests. Everywhere else - *especially* if it's part of a public API like core.time - you should throw an Exception or Error based on what you see fit.I think that it makes perfect sense to use assertions inside of druntime or Phobos functions when testing the correctness of their internal state, but it obviously can't be expected that they run normally (at least in non-templated code), and it can't be expected that much beyond the unit tests run them (at least in non-templated code), since almost no one is going to be using a debug version of druntime/Phobos. Certainly, I don't see anything special about druntime or Phobos with regards to assertions except for the fact that not much beyond the unit tests is going to be using druntime/Phobos in debug mode, so assertions in non-templated code simply aren't going to be doing anything outside of the unit test builds. As for assert(0) specifically, it makes sense in cases where the code should never ever hit that point, and there's a serious problem if it does (e.g. the default branch of a switch statement where it should be impossible for that branch to be hit), but it was a mistake to use it in core.time, since that was failing based on a system call returning a failure (which should basically never happen in this case, but it still depends on the environment and thus could conceivably fail as opposed to only ever failing if druntime's code is definitely broken).Because of this view, I'm not really in agreement with the addition of a core.abort module.Well, then please comment on the PR: https://github.com/D-Programming-Language/druntime/pull/1337 It seems to me that the whole point of abort is to work around the fact that Walter doesn't want assert(0) to print messages before the HLT instruction in release mode. Certainly, I can't see any other reason for it. - Jonathan M Davis
Aug 05 2015
On 8/5/15 12:46 PM, Jonathan M Davis wrote:Well, then please comment on the PR: https://github.com/D-Programming-Language/druntime/pull/1337 It seems to me that the whole point of abort is to work around the fact that Walter doesn't want assert(0) to print messages before the HLT instruction in release mode. Certainly, I can't see any other reason for it.No, the reason is, if you want to print something before halting, and you don't want to use any subsystems that you suspect are corrupt (e.g. stdio), it's not a simple task. This makes it as simple as calling assert. I thought it would be a useful utility. Surely, we can improve core.time to throw in this case instead, since the case where the ticksPerSecond is zero only happens when the OS did not provide it. A slight possibility is that the memory was corrupted. But I think the abort function still has use in other cases. -Steve
Aug 05 2015
On Monday, 3 August 2015 at 15:18:12 UTC, Dicebot wrote:On Monday, 3 August 2015 at 14:34:52 UTC, Steven Schveighoffer wrote:It isn't free. The point of having unreachable as primitive is that you can remove all branching that leads to it.Why do we do this?Because all asserts must be completely removed in -release Yet assert(0) effectively mean "unreachable code" (it is actually defined that way in spec) and thus it is possible to ensure extra "free" bit of safety by crashing the app.
Aug 03 2015
On Tuesday, 4 August 2015 at 02:37:00 UTC, Ola Fosheim Grøstad wrote:On Monday, 3 August 2015 at 15:18:12 UTC, Dicebot wrote:"free compared to throwing exception" would be a better wording indeed. I remember Iain complaining about presence of HLT (and saying he won't do that for gdc) for exactly this reason.On Monday, 3 August 2015 at 14:34:52 UTC, Steven Schveighoffer wrote:It isn't free. The point of having unreachable as primitive is that you can remove all branching that leads to it.Why do we do this?Because all asserts must be completely removed in -release Yet assert(0) effectively mean "unreachable code" (it is actually defined that way in spec) and thus it is possible to ensure extra "free" bit of safety by crashing the app.
Aug 03 2015
On Tuesday, 4 August 2015 at 03:04:14 UTC, Dicebot wrote:indeed. I remember Iain complaining about presence of HLT (and saying he won't do that for gdc) for exactly this reason.Yes. I think a lot of this confusion could have been avoided by using "halt()" instead of "assert(0)" and "unreachable()" for marking code that has been proven unreachable. Right now it isn't obvious to me as a programmer what would happen for "assert(x-x)". I would have to look it up. A more principled approach would be: - "enforce(...)" test for errors - "assert(...)" are (lint-like) annotations that don't affect normal execution, but can be requested to be dynamically tested. - "halt()" marks sudden termination - "unreachable()" injects a proven assumption into the deductive database -"assume(...)" injects a proven assumption into the deductive database - contracts define interfaces between separate pieces of code (like a plugin or dynamically linked library from multiple vendors e.g. sub system interface) that you may turn on/off based on what you interface with. The input/environment/code distinction does not work very well. Sometimes input is a well defined part of the system, sometimes input is code (like dynamic linking a plugin), etc...
Aug 03 2015
On 8/3/2015 8:37 PM, Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= <ola.fosheim.grostad+dlang gmail.com> wrote:The input/environment/code distinction does not work very well.Sure it does. If your user ever sees an assert failure message, your program has a bug in it. Keep that in mind when designing the code, and the distinction will become clear.
Aug 04 2015
On Tuesday, 4 August 2015 at 20:21:24 UTC, Walter Bright wrote:On 8/3/2015 8:37 PM, Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= <ola.fosheim.grostad+dlang gmail.com> wrote:Yes.The input/environment/code distinction does not work very well.Sure it does. If your user ever sees an assert failure message, your program has a bug in it.Keep that in mind when designing the code, and the distinction will become clear.The input/code distinction is too simplistic. Example 1: It makes perfect sense to assert (or assume) that a value from a hardware register or cpu instruction is within range. If the assert fires, it is the spec/code that is wrong, not the input. So you are testing the specification in the code against well defined input. Example 2: It makes perfect sense to enforce that a return value from a plugin library is within range to maintain main program integrity and shut out a misbehaving plugin. And so on.
Aug 04 2015
On Tuesday, 4 August 2015 at 22:06:06 UTC, Ola Fosheim Grøstad wrote:On Tuesday, 4 August 2015 at 20:21:24 UTC, Walter Bright wrote:I'm 90% on Walter's side here as he's right for the majority of common cases, but on the other hand there are plenty of situations where the boundary between code error and environment error get blurred.On 8/3/2015 8:37 PM, Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= <ola.fosheim.grostad+dlang gmail.com> wrote:Yes.The input/environment/code distinction does not work very well.Sure it does. If your user ever sees an assert failure message, your program has a bug in it.Keep that in mind when designing the code, and the distinction will become clear.The input/code distinction is too simplistic. Example 1: It makes perfect sense to assert (or assume) that a value from a hardware register or cpu instruction is within range. If the assert fires, it is the spec/code that is wrong, not the input. So you are testing the specification in the code against well defined input. Example 2: It makes perfect sense to enforce that a return value from a plugin library is within range to maintain main program integrity and shut out a misbehaving plugin. And so on.
Aug 04 2015
On Tuesday, 4 August 2015 at 23:06:44 UTC, John Colvin wrote:I'm 90% on Walter's side here as he's right for the majority of common cases, but on the other hand there are plenty of situations where the boundary between code error and environment error get blurred.Well, the principled difference is that asserts are program-specification-annotations and should not in any way affect execution within the code unit, and if it does it should happen as if it was detected by a hypothetical supervisor external to the program. E.g. for a batch program terminate and have the calling context unwind any side-effects (as in a transaction), or for an interactive program to enter emergency mode, save state in a temporary file and try to recover after restart. Asserts are reflecting system specifications (not code, or just applying to code). If you want to take height for code errors you should use enforce or throw. It makes plenty of sense to assume that code have errors, and to recover from it, but using asserts does not assume that you have errors. It is just a way to add specification to the code for validation/documentation purposes. I don't want those in release at all. I have way to many asserts for that. So this goes much deeper than you and Walter suggest.
Aug 04 2015
On Wednesday, 5 August 2015 at 05:51:32 UTC, Ola Fosheim Grøstad wrote:specification to the code for validation/documentation purposes.Err... Verification, usually not validation.
Aug 05 2015
General advice - simply don't ever use -release unless you are _very_ sure about program correctness (to the point of 100% test coverage and previous successful debug runs)
Aug 03 2015
On 8/3/15 11:59 AM, Dicebot wrote:General advice - simply don't ever use -release unless you are _very_ sure about program correctness (to the point of 100% test coverage and previous successful debug runs)So in other words, only release code that has no bugs. Got it ;) -Steve
Aug 03 2015
On 08/03/2015 11:59 AM, Dicebot wrote:General advice - simply don't ever use -release unless you are _very_ sure about program correctness (to the point of 100% test coverage and previous successful debug runs)This is very true. I never disable asserts or bounds checking for exactly that reason - you can NEVER conclusively determine through prerelease testing that none of those conditions are going to get tripped in real-world usage. ANY developer who thinks they can is absolutely fooling themself. And what happens for the end user WHEN one of those conditions does occur? Memory corruption or otherwise invalid state. Things go boom. Whee. BAD idea. There is only ONE time when asserts or bounds checking should EVER be disabled and that's on a per-function basis (split it out into a separate module if you need to) AFTER profiling has determined that specific location to be a significant bottleneck, and the code in question has been (and will continue to be during all future maintenance) VERY carefully combed-over and peer-reviewed to ensure (as much as possible) that disabling asserts/bounds checks on that localized function cannot lead to corruption, exploits or invalid state.
Aug 04 2015
On Tuesday, 4 August 2015 at 14:40:16 UTC, Nick Sabalausky wrote:On 08/03/2015 11:59 AM, Dicebot wrote:Recently we had quite a lengthy discussion at work regarding possible guidelines for using asserts, contracts and enforce (we have similar own implementation) that would actually allow using -release flag for release builds. And got to certain principles that I believe may work in practice (even though they violate DbC ideology). I will check if I can publish those here.General advice - simply don't ever use -release unless you are _very_ sure about program correctness (to the point of 100% test coverage and previous successful debug runs)This is very true. I never disable asserts or bounds checking for exactly that reason - you can NEVER conclusively determine through prerelease testing that none of those conditions are going to get tripped in real-world usage. ANY developer who thinks they can is absolutely fooling themself. And what happens for the end user WHEN one of those conditions does occur? Memory corruption or otherwise invalid state. Things go boom. Whee. BAD idea. There is only ONE time when asserts or bounds checking should EVER be disabled and that's on a per-function basis (split it out into a separate module if you need to) AFTER profiling has determined that specific location to be a significant bottleneck, and the code in question has been (and will continue to be during all future maintenance) VERY carefully combed-over and peer-reviewed to ensure (as much as possible) that disabling asserts/bounds checks on that localized function cannot lead to corruption, exploits or invalid state.
Aug 04 2015
On 8/4/2015 8:04 AM, Dicebot wrote:Recently we had quite a lengthy discussion at work regarding possible guidelines for using asserts, contracts and enforce (we have similar own implementation) that would actually allow using -release flag for release builds. And got to certain principles that I believe may work in practice (even though they violate DbC ideology). I will check if I can publish those here.That's worthy of more than just a newsgroup post. How about an article or a wiki entry?
Aug 04 2015